1 # SPDX-License-Identifier: GPL-2.0-or-later
6 from . import mathematics
11 class LoftedSplineSurface
:
12 def __init__(self
, activeSpline
, otherSpline
, bMesh
, vert0Index
, resolution
):
13 self
.splineA
= activeSpline
14 self
.splineO
= otherSpline
17 self
.vert0Index
= vert0Index
18 self
.resolution
= resolution
21 def Apply(self
, worldMatrixA
, worldMatrixO
):
22 #deltaPar = 1.0 / float(self.resolution - 1)
25 pointA
= worldMatrixA
@ self
.splineA
.CalcPoint(par
)
26 pointO
= worldMatrixO
@ self
.splineO
.CalcPoint(par
)
27 self
.bMesh
.verts
[self
.vert0Index
].co
= pointA
28 self
.bMesh
.verts
[self
.vert0Index
+ 1].co
= pointO
30 fltResm1
= float(self
.resolution
- 1)
31 for i
in range(1, self
.resolution
):
32 par
= float(i
) / fltResm1
34 pointA
= worldMatrixA
@ self
.splineA
.CalcPoint(par
)
35 pointO
= worldMatrixO
@ self
.splineO
.CalcPoint(par
)
36 self
.bMesh
.verts
[self
.vert0Index
+ 2 * i
].co
= pointA
37 self
.bMesh
.verts
[self
.vert0Index
+ 2 * i
+ 1].co
= pointO
41 currIndexA
= self
.vert0Index
42 currIndexO
= self
.vert0Index
+ 1
44 bmVerts
= self
.bMesh
.verts
45 bmVerts
.ensure_lookup_table()
47 for i
in range(1, self
.resolution
):
48 nextIndexA
= self
.vert0Index
+ 2 * i
49 nextIndexO
= nextIndexA
+ 1
51 self
.bMesh
.faces
.new([bmVerts
[currIndexA
], bmVerts
[currIndexO
], bmVerts
[nextIndexO
], bmVerts
[nextIndexA
]])
53 currIndexA
= nextIndexA
54 currIndexO
= nextIndexO
60 selObjects
= bpy
.context
.selected_objects
61 if len(selObjects
) != 2: raise Exception("len(selObjects) != 2") # shouldn't be possible
63 blenderActiveCurve
= bpy
.context
.active_object
64 blenderOtherCurve
= selObjects
[0]
65 if blenderActiveCurve
== blenderOtherCurve
: blenderOtherCurve
= selObjects
[1]
67 aCurve
= curves
.Curve(blenderActiveCurve
)
68 oCurve
= curves
.Curve(blenderOtherCurve
)
70 name
= "TODO: autoname"
72 return LoftedSurface(aCurve
, oCurve
, name
)
75 def __init__(self
, activeCurve
, otherCurve
, name
= "LoftedSurface"):
76 self
.curveA
= activeCurve
77 self
.curveO
= otherCurve
80 self
.nrSplines
= self
.curveA
.nrSplines
81 if self
.curveO
.nrSplines
< self
.nrSplines
: self
.nrSplines
= self
.curveO
.nrSplines
83 self
.bMesh
= bmesh
.new()
85 self
.splineSurfaces
= self
.SetupSplineSurfaces()
90 def SetupSplineSurfaces(self
):
94 for i
in range(self
.nrSplines
):
95 splineA
= self
.curveA
.splines
[i
]
96 splineO
= self
.curveO
.splines
[i
]
98 res
= splineA
.resolution
99 if splineO
.resolution
< res
: res
= splineO
.resolution
101 for iv
in range(2 * res
): self
.bMesh
.verts
.new()
103 splSurf
= LoftedSplineSurface(splineA
, splineO
, self
.bMesh
, currV0Index
, res
)
105 rvSplineSurfaces
.append(splSurf
)
107 currV0Index
+= 2 * res
109 return rvSplineSurfaces
113 for splineSurface
in self
.splineSurfaces
: splineSurface
.Apply(self
.curveA
.worldMatrix
, self
.curveO
.worldMatrix
)
116 def AddToScene(self
):
117 mesh
= bpy
.data
.meshes
.new("Mesh" + self
.name
)
119 self
.bMesh
.to_mesh(mesh
)
122 meshObject
= bpy
.data
.objects
.new(self
.name
, mesh
)
124 bpy
.context
.collection
.objects
.link(meshObject
)
128 # active spline is swept over other spline (rail)
129 class SweptSplineSurface
:
130 def __init__(self
, activeSpline
, otherSpline
, bMesh
, vert0Index
, resolutionA
, resolutionO
):
131 self
.splineA
= activeSpline
132 self
.splineO
= otherSpline
135 self
.vert0Index
= vert0Index
136 self
.resolutionA
= resolutionA
137 self
.resolutionO
= resolutionO
140 def Apply(self
, worldMatrixA
, worldMatrixO
):
142 fltResAm1
= float(self
.resolutionA
- 1)
143 for i
in range(self
.resolutionA
):
144 par
= float(i
) / fltResAm1
145 pointA
= self
.splineA
.CalcPoint(par
)
146 localPointsA
.append(pointA
)
150 localDerivativesO
= []
151 fltResOm1
= float(self
.resolutionO
- 1)
152 for i
in range(self
.resolutionO
):
153 par
= float(i
) / fltResOm1
155 pointO
= self
.splineO
.CalcPoint(par
)
156 worldPointsO
.append(worldMatrixO
@ pointO
)
158 derivativeO
= self
.splineO
.CalcDerivative(par
)
159 localDerivativesO
.append(derivativeO
)
162 currWorldMatrixA
= worldMatrixA
163 worldMatrixOInv
= worldMatrixO
.inverted()
164 prevDerivativeO
= localDerivativesO
[0]
165 for iO
in range(self
.resolutionO
):
166 currDerivativeO
= localDerivativesO
[iO
]
167 localRotMatO
= mathematics
.CalcRotationMatrix(prevDerivativeO
, currDerivativeO
)
169 currLocalAToLocalO
= worldMatrixOInv
@ currWorldMatrixA
171 for iA
in range(self
.resolutionA
):
172 pointALocalToO
= currLocalAToLocalO
@ localPointsA
[iA
]
173 rotatedPointA
= localRotMatO
@ pointALocalToO
174 worldPointsA
.append(worldMatrixO
@ rotatedPointA
)
177 worldPoint0A
= worldPointsA
[0]
178 for i
in range(self
.resolutionA
): worldOffsetsA
.append(worldPointsA
[i
] - worldPoint0A
)
181 for iA
in range(self
.resolutionA
):
182 iVert
= self
.vert0Index
+ (self
.resolutionA
* iO
) + iA
183 currVert
= worldPointsO
[iO
] + worldOffsetsA
[iA
]
184 self
.bMesh
.verts
[iVert
].co
= currVert
186 prevDerivativeO
= currDerivativeO
187 currWorldMatrixA
= worldMatrixO
@ localRotMatO
@ currLocalAToLocalO
191 bmVerts
= self
.bMesh
.verts
192 bmVerts
.ensure_lookup_table()
194 for iO
in range(self
.resolutionO
- 1):
195 for iA
in range(self
.resolutionA
- 1):
196 currIndexA1
= self
.vert0Index
+ (self
.resolutionA
* iO
) + iA
197 currIndexA2
= currIndexA1
+ 1
198 nextIndexA1
= self
.vert0Index
+ (self
.resolutionA
* (iO
+ 1)) + iA
199 nextIndexA2
= nextIndexA1
+ 1
201 self
.bMesh
.faces
.new([bmVerts
[currIndexA1
], bmVerts
[currIndexA2
], bmVerts
[nextIndexA2
], bmVerts
[nextIndexA1
]])
208 selObjects
= bpy
.context
.selected_objects
209 if len(selObjects
) != 2: raise Exception("len(selObjects) != 2") # shouldn't be possible
211 blenderActiveCurve
= bpy
.context
.active_object
212 blenderOtherCurve
= selObjects
[0]
213 if blenderActiveCurve
== blenderOtherCurve
: blenderOtherCurve
= selObjects
[1]
215 aCurve
= curves
.Curve(blenderActiveCurve
)
216 oCurve
= curves
.Curve(blenderOtherCurve
)
218 name
= "TODO: autoname"
220 return SweptSurface(aCurve
, oCurve
, name
)
223 def __init__(self
, activeCurve
, otherCurve
, name
= "SweptSurface"):
224 self
.curveA
= activeCurve
225 self
.curveO
= otherCurve
228 self
.nrSplines
= self
.curveA
.nrSplines
229 if self
.curveO
.nrSplines
< self
.nrSplines
: self
.nrSplines
= self
.curveO
.nrSplines
231 self
.bMesh
= bmesh
.new()
233 self
.splineSurfaces
= self
.SetupSplineSurfaces()
238 def SetupSplineSurfaces(self
):
239 rvSplineSurfaces
= []
242 for i
in range(self
.nrSplines
):
243 splineA
= self
.curveA
.splines
[i
]
244 splineO
= self
.curveO
.splines
[i
]
246 resA
= splineA
.resolution
247 resO
= splineO
.resolution
249 for iv
in range(resA
* resO
): self
.bMesh
.verts
.new()
251 splSurf
= SweptSplineSurface(splineA
, splineO
, self
.bMesh
, currV0Index
, resA
, resO
)
253 rvSplineSurfaces
.append(splSurf
)
255 currV0Index
+= resA
* resO
257 return rvSplineSurfaces
261 for splineSurface
in self
.splineSurfaces
: splineSurface
.Apply(self
.curveA
.worldMatrix
, self
.curveO
.worldMatrix
)
264 def AddToScene(self
):
265 mesh
= bpy
.data
.meshes
.new("Mesh" + self
.name
)
267 self
.bMesh
.to_mesh(mesh
)
270 meshObject
= bpy
.data
.objects
.new(self
.name
, mesh
)
272 bpy
.context
.collection
.objects
.link(meshObject
)
276 # profileSpline is swept over rail1Spline and scaled/rotated to have its endpoint on rail2Spline
277 class BirailedSplineSurface
:
278 def __init__(self
, rail1Spline
, rail2Spline
, profileSpline
, bMesh
, vert0Index
, resolutionRails
, resolutionProfile
):
279 self
.rail1Spline
= rail1Spline
280 self
.rail2Spline
= rail2Spline
281 self
.profileSpline
= profileSpline
284 self
.vert0Index
= vert0Index
285 self
.resolutionRails
= resolutionRails
286 self
.resolutionProfile
= resolutionProfile
289 def Apply(self
, worldMatrixRail1
, worldMatrixRail2
, worldMatrixProfile
):
290 localPointsProfile
= []
291 fltResProfilem1
= float(self
.resolutionProfile
- 1)
292 for i
in range(self
.resolutionProfile
):
293 par
= float(i
) / fltResProfilem1
294 pointProfile
= self
.profileSpline
.CalcPoint(par
)
295 localPointsProfile
.append(pointProfile
)
298 worldPointsRail1
= []
299 localDerivativesRail1
= []
300 worldPointsRail2
= []
301 fltResRailsm1
= float(self
.resolutionRails
- 1)
302 for i
in range(self
.resolutionRails
):
303 par
= float(i
) / fltResRailsm1
305 pointRail1
= self
.rail1Spline
.CalcPoint(par
)
306 worldPointsRail1
.append(worldMatrixRail1
@ pointRail1
)
308 derivativeRail1
= self
.rail1Spline
.CalcDerivative(par
)
309 localDerivativesRail1
.append(derivativeRail1
)
311 pointRail2
= self
.rail2Spline
.CalcPoint(par
)
312 worldPointsRail2
.append(worldMatrixRail2
@ pointRail2
)
315 currWorldMatrixProfile
= worldMatrixProfile
316 worldMatrixRail1Inv
= worldMatrixRail1
.inverted()
317 prevDerivativeRail1
= localDerivativesRail1
[0]
318 for iRail
in range(self
.resolutionRails
):
319 currDerivativeRail1
= localDerivativesRail1
[iRail
]
320 localRotMatRail1
= mathematics
.CalcRotationMatrix(prevDerivativeRail1
, currDerivativeRail1
)
322 currLocalProfileToLocalRail1
= worldMatrixRail1Inv
@ currWorldMatrixProfile
323 worldPointsProfileRail1
= []
324 for iProfile
in range(self
.resolutionProfile
):
325 pointProfileLocalToRail1
= currLocalProfileToLocalRail1
@ localPointsProfile
[iProfile
]
326 rotatedPointProfile
= localRotMatRail1
@ pointProfileLocalToRail1
327 worldPointsProfileRail1
.append(worldMatrixRail1
@ rotatedPointProfile
)
329 worldOffsetsProfileRail1
= []
330 worldPoint0ProfileRail1
= worldPointsProfileRail1
[0]
331 for iProfile
in range(self
.resolutionProfile
): worldOffsetsProfileRail1
.append(worldPointsProfileRail1
[iProfile
] - worldPoint0ProfileRail1
)
333 worldStartPointProfileRail1
= worldPointsRail1
[iRail
]
334 worldEndPointProfileRail1
= worldStartPointProfileRail1
+ worldOffsetsProfileRail1
[-1]
335 v3From
= worldEndPointProfileRail1
- worldStartPointProfileRail1
336 v3To
= worldPointsRail2
[iRail
] - worldStartPointProfileRail1
337 if not v3From
.magnitude
== 0:
338 scaleFactorRail2
= v3To
.magnitude
/ v3From
.magnitude
341 rotMatRail2
= mathematics
.CalcRotationMatrix(v3From
, v3To
)
343 worldOffsetsProfileRail2
= []
344 for iProfile
in range(self
.resolutionProfile
):
345 offsetProfileRail1
= worldOffsetsProfileRail1
[iProfile
]
346 worldOffsetsProfileRail2
.append(rotMatRail2
@ (offsetProfileRail1
* scaleFactorRail2
))
349 for iProfile
in range(self
.resolutionProfile
):
350 iVert
= self
.vert0Index
+ (self
.resolutionProfile
* iRail
) + iProfile
351 currVert
= worldPointsRail1
[iRail
] + worldOffsetsProfileRail2
[iProfile
]
352 self
.bMesh
.verts
[iVert
].co
= currVert
354 prevDerivativeRail1
= currDerivativeRail1
355 currWorldMatrixProfile
= worldMatrixRail1
@ localRotMatRail1
@ currLocalProfileToLocalRail1
359 bmVerts
= self
.bMesh
.verts
360 bmVerts
.ensure_lookup_table()
362 for iRail
in range(self
.resolutionRails
- 1):
363 for iProfile
in range(self
.resolutionProfile
- 1):
364 currIndex1
= self
.vert0Index
+ (self
.resolutionProfile
* iRail
) + iProfile
365 currIndex2
= currIndex1
+ 1
366 nextIndex1
= self
.vert0Index
+ (self
.resolutionProfile
* (iRail
+ 1)) + iProfile
367 nextIndex2
= nextIndex1
+ 1
369 self
.bMesh
.faces
.new([bmVerts
[currIndex1
], bmVerts
[currIndex2
], bmVerts
[nextIndex2
], bmVerts
[nextIndex1
]])
373 class BirailedSurface
:
376 selectedObjects
= bpy
.context
.selected_objects
378 rail1Curve
= curves
.Curve(selectedObjects
[0])
379 rail2Curve
= curves
.Curve(selectedObjects
[1])
380 profileCurve
= curves
.Curve(selectedObjects
[2])
382 name
= "BirailedSurface"
384 return BirailedSurface(rail1Curve
, rail2Curve
, profileCurve
, name
)
387 def __init__(self
, rail1Curve
, rail2Curve
, profileCurve
, name
= "BirailedSurface"):
388 self
.rail1Curve
= rail1Curve
389 self
.rail2Curve
= rail2Curve
390 self
.profileCurve
= profileCurve
393 self
.nrSplines
= self
.rail1Curve
.nrSplines
394 if self
.rail2Curve
.nrSplines
< self
.nrSplines
: self
.nrSplines
= self
.rail2Curve
.nrSplines
395 if self
.profileCurve
.nrSplines
< self
.nrSplines
: self
.nrSplines
= self
.profileCurve
.nrSplines
397 self
.bMesh
= bmesh
.new()
399 self
.splineSurfaces
= self
.SetupSplineSurfaces()
404 def SetupSplineSurfaces(self
):
405 rvSplineSurfaces
= []
408 for i
in range(self
.nrSplines
):
409 splineRail1
= self
.rail1Curve
.splines
[i
]
410 splineRail2
= self
.rail2Curve
.splines
[i
]
411 splineProfile
= self
.profileCurve
.splines
[i
]
413 resProfile
= splineProfile
.resolution
414 resRails
= splineRail1
.resolution
415 if splineRail2
.resolution
< resRails
: resRails
= splineRail2
.resolution
417 for iv
in range(resProfile
* resRails
): self
.bMesh
.verts
.new()
419 splSurf
= BirailedSplineSurface(splineRail1
, splineRail2
, splineProfile
, self
.bMesh
, currV0Index
, resRails
, resProfile
)
421 rvSplineSurfaces
.append(splSurf
)
423 currV0Index
+= resProfile
* resRails
425 return rvSplineSurfaces
429 for splineSurface
in self
.splineSurfaces
: splineSurface
.Apply(self
.rail1Curve
.worldMatrix
, self
.rail2Curve
.worldMatrix
, self
.profileCurve
.worldMatrix
)
432 def AddToScene(self
):
433 mesh
= bpy
.data
.meshes
.new("Mesh" + self
.name
)
435 self
.bMesh
.to_mesh(mesh
)
438 meshObject
= bpy
.data
.objects
.new(self
.name
, mesh
)
440 bpy
.context
.collection
.objects
.link(meshObject
)