1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
8 from . import mathematics
13 class LoftedSplineSurface
:
14 def __init__(self
, activeSpline
, otherSpline
, bMesh
, vert0Index
, resolution
):
15 self
.splineA
= activeSpline
16 self
.splineO
= otherSpline
19 self
.vert0Index
= vert0Index
20 self
.resolution
= resolution
23 def Apply(self
, worldMatrixA
, worldMatrixO
):
24 #deltaPar = 1.0 / float(self.resolution - 1)
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
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
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
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()
92 def SetupSplineSurfaces(self
):
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
)
107 rvSplineSurfaces
.append(splSurf
)
109 currV0Index
+= 2 * res
111 return rvSplineSurfaces
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
)
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
137 self
.vert0Index
= vert0Index
138 self
.resolutionA
= resolutionA
139 self
.resolutionO
= resolutionO
142 def Apply(self
, worldMatrixA
, worldMatrixO
):
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
)
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
173 for iA
in range(self
.resolutionA
):
174 pointALocalToO
= currLocalAToLocalO
@ localPointsA
[iA
]
175 rotatedPointA
= localRotMatO
@ pointALocalToO
176 worldPointsA
.append(worldMatrixO
@ rotatedPointA
)
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
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
]])
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
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()
240 def SetupSplineSurfaces(self
):
241 rvSplineSurfaces
= []
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
)
255 rvSplineSurfaces
.append(splSurf
)
257 currV0Index
+= resA
* resO
259 return rvSplineSurfaces
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
)
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
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
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
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
:
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
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()
406 def SetupSplineSurfaces(self
):
407 rvSplineSurfaces
= []
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
)
423 rvSplineSurfaces
.append(splSurf
)
425 currV0Index
+= resProfile
* resRails
427 return rvSplineSurfaces
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
)
440 meshObject
= bpy
.data
.objects
.new(self
.name
, mesh
)
442 bpy
.context
.collection
.objects
.link(meshObject
)