Sun Position: update translation
[blender-addons.git] / curve_tools / surfaces.py
blob691e16fc5955524f57b4db6d63f8675f24eb7db9
1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 import bpy
6 import bmesh
8 from . import mathematics
9 from . import curves
13 class LoftedSplineSurface:
14 def __init__(self, activeSpline, otherSpline, bMesh, vert0Index, resolution):
15 self.splineA = activeSpline
16 self.splineO = otherSpline
18 self.bMesh = bMesh
19 self.vert0Index = vert0Index
20 self.resolution = resolution
23 def Apply(self, worldMatrixA, worldMatrixO):
24 #deltaPar = 1.0 / float(self.resolution - 1)
26 par = 0.0
27 pointA = worldMatrixA @ self.splineA.CalcPoint(par)
28 pointO = worldMatrixO @ self.splineO.CalcPoint(par)
29 self.bMesh.verts[self.vert0Index].co = pointA
30 self.bMesh.verts[self.vert0Index + 1].co = pointO
32 fltResm1 = float(self.resolution - 1)
33 for i in range(1, self.resolution):
34 par = float(i) / fltResm1
36 pointA = worldMatrixA @ self.splineA.CalcPoint(par)
37 pointO = worldMatrixO @ self.splineO.CalcPoint(par)
38 self.bMesh.verts[self.vert0Index + 2 * i].co = pointA
39 self.bMesh.verts[self.vert0Index + 2 * i + 1].co = pointO
42 def AddFaces(self):
43 currIndexA = self.vert0Index
44 currIndexO = self.vert0Index + 1
46 bmVerts = self.bMesh.verts
47 bmVerts.ensure_lookup_table()
49 for i in range(1, self.resolution):
50 nextIndexA = self.vert0Index + 2 * i
51 nextIndexO = nextIndexA + 1
53 self.bMesh.faces.new([bmVerts[currIndexA], bmVerts[currIndexO], bmVerts[nextIndexO], bmVerts[nextIndexA]])
55 currIndexA = nextIndexA
56 currIndexO = nextIndexO
59 class LoftedSurface:
60 @staticmethod
61 def FromSelection():
62 selObjects = bpy.context.selected_objects
63 if len(selObjects) != 2: raise Exception("len(selObjects) != 2") # shouldn't be possible
65 blenderActiveCurve = bpy.context.active_object
66 blenderOtherCurve = selObjects[0]
67 if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
69 aCurve = curves.Curve(blenderActiveCurve)
70 oCurve = curves.Curve(blenderOtherCurve)
72 name = "TODO: autoname"
74 return LoftedSurface(aCurve, oCurve, name)
77 def __init__(self, activeCurve, otherCurve, name = "LoftedSurface"):
78 self.curveA = activeCurve
79 self.curveO = otherCurve
80 self.name = name
82 self.nrSplines = self.curveA.nrSplines
83 if self.curveO.nrSplines < self.nrSplines: self.nrSplines = self.curveO.nrSplines
85 self.bMesh = bmesh.new()
87 self.splineSurfaces = self.SetupSplineSurfaces()
89 self.Apply()
92 def SetupSplineSurfaces(self):
93 rvSplineSurfaces = []
95 currV0Index = 0
96 for i in range(self.nrSplines):
97 splineA = self.curveA.splines[i]
98 splineO = self.curveO.splines[i]
100 res = splineA.resolution
101 if splineO.resolution < res: res = splineO.resolution
103 for iv in range(2 * res): self.bMesh.verts.new()
105 splSurf = LoftedSplineSurface(splineA, splineO, self.bMesh, currV0Index, res)
106 splSurf.AddFaces()
107 rvSplineSurfaces.append(splSurf)
109 currV0Index += 2 * res
111 return rvSplineSurfaces
114 def Apply(self):
115 for splineSurface in self.splineSurfaces: splineSurface.Apply(self.curveA.worldMatrix, self.curveO.worldMatrix)
118 def AddToScene(self):
119 mesh = bpy.data.meshes.new("Mesh" + self.name)
121 self.bMesh.to_mesh(mesh)
122 mesh.update()
124 meshObject = bpy.data.objects.new(self.name, mesh)
126 bpy.context.collection.objects.link(meshObject)
130 # active spline is swept over other spline (rail)
131 class SweptSplineSurface:
132 def __init__(self, activeSpline, otherSpline, bMesh, vert0Index, resolutionA, resolutionO):
133 self.splineA = activeSpline
134 self.splineO = otherSpline
136 self.bMesh = bMesh
137 self.vert0Index = vert0Index
138 self.resolutionA = resolutionA
139 self.resolutionO = resolutionO
142 def Apply(self, worldMatrixA, worldMatrixO):
143 localPointsA = []
144 fltResAm1 = float(self.resolutionA - 1)
145 for i in range(self.resolutionA):
146 par = float(i) / fltResAm1
147 pointA = self.splineA.CalcPoint(par)
148 localPointsA.append(pointA)
151 worldPointsO = []
152 localDerivativesO = []
153 fltResOm1 = float(self.resolutionO - 1)
154 for i in range(self.resolutionO):
155 par = float(i) / fltResOm1
157 pointO = self.splineO.CalcPoint(par)
158 worldPointsO.append(worldMatrixO @ pointO)
160 derivativeO = self.splineO.CalcDerivative(par)
161 localDerivativesO.append(derivativeO)
164 currWorldMatrixA = worldMatrixA
165 worldMatrixOInv = worldMatrixO.inverted()
166 prevDerivativeO = localDerivativesO[0]
167 for iO in range(self.resolutionO):
168 currDerivativeO = localDerivativesO[iO]
169 localRotMatO = mathematics.CalcRotationMatrix(prevDerivativeO, currDerivativeO)
171 currLocalAToLocalO = worldMatrixOInv @ currWorldMatrixA
172 worldPointsA = []
173 for iA in range(self.resolutionA):
174 pointALocalToO = currLocalAToLocalO @ localPointsA[iA]
175 rotatedPointA = localRotMatO @ pointALocalToO
176 worldPointsA.append(worldMatrixO @ rotatedPointA)
178 worldOffsetsA = []
179 worldPoint0A = worldPointsA[0]
180 for i in range(self.resolutionA): worldOffsetsA.append(worldPointsA[i] - worldPoint0A)
183 for iA in range(self.resolutionA):
184 iVert = self.vert0Index + (self.resolutionA * iO) + iA
185 currVert = worldPointsO[iO] + worldOffsetsA[iA]
186 self.bMesh.verts[iVert].co = currVert
188 prevDerivativeO = currDerivativeO
189 currWorldMatrixA = worldMatrixO @ localRotMatO @ currLocalAToLocalO
192 def AddFaces(self):
193 bmVerts = self.bMesh.verts
194 bmVerts.ensure_lookup_table()
196 for iO in range(self.resolutionO - 1):
197 for iA in range(self.resolutionA - 1):
198 currIndexA1 = self.vert0Index + (self.resolutionA * iO) + iA
199 currIndexA2 = currIndexA1 + 1
200 nextIndexA1 = self.vert0Index + (self.resolutionA * (iO + 1)) + iA
201 nextIndexA2 = nextIndexA1 + 1
203 self.bMesh.faces.new([bmVerts[currIndexA1], bmVerts[currIndexA2], bmVerts[nextIndexA2], bmVerts[nextIndexA1]])
207 class SweptSurface:
208 @staticmethod
209 def FromSelection():
210 selObjects = bpy.context.selected_objects
211 if len(selObjects) != 2: raise Exception("len(selObjects) != 2") # shouldn't be possible
213 blenderActiveCurve = bpy.context.active_object
214 blenderOtherCurve = selObjects[0]
215 if blenderActiveCurve == blenderOtherCurve: blenderOtherCurve = selObjects[1]
217 aCurve = curves.Curve(blenderActiveCurve)
218 oCurve = curves.Curve(blenderOtherCurve)
220 name = "TODO: autoname"
222 return SweptSurface(aCurve, oCurve, name)
225 def __init__(self, activeCurve, otherCurve, name = "SweptSurface"):
226 self.curveA = activeCurve
227 self.curveO = otherCurve
228 self.name = name
230 self.nrSplines = self.curveA.nrSplines
231 if self.curveO.nrSplines < self.nrSplines: self.nrSplines = self.curveO.nrSplines
233 self.bMesh = bmesh.new()
235 self.splineSurfaces = self.SetupSplineSurfaces()
237 self.Apply()
240 def SetupSplineSurfaces(self):
241 rvSplineSurfaces = []
243 currV0Index = 0
244 for i in range(self.nrSplines):
245 splineA = self.curveA.splines[i]
246 splineO = self.curveO.splines[i]
248 resA = splineA.resolution
249 resO = splineO.resolution
251 for iv in range(resA * resO): self.bMesh.verts.new()
253 splSurf = SweptSplineSurface(splineA, splineO, self.bMesh, currV0Index, resA, resO)
254 splSurf.AddFaces()
255 rvSplineSurfaces.append(splSurf)
257 currV0Index += resA * resO
259 return rvSplineSurfaces
262 def Apply(self):
263 for splineSurface in self.splineSurfaces: splineSurface.Apply(self.curveA.worldMatrix, self.curveO.worldMatrix)
266 def AddToScene(self):
267 mesh = bpy.data.meshes.new("Mesh" + self.name)
269 self.bMesh.to_mesh(mesh)
270 mesh.update()
272 meshObject = bpy.data.objects.new(self.name, mesh)
274 bpy.context.collection.objects.link(meshObject)
278 # profileSpline is swept over rail1Spline and scaled/rotated to have its endpoint on rail2Spline
279 class BirailedSplineSurface:
280 def __init__(self, rail1Spline, rail2Spline, profileSpline, bMesh, vert0Index, resolutionRails, resolutionProfile):
281 self.rail1Spline = rail1Spline
282 self.rail2Spline = rail2Spline
283 self.profileSpline = profileSpline
285 self.bMesh = bMesh
286 self.vert0Index = vert0Index
287 self.resolutionRails = resolutionRails
288 self.resolutionProfile = resolutionProfile
291 def Apply(self, worldMatrixRail1, worldMatrixRail2, worldMatrixProfile):
292 localPointsProfile = []
293 fltResProfilem1 = float(self.resolutionProfile - 1)
294 for i in range(self.resolutionProfile):
295 par = float(i) / fltResProfilem1
296 pointProfile = self.profileSpline.CalcPoint(par)
297 localPointsProfile.append(pointProfile)
300 worldPointsRail1 = []
301 localDerivativesRail1 = []
302 worldPointsRail2 = []
303 fltResRailsm1 = float(self.resolutionRails - 1)
304 for i in range(self.resolutionRails):
305 par = float(i) / fltResRailsm1
307 pointRail1 = self.rail1Spline.CalcPoint(par)
308 worldPointsRail1.append(worldMatrixRail1 @ pointRail1)
310 derivativeRail1 = self.rail1Spline.CalcDerivative(par)
311 localDerivativesRail1.append(derivativeRail1)
313 pointRail2 = self.rail2Spline.CalcPoint(par)
314 worldPointsRail2.append(worldMatrixRail2 @ pointRail2)
317 currWorldMatrixProfile = worldMatrixProfile
318 worldMatrixRail1Inv = worldMatrixRail1.inverted()
319 prevDerivativeRail1 = localDerivativesRail1[0]
320 for iRail in range(self.resolutionRails):
321 currDerivativeRail1 = localDerivativesRail1[iRail]
322 localRotMatRail1 = mathematics.CalcRotationMatrix(prevDerivativeRail1, currDerivativeRail1)
324 currLocalProfileToLocalRail1 = worldMatrixRail1Inv @ currWorldMatrixProfile
325 worldPointsProfileRail1 = []
326 for iProfile in range(self.resolutionProfile):
327 pointProfileLocalToRail1 = currLocalProfileToLocalRail1 @ localPointsProfile[iProfile]
328 rotatedPointProfile = localRotMatRail1 @ pointProfileLocalToRail1
329 worldPointsProfileRail1.append(worldMatrixRail1 @ rotatedPointProfile)
331 worldOffsetsProfileRail1 = []
332 worldPoint0ProfileRail1 = worldPointsProfileRail1[0]
333 for iProfile in range(self.resolutionProfile): worldOffsetsProfileRail1.append(worldPointsProfileRail1[iProfile] - worldPoint0ProfileRail1)
335 worldStartPointProfileRail1 = worldPointsRail1[iRail]
336 worldEndPointProfileRail1 = worldStartPointProfileRail1 + worldOffsetsProfileRail1[-1]
337 v3From = worldEndPointProfileRail1 - worldStartPointProfileRail1
338 v3To = worldPointsRail2[iRail] - worldStartPointProfileRail1
339 if not v3From.magnitude == 0:
340 scaleFactorRail2 = v3To.magnitude / v3From.magnitude
341 else:
342 scaleFactorRail2 = 1
343 rotMatRail2 = mathematics.CalcRotationMatrix(v3From, v3To)
345 worldOffsetsProfileRail2 = []
346 for iProfile in range(self.resolutionProfile):
347 offsetProfileRail1 = worldOffsetsProfileRail1[iProfile]
348 worldOffsetsProfileRail2.append(rotMatRail2 @ (offsetProfileRail1 * scaleFactorRail2))
351 for iProfile in range(self.resolutionProfile):
352 iVert = self.vert0Index + (self.resolutionProfile * iRail) + iProfile
353 currVert = worldPointsRail1[iRail] + worldOffsetsProfileRail2[iProfile]
354 self.bMesh.verts[iVert].co = currVert
356 prevDerivativeRail1 = currDerivativeRail1
357 currWorldMatrixProfile = worldMatrixRail1 @ localRotMatRail1 @ currLocalProfileToLocalRail1
360 def AddFaces(self):
361 bmVerts = self.bMesh.verts
362 bmVerts.ensure_lookup_table()
364 for iRail in range(self.resolutionRails - 1):
365 for iProfile in range(self.resolutionProfile - 1):
366 currIndex1 = self.vert0Index + (self.resolutionProfile * iRail) + iProfile
367 currIndex2 = currIndex1 + 1
368 nextIndex1 = self.vert0Index + (self.resolutionProfile * (iRail + 1)) + iProfile
369 nextIndex2 = nextIndex1 + 1
371 self.bMesh.faces.new([bmVerts[currIndex1], bmVerts[currIndex2], bmVerts[nextIndex2], bmVerts[nextIndex1]])
375 class BirailedSurface:
376 @staticmethod
377 def FromSelection():
378 selectedObjects = bpy.context.selected_objects
380 rail1Curve = curves.Curve(selectedObjects[0])
381 rail2Curve = curves.Curve(selectedObjects[1])
382 profileCurve = curves.Curve(selectedObjects[2])
384 name = "BirailedSurface"
386 return BirailedSurface(rail1Curve, rail2Curve, profileCurve, name)
389 def __init__(self, rail1Curve, rail2Curve, profileCurve, name = "BirailedSurface"):
390 self.rail1Curve = rail1Curve
391 self.rail2Curve = rail2Curve
392 self.profileCurve = profileCurve
393 self.name = name
395 self.nrSplines = self.rail1Curve.nrSplines
396 if self.rail2Curve.nrSplines < self.nrSplines: self.nrSplines = self.rail2Curve.nrSplines
397 if self.profileCurve.nrSplines < self.nrSplines: self.nrSplines = self.profileCurve.nrSplines
399 self.bMesh = bmesh.new()
401 self.splineSurfaces = self.SetupSplineSurfaces()
403 self.Apply()
406 def SetupSplineSurfaces(self):
407 rvSplineSurfaces = []
409 currV0Index = 0
410 for i in range(self.nrSplines):
411 splineRail1 = self.rail1Curve.splines[i]
412 splineRail2 = self.rail2Curve.splines[i]
413 splineProfile = self.profileCurve.splines[i]
415 resProfile = splineProfile.resolution
416 resRails = splineRail1.resolution
417 if splineRail2.resolution < resRails: resRails = splineRail2.resolution
419 for iv in range(resProfile * resRails): self.bMesh.verts.new()
421 splSurf = BirailedSplineSurface(splineRail1, splineRail2, splineProfile, self.bMesh, currV0Index, resRails, resProfile)
422 splSurf.AddFaces()
423 rvSplineSurfaces.append(splSurf)
425 currV0Index += resProfile * resRails
427 return rvSplineSurfaces
430 def Apply(self):
431 for splineSurface in self.splineSurfaces: splineSurface.Apply(self.rail1Curve.worldMatrix, self.rail2Curve.worldMatrix, self.profileCurve.worldMatrix)
434 def AddToScene(self):
435 mesh = bpy.data.meshes.new("Mesh" + self.name)
437 self.bMesh.to_mesh(mesh)
438 mesh.update()
440 meshObject = bpy.data.objects.new(self.name, mesh)
442 bpy.context.collection.objects.link(meshObject)