Merge branch 'master' into blender2.8
[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)