1import bpy 2import bmesh 3 4from . import mathematics 5from . import curves 6 7 8 9class 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 55class 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.collection.objects.link(meshObject) 123 124 125 126# active spline is swept over other spline (rail) 127class 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 = mathematics.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 203class 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.collection.objects.link(meshObject) 271 272 273 274# profileSpline is swept over rail1Spline and scaled/rotated to have its endpoint on rail2Spline 275class 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 = mathematics.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 if not v3From.magnitude == 0: 336 scaleFactorRail2 = v3To.magnitude / v3From.magnitude 337 else: 338 scaleFactorRail2 = 1 339 rotMatRail2 = mathematics.CalcRotationMatrix(v3From, v3To) 340 341 worldOffsetsProfileRail2 = [] 342 for iProfile in range(self.resolutionProfile): 343 offsetProfileRail1 = worldOffsetsProfileRail1[iProfile] 344 worldOffsetsProfileRail2.append(rotMatRail2 @ (offsetProfileRail1 * scaleFactorRail2)) 345 346 347 for iProfile in range(self.resolutionProfile): 348 iVert = self.vert0Index + (self.resolutionProfile * iRail) + iProfile 349 currVert = worldPointsRail1[iRail] + worldOffsetsProfileRail2[iProfile] 350 self.bMesh.verts[iVert].co = currVert 351 352 prevDerivativeRail1 = currDerivativeRail1 353 currWorldMatrixProfile = worldMatrixRail1 @ localRotMatRail1 @ currLocalProfileToLocalRail1 354 355 356 def AddFaces(self): 357 bmVerts = self.bMesh.verts 358 bmVerts.ensure_lookup_table() 359 360 for iRail in range(self.resolutionRails - 1): 361 for iProfile in range(self.resolutionProfile - 1): 362 currIndex1 = self.vert0Index + (self.resolutionProfile * iRail) + iProfile 363 currIndex2 = currIndex1 + 1 364 nextIndex1 = self.vert0Index + (self.resolutionProfile * (iRail + 1)) + iProfile 365 nextIndex2 = nextIndex1 + 1 366 367 self.bMesh.faces.new([bmVerts[currIndex1], bmVerts[currIndex2], bmVerts[nextIndex2], bmVerts[nextIndex1]]) 368 369 370 371class BirailedSurface: 372 @staticmethod 373 def FromSelection(): 374 selectedObjects = bpy.context.selected_objects 375 376 rail1Curve = curves.Curve(selectedObjects[0]) 377 rail2Curve = curves.Curve(selectedObjects[1]) 378 profileCurve = curves.Curve(selectedObjects[2]) 379 380 name = "BirailedSurface" 381 382 return BirailedSurface(rail1Curve, rail2Curve, profileCurve, name) 383 384 385 def __init__(self, rail1Curve, rail2Curve, profileCurve, name = "BirailedSurface"): 386 self.rail1Curve = rail1Curve 387 self.rail2Curve = rail2Curve 388 self.profileCurve = profileCurve 389 self.name = name 390 391 self.nrSplines = self.rail1Curve.nrSplines 392 if self.rail2Curve.nrSplines < self.nrSplines: self.nrSplines = self.rail2Curve.nrSplines 393 if self.profileCurve.nrSplines < self.nrSplines: self.nrSplines = self.profileCurve.nrSplines 394 395 self.bMesh = bmesh.new() 396 397 self.splineSurfaces = self.SetupSplineSurfaces() 398 399 self.Apply() 400 401 402 def SetupSplineSurfaces(self): 403 rvSplineSurfaces = [] 404 405 currV0Index = 0 406 for i in range(self.nrSplines): 407 splineRail1 = self.rail1Curve.splines[i] 408 splineRail2 = self.rail2Curve.splines[i] 409 splineProfile = self.profileCurve.splines[i] 410 411 resProfile = splineProfile.resolution 412 resRails = splineRail1.resolution 413 if splineRail2.resolution < resRails: resRails = splineRail2.resolution 414 415 for iv in range(resProfile * resRails): self.bMesh.verts.new() 416 417 splSurf = BirailedSplineSurface(splineRail1, splineRail2, splineProfile, self.bMesh, currV0Index, resRails, resProfile) 418 splSurf.AddFaces() 419 rvSplineSurfaces.append(splSurf) 420 421 currV0Index += resProfile * resRails 422 423 return rvSplineSurfaces 424 425 426 def Apply(self): 427 for splineSurface in self.splineSurfaces: splineSurface.Apply(self.rail1Curve.worldMatrix, self.rail2Curve.worldMatrix, self.profileCurve.worldMatrix) 428 429 430 def AddToScene(self): 431 mesh = bpy.data.meshes.new("Mesh" + self.name) 432 433 self.bMesh.to_mesh(mesh) 434 mesh.update() 435 436 meshObject = bpy.data.objects.new(self.name, mesh) 437 438 bpy.context.collection.objects.link(meshObject) 439