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