Update for changes in BMesh
[blender-addons-contrib.git] / curve_tools / Surfaces.py
1 import bpy
2 import bmesh
3
4 from . import Math
5 from . import Curves
6
7
8
9 class LoftedSplineSurface:
10     def __init__(self, activeSpline, otherSpline, bMesh, vert0Index, resolution):
11         self.splineA = activeSpline
12         self.splineO = otherSpline
13         
14         self.bMesh = bMesh
15         self.vert0Index = vert0Index
16         self.resolution = resolution
17
18         
19     def Apply(self, worldMatrixA, worldMatrixO):
20         #deltaPar = 1.0 / float(self.resolution - 1)
21         
22         par = 0.0
23         pointA = worldMatrixA * self.splineA.CalcPoint(par)
24         pointO = worldMatrixO * self.splineO.CalcPoint(par)
25         self.bMesh.verts[self.vert0Index].co = pointA
26         self.bMesh.verts[self.vert0Index + 1].co = pointO
27         
28         fltResm1 = float(self.resolution - 1)
29         for i in range(1, self.resolution):
30             par = float(i) / fltResm1
31         
32             pointA = worldMatrixA * self.splineA.CalcPoint(par)
33             pointO = worldMatrixO * self.splineO.CalcPoint(par)
34             self.bMesh.verts[self.vert0Index + 2 * i].co = pointA
35             self.bMesh.verts[self.vert0Index + 2 * i + 1].co = pointO
36
37         
38     def AddFaces(self):
39         currIndexA = self.vert0Index
40         currIndexO = self.vert0Index + 1
41         
42         bmVerts = self.bMesh.verts
43         bmVerts.ensure_lookup_table()
44
45         for i in range(1, self.resolution):
46             nextIndexA = self.vert0Index + 2 * i
47             nextIndexO = nextIndexA + 1
48             
49             self.bMesh.faces.new([bmVerts[currIndexA], bmVerts[currIndexO], bmVerts[nextIndexO], bmVerts[nextIndexA]])
50             
51             currIndexA = nextIndexA
52             currIndexO = nextIndexO
53             
54
55 class LoftedSurface:
56     @staticmethod
57     def FromSelection():
58         selObjects = bpy.context.selected_objects
59         if len(selObjects) != 2: raise Exception("len(selObjects) != 2") # shouldn't be possible
60         
61         blenderActiveCurve = bpy.context.active_object
62         blenderOtherCurve = selObjects[0]
63         if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
64         
65         aCurve = Curves.Curve(blenderActiveCurve)
66         oCurve = Curves.Curve(blenderOtherCurve)
67         
68         name = "TODO: autoname"
69         
70         return LoftedSurface(aCurve, oCurve, name)
71     
72
73     def __init__(self, activeCurve, otherCurve, name = "LoftedSurface"):
74         self.curveA = activeCurve
75         self.curveO = otherCurve
76         self.name  = name
77         
78         self.nrSplines = self.curveA.nrSplines
79         if self.curveO.nrSplines < self.nrSplines: self.nrSplines = self.curveO.nrSplines
80         
81         self.bMesh = bmesh.new()
82         
83         self.splineSurfaces = self.SetupSplineSurfaces()
84         
85         self.Apply()
86         
87         
88     def SetupSplineSurfaces(self):
89         rvSplineSurfaces = []
90         
91         currV0Index = 0
92         for i in range(self.nrSplines):
93             splineA = self.curveA.splines[i]
94             splineO = self.curveO.splines[i]
95             
96             res = splineA.resolution
97             if splineO.resolution < res: res = splineO.resolution
98             
99             for iv in range(2 * res): self.bMesh.verts.new()
100             
101             splSurf = LoftedSplineSurface(splineA, splineO, self.bMesh, currV0Index, res)
102             splSurf.AddFaces()
103             rvSplineSurfaces.append(splSurf)
104             
105             currV0Index += 2 * res
106         
107         return rvSplineSurfaces
108         
109         
110     def Apply(self):
111         for splineSurface in self.splineSurfaces: splineSurface.Apply(self.curveA.worldMatrix, self.curveO.worldMatrix)
112         
113         
114     def AddToScene(self):
115         mesh = bpy.data.meshes.new("Mesh" + self.name)
116         
117         self.bMesh.to_mesh(mesh)
118         mesh.update()
119         
120         meshObject = bpy.data.objects.new(self.name, mesh)
121         
122         bpy.context.scene.objects.link(meshObject)
123
124
125
126 # active spline is swept over other spline (rail)
127 class SweptSplineSurface:
128     def __init__(self, activeSpline, otherSpline, bMesh, vert0Index, resolutionA, resolutionO):
129         self.splineA = activeSpline
130         self.splineO = otherSpline
131         
132         self.bMesh = bMesh
133         self.vert0Index = vert0Index
134         self.resolutionA = resolutionA
135         self.resolutionO = resolutionO
136
137         
138     def Apply(self, worldMatrixA, worldMatrixO):
139         localPointsA = []
140         fltResAm1 = float(self.resolutionA - 1)
141         for i in range(self.resolutionA):
142             par = float(i) / fltResAm1
143             pointA = self.splineA.CalcPoint(par)
144             localPointsA.append(pointA)
145
146         
147         worldPointsO = []
148         localDerivativesO = []
149         fltResOm1 = float(self.resolutionO - 1)
150         for i in range(self.resolutionO):
151             par = float(i) / fltResOm1
152             
153             pointO = self.splineO.CalcPoint(par)
154             worldPointsO.append(worldMatrixO * pointO)
155             
156             derivativeO = self.splineO.CalcDerivative(par)
157             localDerivativesO.append(derivativeO)
158         
159         
160         currWorldMatrixA = worldMatrixA
161         worldMatrixOInv = worldMatrixO.inverted()
162         prevDerivativeO = localDerivativesO[0]
163         for iO in range(self.resolutionO):
164             currDerivativeO = localDerivativesO[iO]
165             localRotMatO = Math.CalcRotationMatrix(prevDerivativeO, currDerivativeO)
166             
167             currLocalAToLocalO = worldMatrixOInv * currWorldMatrixA       
168             worldPointsA = []
169             for iA in range(self.resolutionA):
170                 pointALocalToO = currLocalAToLocalO * localPointsA[iA]
171                 rotatedPointA = localRotMatO * pointALocalToO
172                 worldPointsA.append(worldMatrixO * rotatedPointA)
173                 
174             worldOffsetsA = []
175             worldPoint0A = worldPointsA[0]
176             for i in range(self.resolutionA): worldOffsetsA.append(worldPointsA[i] - worldPoint0A)
177             
178             
179             for iA in range(self.resolutionA):
180                 iVert = self.vert0Index + (self.resolutionA * iO) + iA
181                 currVert = worldPointsO[iO] + worldOffsetsA[iA]
182                 self.bMesh.verts[iVert].co = currVert
183                 
184             prevDerivativeO = currDerivativeO
185             currWorldMatrixA = worldMatrixO * localRotMatO * currLocalAToLocalO
186
187         
188     def AddFaces(self):
189         bmVerts = self.bMesh.verts
190         bmVerts.ensure_lookup_table()
191         
192         for iO in range(self.resolutionO - 1):
193             for iA in range(self.resolutionA - 1):
194                 currIndexA1 = self.vert0Index + (self.resolutionA * iO) + iA
195                 currIndexA2 = currIndexA1 + 1
196                 nextIndexA1 = self.vert0Index + (self.resolutionA * (iO + 1)) + iA
197                 nextIndexA2 = nextIndexA1 + 1
198         
199                 self.bMesh.faces.new([bmVerts[currIndexA1], bmVerts[currIndexA2], bmVerts[nextIndexA2], bmVerts[nextIndexA1]])
200         
201         
202
203 class SweptSurface:
204     @staticmethod
205     def FromSelection():
206         selObjects = bpy.context.selected_objects
207         if len(selObjects) != 2: raise Exception("len(selObjects) != 2") # shouldn't be possible
208         
209         blenderActiveCurve = bpy.context.active_object
210         blenderOtherCurve = selObjects[0]
211         if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
212         
213         aCurve = Curves.Curve(blenderActiveCurve)
214         oCurve = Curves.Curve(blenderOtherCurve)
215         
216         name = "TODO: autoname"
217         
218         return SweptSurface(aCurve, oCurve, name)
219     
220
221     def __init__(self, activeCurve, otherCurve, name = "SweptSurface"):
222         self.curveA = activeCurve
223         self.curveO = otherCurve
224         self.name  = name
225         
226         self.nrSplines = self.curveA.nrSplines
227         if self.curveO.nrSplines < self.nrSplines: self.nrSplines = self.curveO.nrSplines
228         
229         self.bMesh = bmesh.new()
230         
231         self.splineSurfaces = self.SetupSplineSurfaces()
232         
233         self.Apply()
234         
235         
236     def SetupSplineSurfaces(self):
237         rvSplineSurfaces = []
238         
239         currV0Index = 0
240         for i in range(self.nrSplines):
241             splineA = self.curveA.splines[i]
242             splineO = self.curveO.splines[i]
243             
244             resA = splineA.resolution
245             resO = splineO.resolution
246             
247             for iv in range(resA * resO): self.bMesh.verts.new()
248             
249             splSurf = SweptSplineSurface(splineA, splineO, self.bMesh, currV0Index, resA, resO)
250             splSurf.AddFaces()
251             rvSplineSurfaces.append(splSurf)
252             
253             currV0Index += resA * resO
254         
255         return rvSplineSurfaces
256         
257         
258     def Apply(self):
259         for splineSurface in self.splineSurfaces: splineSurface.Apply(self.curveA.worldMatrix, self.curveO.worldMatrix)
260         
261         
262     def AddToScene(self):
263         mesh = bpy.data.meshes.new("Mesh" + self.name)
264         
265         self.bMesh.to_mesh(mesh)
266         mesh.update()
267         
268         meshObject = bpy.data.objects.new(self.name, mesh)
269         
270         bpy.context.scene.objects.link(meshObject)
271         
272
273
274 # profileSpline is swept over rail1Spline and scaled/rotated to have its endpoint on rail2Spline
275 class BirailedSplineSurface:
276     def __init__(self, rail1Spline, rail2Spline, profileSpline, bMesh, vert0Index, resolutionRails, resolutionProfile):
277         self.rail1Spline = rail1Spline
278         self.rail2Spline = rail2Spline
279         self.profileSpline = profileSpline
280         
281         self.bMesh = bMesh
282         self.vert0Index = vert0Index
283         self.resolutionRails = resolutionRails
284         self.resolutionProfile = resolutionProfile
285
286         
287     def Apply(self, worldMatrixRail1, worldMatrixRail2, worldMatrixProfile):
288         localPointsProfile = []
289         fltResProfilem1 = float(self.resolutionProfile - 1)
290         for i in range(self.resolutionProfile):
291             par = float(i) / fltResProfilem1
292             pointProfile = self.profileSpline.CalcPoint(par)
293             localPointsProfile.append(pointProfile)
294
295         
296         worldPointsRail1 = []
297         localDerivativesRail1 = []
298         worldPointsRail2 = []
299         fltResRailsm1 = float(self.resolutionRails - 1)
300         for i in range(self.resolutionRails):
301             par = float(i) / fltResRailsm1
302             
303             pointRail1 = self.rail1Spline.CalcPoint(par)
304             worldPointsRail1.append(worldMatrixRail1 * pointRail1)
305             
306             derivativeRail1 = self.rail1Spline.CalcDerivative(par)
307             localDerivativesRail1.append(derivativeRail1)
308             
309             pointRail2 = self.rail2Spline.CalcPoint(par)
310             worldPointsRail2.append(worldMatrixRail2 * pointRail2)
311         
312         
313         currWorldMatrixProfile = worldMatrixProfile
314         worldMatrixRail1Inv = worldMatrixRail1.inverted()
315         prevDerivativeRail1 = localDerivativesRail1[0]
316         for iRail in range(self.resolutionRails):
317             currDerivativeRail1 = localDerivativesRail1[iRail]
318             localRotMatRail1 = Math.CalcRotationMatrix(prevDerivativeRail1, currDerivativeRail1)
319             
320             currLocalProfileToLocalRail1 = worldMatrixRail1Inv * currWorldMatrixProfile       
321             worldPointsProfileRail1 = []
322             for iProfile in range(self.resolutionProfile):
323                 pointProfileLocalToRail1 = currLocalProfileToLocalRail1 * localPointsProfile[iProfile]
324                 rotatedPointProfile = localRotMatRail1 * pointProfileLocalToRail1
325                 worldPointsProfileRail1.append(worldMatrixRail1 * rotatedPointProfile)
326                 
327             worldOffsetsProfileRail1 = []
328             worldPoint0ProfileRail1 = worldPointsProfileRail1[0]
329             for iProfile in range(self.resolutionProfile): worldOffsetsProfileRail1.append(worldPointsProfileRail1[iProfile] - worldPoint0ProfileRail1)
330                 
331             worldStartPointProfileRail1 = worldPointsRail1[iRail]
332             worldEndPointProfileRail1 = worldStartPointProfileRail1 + worldOffsetsProfileRail1[-1]
333             v3From = worldEndPointProfileRail1 - worldStartPointProfileRail1
334             v3To = worldPointsRail2[iRail] - worldStartPointProfileRail1
335             scaleFactorRail2 = v3To.magnitude / v3From.magnitude
336             rotMatRail2 = Math.CalcRotationMatrix(v3From, v3To)
337             
338             worldOffsetsProfileRail2 = []
339             for iProfile in range(self.resolutionProfile):
340                 offsetProfileRail1 = worldOffsetsProfileRail1[iProfile]
341                 worldOffsetsProfileRail2.append(rotMatRail2 * (offsetProfileRail1 * scaleFactorRail2))
342             
343             
344             for iProfile in range(self.resolutionProfile):
345                 iVert = self.vert0Index + (self.resolutionProfile * iRail) + iProfile
346                 currVert = worldPointsRail1[iRail] + worldOffsetsProfileRail2[iProfile]
347                 self.bMesh.verts[iVert].co = currVert
348                 
349             prevDerivativeRail1 = currDerivativeRail1
350             currWorldMatrixProfile = worldMatrixRail1 * localRotMatRail1 * currLocalProfileToLocalRail1
351
352         
353     def AddFaces(self):
354         bmVerts = self.bMesh.verts
355         bmVerts.ensure_lookup_table()
356         
357         for iRail in range(self.resolutionRails - 1):
358             for iProfile in range(self.resolutionProfile - 1):
359                 currIndex1 = self.vert0Index + (self.resolutionProfile * iRail) + iProfile
360                 currIndex2 = currIndex1 + 1
361                 nextIndex1 = self.vert0Index + (self.resolutionProfile * (iRail + 1)) + iProfile
362                 nextIndex2 = nextIndex1 + 1
363         
364                 self.bMesh.faces.new([bmVerts[currIndex1], bmVerts[currIndex2], bmVerts[nextIndex2], bmVerts[nextIndex1]])
365         
366         
367
368 class BirailedSurface:
369     @staticmethod
370     def FromSelection():
371         nrSelectedObjects = bpy.context.scene.curvetools.NrSelectedObjects
372         if nrSelectedObjects != 3: raise Exception("nrSelectedObjects != 3") # shouldn't be possible
373         
374         
375         selectedObjects = bpy.context.scene.curvetools.SelectedObjects
376         selectedObjectValues = selectedObjects.values()
377
378         curveName = selectedObjectValues[0].name
379         rail1BlenderCurve = None
380         try: rail1BlenderCurve = bpy.data.objects[curveName]
381         except: rail1BlenderCurve = None
382         if rail1BlenderCurve is None: raise Exception("rail1BlenderCurve is None")
383
384         curveName = selectedObjectValues[1].name
385         rail2BlenderCurve = None
386         try: rail2BlenderCurve = bpy.data.objects[curveName]
387         except: rail2BlenderCurve = None
388         if rail2BlenderCurve is None: raise Exception("rail2BlenderCurve is None")
389
390         curveName = selectedObjectValues[2].name
391         profileBlenderCurve = None
392         try: profileBlenderCurve = bpy.data.objects[curveName]
393         except: profileBlenderCurve = None
394         if profileBlenderCurve is None: raise Exception("profileBlenderCurve is None")
395         
396         
397         rail1Curve = Curves.Curve(rail1BlenderCurve)
398         rail2Curve = Curves.Curve(rail2BlenderCurve)
399         profileCurve = Curves.Curve(profileBlenderCurve)
400         
401         name = "TODO: autoname"
402         
403         return BirailedSurface(rail1Curve, rail2Curve, profileCurve, name)
404     
405
406     def __init__(self, rail1Curve, rail2Curve, profileCurve, name = "BirailedSurface"):
407         self.rail1Curve = rail1Curve
408         self.rail2Curve = rail2Curve
409         self.profileCurve = profileCurve
410         self.name  = name
411         
412         self.nrSplines = self.rail1Curve.nrSplines
413         if self.rail2Curve.nrSplines < self.nrSplines: self.nrSplines = self.rail2Curve.nrSplines
414         if self.profileCurve.nrSplines < self.nrSplines: self.nrSplines = self.profileCurve.nrSplines
415         
416         self.bMesh = bmesh.new()
417         
418         self.splineSurfaces = self.SetupSplineSurfaces()
419         
420         self.Apply()
421         
422         
423     def SetupSplineSurfaces(self):
424         rvSplineSurfaces = []
425         
426         currV0Index = 0
427         for i in range(self.nrSplines):
428             splineRail1 = self.rail1Curve.splines[i]
429             splineRail2 = self.rail2Curve.splines[i]
430             splineProfile = self.profileCurve.splines[i]
431             
432             resProfile = splineProfile.resolution
433             resRails = splineRail1.resolution
434             if splineRail2.resolution < resRails: resRails = splineRail2.resolution
435             
436             for iv in range(resProfile * resRails): self.bMesh.verts.new()
437             
438             splSurf = BirailedSplineSurface(splineRail1, splineRail2, splineProfile, self.bMesh, currV0Index, resRails, resProfile)
439             splSurf.AddFaces()
440             rvSplineSurfaces.append(splSurf)
441             
442             currV0Index += resProfile * resRails
443         
444         return rvSplineSurfaces
445         
446         
447     def Apply(self):
448         for splineSurface in self.splineSurfaces: splineSurface.Apply(self.rail1Curve.worldMatrix, self.rail2Curve.worldMatrix, self.profileCurve.worldMatrix)
449         
450         
451     def AddToScene(self):
452         mesh = bpy.data.meshes.new("Mesh" + self.name)
453         
454         self.bMesh.to_mesh(mesh)
455         mesh.update()
456         
457         meshObject = bpy.data.objects.new(self.name, mesh)
458         
459         bpy.context.scene.objects.link(meshObject)
460
461