1 import bpy
2 import bmesh
4 from . import Math
5 from . import Curves
9 class LoftedSplineSurface:
10     def __init__(self, activeSpline, otherSpline, bMesh, vert0Index, resolution):
11         self.splineA = activeSpline
12         self.splineO = otherSpline
14         self.bMesh = bMesh
15         self.vert0Index = vert0Index
16         self.resolution = resolution
19     def Apply(self, worldMatrixA, worldMatrixO):
20         #deltaPar = 1.0 / float(self.resolution - 1)
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
28         fltResm1 = float(self.resolution - 1)
29         for i in range(1, self.resolution):
30             par = float(i) / fltResm1
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
39         currIndexA = self.vert0Index
40         currIndexO = self.vert0Index + 1
42         bmVerts = self.bMesh.verts
43         for i in range(1, self.resolution):
44             nextIndexA = self.vert0Index + 2 * i
45             nextIndexO = nextIndexA + 1
47             self.bMesh.faces.new([bmVerts[currIndexA], bmVerts[currIndexO], bmVerts[nextIndexO], bmVerts[nextIndexA]])
49             currIndexA = nextIndexA
50             currIndexO = nextIndexO
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
59         blenderActiveCurve = bpy.context.active_object
60         blenderOtherCurve = selObjects[0]
61         if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
63         aCurve = Curves.Curve(blenderActiveCurve)
64         oCurve = Curves.Curve(blenderOtherCurve)
66         name = "TODO: autoname"
68         return LoftedSurface(aCurve, oCurve, name)
71     def __init__(self, activeCurve, otherCurve, name = "LoftedSurface"):
72         self.curveA = activeCurve
73         self.curveO = otherCurve
74         self.name  = name
76         self.nrSplines = self.curveA.nrSplines
77         if self.curveO.nrSplines < self.nrSplines: self.nrSplines = self.curveO.nrSplines
79         self.bMesh = bmesh.new()
81         self.splineSurfaces = self.SetupSplineSurfaces()
83         self.Apply()
86     def SetupSplineSurfaces(self):
87         rvSplineSurfaces = []
89         currV0Index = 0
90         for i in range(self.nrSplines):
91             splineA = self.curveA.splines[i]
92             splineO = self.curveO.splines[i]
94             res = splineA.resolution
95             if splineO.resolution < res: res = splineO.resolution
97             for iv in range(2 * res): self.bMesh.verts.new()
99             splSurf = LoftedSplineSurface(splineA, splineO, self.bMesh, currV0Index, res)
101             rvSplineSurfaces.append(splSurf)
103             currV0Index += 2 * res
105         return rvSplineSurfaces
108     def Apply(self):
109         for splineSurface in self.splineSurfaces: splineSurface.Apply(self.curveA.worldMatrix, self.curveO.worldMatrix)
113         mesh = bpy.data.meshes.new("Mesh" + self.name)
115         self.bMesh.to_mesh(mesh)
116         mesh.update()
118         meshObject = bpy.data.objects.new(self.name, mesh)
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
130         self.bMesh = bMesh
131         self.vert0Index = vert0Index
132         self.resolutionA = resolutionA
133         self.resolutionO = resolutionO
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)
145         worldPointsO = []
146         localDerivativesO = []
147         fltResOm1 = float(self.resolutionO - 1)
148         for i in range(self.resolutionO):
149             par = float(i) / fltResOm1
151             pointO = self.splineO.CalcPoint(par)
152             worldPointsO.append(worldMatrixO * pointO)
154             derivativeO = self.splineO.CalcDerivative(par)
155             localDerivativesO.append(derivativeO)
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)
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)
172             worldOffsetsA = []
173             worldPoint0A = worldPointsA[0]
174             for i in range(self.resolutionA): worldOffsetsA.append(worldPointsA[i] - worldPoint0A)
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
182             prevDerivativeO = currDerivativeO
183             currWorldMatrixA = worldMatrixO * localRotMatO * currLocalAToLocalO
187         bmVerts = self.bMesh.verts
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
196                 self.bMesh.faces.new([bmVerts[currIndexA1], bmVerts[currIndexA2], bmVerts[nextIndexA2], bmVerts[nextIndexA1]])
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
206         blenderActiveCurve = bpy.context.active_object
207         blenderOtherCurve = selObjects[0]
208         if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
210         aCurve = Curves.Curve(blenderActiveCurve)
211         oCurve = Curves.Curve(blenderOtherCurve)
213         name = "TODO: autoname"
215         return SweptSurface(aCurve, oCurve, name)
218     def __init__(self, activeCurve, otherCurve, name = "SweptSurface"):
219         self.curveA = activeCurve
220         self.curveO = otherCurve
221         self.name  = name
223         self.nrSplines = self.curveA.nrSplines
224         if self.curveO.nrSplines < self.nrSplines: self.nrSplines = self.curveO.nrSplines
226         self.bMesh = bmesh.new()
228         self.splineSurfaces = self.SetupSplineSurfaces()
230         self.Apply()
233     def SetupSplineSurfaces(self):
234         rvSplineSurfaces = []
236         currV0Index = 0
237         for i in range(self.nrSplines):
238             splineA = self.curveA.splines[i]
239             splineO = self.curveO.splines[i]
241             resA = splineA.resolution
242             resO = splineO.resolution
244             for iv in range(resA * resO): self.bMesh.verts.new()
246             splSurf = SweptSplineSurface(splineA, splineO, self.bMesh, currV0Index, resA, resO)
248             rvSplineSurfaces.append(splSurf)
250             currV0Index += resA * resO
252         return rvSplineSurfaces
255     def Apply(self):
256         for splineSurface in self.splineSurfaces: splineSurface.Apply(self.curveA.worldMatrix, self.curveO.worldMatrix)
260         mesh = bpy.data.meshes.new("Mesh" + self.name)
262         self.bMesh.to_mesh(mesh)
263         mesh.update()
265         meshObject = bpy.data.objects.new(self.name, mesh)
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
278         self.bMesh = bMesh
279         self.vert0Index = vert0Index
280         self.resolutionRails = resolutionRails
281         self.resolutionProfile = resolutionProfile
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)
293         worldPointsRail1 = []
294         localDerivativesRail1 = []
295         worldPointsRail2 = []
296         fltResRailsm1 = float(self.resolutionRails - 1)
297         for i in range(self.resolutionRails):
298             par = float(i) / fltResRailsm1
300             pointRail1 = self.rail1Spline.CalcPoint(par)
301             worldPointsRail1.append(worldMatrixRail1 * pointRail1)
303             derivativeRail1 = self.rail1Spline.CalcDerivative(par)
304             localDerivativesRail1.append(derivativeRail1)
306             pointRail2 = self.rail2Spline.CalcPoint(par)
307             worldPointsRail2.append(worldMatrixRail2 * pointRail2)
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)
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)
324             worldOffsetsProfileRail1 = []
325             worldPoint0ProfileRail1 = worldPointsProfileRail1[0]
326             for iProfile in range(self.resolutionProfile): worldOffsetsProfileRail1.append(worldPointsProfileRail1[iProfile] - worldPoint0ProfileRail1)
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)
335             worldOffsetsProfileRail2 = []
336             for iProfile in range(self.resolutionProfile):
337                 offsetProfileRail1 = worldOffsetsProfileRail1[iProfile]
338                 worldOffsetsProfileRail2.append(rotMatRail2 * (offsetProfileRail1 * scaleFactorRail2))
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
346             prevDerivativeRail1 = currDerivativeRail1
347             currWorldMatrixProfile = worldMatrixRail1 * localRotMatRail1 * currLocalProfileToLocalRail1
351         bmVerts = self.bMesh.verts
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
360                 self.bMesh.faces.new([bmVerts[currIndex1], bmVerts[currIndex2], bmVerts[nextIndex2], bmVerts[nextIndex1]])
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
371         selectedObjects = bpy.context.scene.curvetools.SelectedObjects
372         selectedObjectValues = selectedObjects.values()
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")
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")
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")
393         rail1Curve = Curves.Curve(rail1BlenderCurve)
394         rail2Curve = Curves.Curve(rail2BlenderCurve)
395         profileCurve = Curves.Curve(profileBlenderCurve)
397         name = "TODO: autoname"
399         return BirailedSurface(rail1Curve, rail2Curve, profileCurve, name)
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
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
412         self.bMesh = bmesh.new()
414         self.splineSurfaces = self.SetupSplineSurfaces()
416         self.Apply()
419     def SetupSplineSurfaces(self):
420         rvSplineSurfaces = []
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]
428             resProfile = splineProfile.resolution
429             resRails = splineRail1.resolution
430             if splineRail2.resolution < resRails: resRails = splineRail2.resolution
432             for iv in range(resProfile * resRails): self.bMesh.verts.new()
434             splSurf = BirailedSplineSurface(splineRail1, splineRail2, splineProfile, self.bMesh, currV0Index, resRails, resProfile)