1import bpy 2from . import mathematics 3from . import curves 4from . import util 5 6from mathutils import Vector 7 8algoPOV = None 9algoDIR = None 10 11 12class BezierSegmentIntersectionPoint: 13 def __init__(self, segment, parameter, intersectionPoint): 14 self.segment = segment 15 self.parameter = parameter 16 self.intersectionPoint = intersectionPoint 17 18 19class BezierSegmentsIntersector: 20 def __init__(self, segment1, segment2, worldMatrix1, worldMatrix2): 21 self.segment1 = segment1 22 self.segment2 = segment2 23 self.worldMatrix1 = worldMatrix1 24 self.worldMatrix2 = worldMatrix2 25 26 def CalcFirstIntersection(self, nrSamples1, nrSamples2): 27 algorithm = bpy.context.scene.curvetools.IntersectCurvesAlgorithm 28 29 if algorithm == '3D': 30 return self.CalcFirstRealIntersection3D(nrSamples1, nrSamples2) 31 32 if algorithm == 'From_View': 33 global algoDIR 34 if algoDIR is not None: 35 return self.CalcFirstRealIntersectionFromViewDIR(nrSamples1, nrSamples2) 36 37 global algoPOV 38 if algoPOV is not None: 39 return self.CalcFirstRealIntersectionFromViewPOV(nrSamples1, nrSamples2) 40 41 return None 42 43 def CalcFirstIntersection3D(self, nrSamples1, nrSamples2): 44 fltNrSamples1 = float(nrSamples1) 45 fltNrSamples2 = float(nrSamples2) 46 47 limitDistance = bpy.context.scene.curvetools.LimitDistance 48 49 for iSample1 in range(nrSamples1): 50 segPar10 = float(iSample1) / fltNrSamples1 51 segPar11 = float(iSample1 + 1) / fltNrSamples1 52 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 53 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 54 55 for iSample2 in range(nrSamples2): 56 segPar20 = float(iSample2) / fltNrSamples2 57 segPar21 = float(iSample2 + 1) / fltNrSamples2 58 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 59 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 60 61 intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance) 62 if intersectionPointData is None: 63 continue 64 65 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 66 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 67 intersectionSegment1Parameter, 68 intersectionPointData[2]) 69 70 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 71 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 72 intersectionSegment2Parameter, 73 intersectionPointData[3]) 74 75 return [intersectionPoint1, intersectionPoint2] 76 77 return None 78 79 def CalcFirstRealIntersection3D(self, nrSamples1, nrSamples2): 80 fltNrSamples1 = float(nrSamples1) 81 fltNrSamples2 = float(nrSamples2) 82 83 limitDistance = bpy.context.scene.curvetools.LimitDistance 84 85 for iSample1 in range(nrSamples1): 86 segPar10 = float(iSample1) / fltNrSamples1 87 segPar11 = float(iSample1 + 1) / fltNrSamples1 88 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 89 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 90 91 for iSample2 in range(nrSamples2): 92 segPar20 = float(iSample2) / fltNrSamples2 93 segPar21 = float(iSample2 + 1) / fltNrSamples2 94 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 95 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 96 97 intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance) 98 if intersectionPointData is None: 99 continue 100 101 # intersection point can't be an existing point 102 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / (fltNrSamples1)) 103 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 104 if (mathematics.IsSamePoint(P0, worldPoint1, limitDistance)) or \ 105 (mathematics.IsSamePoint(P1, worldPoint1, limitDistance)): 106 107 intersectionPoint1 = None 108 else: 109 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 110 intersectionSegment1Parameter, 111 worldPoint1) 112 113 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / (fltNrSamples2)) 114 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 115 if (mathematics.IsSamePoint(Q0, worldPoint2, limitDistance)) or \ 116 (mathematics.IsSamePoint(Q1, worldPoint2, limitDistance)): 117 118 intersectionPoint2 = None 119 else: 120 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 121 intersectionSegment2Parameter, 122 worldPoint2) 123 124 return [intersectionPoint1, intersectionPoint2] 125 126 return None 127 128 def CalcFirstIntersectionFromViewDIR(self, nrSamples1, nrSamples2): 129 global algoDIR 130 131 fltNrSamples1 = float(nrSamples1) 132 fltNrSamples2 = float(nrSamples2) 133 134 for iSample1 in range(nrSamples1): 135 segPar10 = float(iSample1) / fltNrSamples1 136 segPar11 = float(iSample1 + 1) / fltNrSamples1 137 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 138 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 139 140 for iSample2 in range(nrSamples2): 141 segPar20 = float(iSample2) / fltNrSamples2 142 segPar21 = float(iSample2 + 1) / fltNrSamples2 143 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 144 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 145 146 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR) 147 if intersectionPointData is None: 148 continue 149 150 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 151 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 152 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 153 intersectionSegment1Parameter, 154 worldPoint1) 155 156 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 157 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 158 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 159 intersectionSegment2Parameter, 160 worldPoint2) 161 162 return [intersectionPoint1, intersectionPoint2] 163 164 return None 165 166 def CalcFirstRealIntersectionFromViewDIR(self, nrSamples1, nrSamples2): 167 global algoDIR 168 169 fltNrSamples1 = float(nrSamples1) 170 fltNrSamples2 = float(nrSamples2) 171 172 limitDistance = bpy.context.scene.curvetools.LimitDistance 173 174 for iSample1 in range(nrSamples1): 175 segPar10 = float(iSample1) / fltNrSamples1 176 segPar11 = float(iSample1 + 1) / fltNrSamples1 177 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 178 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 179 180 for iSample2 in range(nrSamples2): 181 segPar20 = float(iSample2) / fltNrSamples2 182 segPar21 = float(iSample2 + 1) / fltNrSamples2 183 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 184 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 185 186 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR) 187 if intersectionPointData is None: 188 continue 189 190 # intersection point can't be an existing point 191 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / (fltNrSamples1)) 192 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 193 if (mathematics.IsSamePoint(P0, worldPoint1, limitDistance)) or \ 194 (mathematics.IsSamePoint(P1, worldPoint1, limitDistance)): 195 196 intersectionPoint1 = None 197 else: 198 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 199 intersectionSegment1Parameter, 200 worldPoint1) 201 202 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / (fltNrSamples2)) 203 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 204 if (mathematics.IsSamePoint(Q0, worldPoint2, limitDistance)) or \ 205 (mathematics.IsSamePoint(Q1, worldPoint2, limitDistance)): 206 207 intersectionPoint2 = None 208 else: 209 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 210 intersectionSegment2Parameter, 211 worldPoint2) 212 213 return [intersectionPoint1, intersectionPoint2] 214 215 return None 216 217 def CalcFirstIntersectionFromViewPOV(self, nrSamples1, nrSamples2): 218 global algoPOV 219 220 fltNrSamples1 = float(nrSamples1) 221 fltNrSamples2 = float(nrSamples2) 222 223 for iSample1 in range(nrSamples1): 224 segPar10 = float(iSample1) / fltNrSamples1 225 segPar11 = float(iSample1 + 1) / fltNrSamples1 226 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 227 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 228 229 for iSample2 in range(nrSamples2): 230 segPar20 = float(iSample2) / fltNrSamples2 231 segPar21 = float(iSample2 + 1) / fltNrSamples2 232 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 233 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 234 235 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV) 236 if intersectionPointData is None: 237 continue 238 239 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 240 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 241 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 242 intersectionSegment1Parameter, 243 worldPoint1) 244 245 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 246 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 247 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 248 intersectionSegment2Parameter, 249 worldPoint2) 250 251 return [intersectionPoint1, intersectionPoint2] 252 253 return None 254 255 def CalcFirstRealIntersectionFromViewPOV(self, nrSamples1, nrSamples2): 256 global algoPOV 257 258 fltNrSamples1 = float(nrSamples1) 259 fltNrSamples2 = float(nrSamples2) 260 261 limitDistance = bpy.context.scene.curvetools.LimitDistance 262 263 for iSample1 in range(nrSamples1): 264 segPar10 = float(iSample1) / fltNrSamples1 265 segPar11 = float(iSample1 + 1) / fltNrSamples1 266 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 267 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 268 269 for iSample2 in range(nrSamples2): 270 segPar20 = float(iSample2) / fltNrSamples2 271 segPar21 = float(iSample2 + 1) / fltNrSamples2 272 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 273 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 274 275 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV) 276 if intersectionPointData is None: 277 continue 278 279 # intersection point can't be an existing point 280 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 281 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 282 if (mathematics.IsSamePoint(P0, worldPoint1, limitDistance)) or \ 283 (mathematics.IsSamePoint(P1, worldPoint1, limitDistance)): 284 285 intersectionPoint1 = None 286 else: 287 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 288 intersectionSegment1Parameter, 289 worldPoint1) 290 291 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 292 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 293 if (mathematics.IsSamePoint(Q0, worldPoint2, limitDistance)) or \ 294 (mathematics.IsSamePoint(Q1, worldPoint2, limitDistance)): 295 296 intersectionPoint2 = None 297 else: 298 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 299 intersectionSegment2Parameter, 300 worldPoint2) 301 302 return [intersectionPoint1, intersectionPoint2] 303 304 return None 305 306 def CalcIntersections(self, nrSamples1, nrSamples2): 307 algorithm = bpy.context.scene.curvetools.IntersectCurvesAlgorithm 308 309 if algorithm == '3D': 310 return self.CalcIntersections3D(nrSamples1, nrSamples2) 311 312 if algorithm == 'From_View': 313 global algoDIR 314 if algoDIR is not None: 315 return self.CalcIntersectionsFromViewDIR(nrSamples1, nrSamples2) 316 317 global algoPOV 318 if algoPOV is not None: 319 return self.CalcIntersectionsFromViewPOV(nrSamples1, nrSamples2) 320 321 return [[], []] 322 323 def CalcIntersections3D(self, nrSamples1, nrSamples2): 324 rvIntersections1 = [] 325 rvIntersections2 = [] 326 327 fltNrSamples1 = float(nrSamples1) 328 fltNrSamples2 = float(nrSamples2) 329 330 limitDistance = bpy.context.scene.curvetools.LimitDistance 331 332 for iSample1 in range(nrSamples1): 333 segPar10 = float(iSample1) / fltNrSamples1 334 segPar11 = float(iSample1 + 1) / fltNrSamples1 335 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 336 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 337 338 for iSample2 in range(nrSamples2): 339 segPar20 = float(iSample2) / fltNrSamples2 340 segPar21 = float(iSample2 + 1) / fltNrSamples2 341 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 342 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 343 344 intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance) 345 if intersectionPointData is None: 346 continue 347 348 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 349 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 350 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 351 intersectionSegment1Parameter, 352 worldPoint1) 353 rvIntersections1.append(intersectionPoint1) 354 355 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 356 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 357 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 358 intersectionSegment2Parameter, 359 worldPoint2) 360 rvIntersections2.append(intersectionPoint2) 361 362 return [rvIntersections1, rvIntersections2] 363 364 def CalcIntersectionsFromViewDIR(self, nrSamples1, nrSamples2): 365 global algoDIR 366 367 rvIntersections1 = [] 368 rvIntersections2 = [] 369 370 fltNrSamples1 = float(nrSamples1) 371 fltNrSamples2 = float(nrSamples2) 372 373 for iSample1 in range(nrSamples1): 374 segPar10 = float(iSample1) / fltNrSamples1 375 segPar11 = float(iSample1 + 1) / fltNrSamples1 376 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 377 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 378 379 for iSample2 in range(nrSamples2): 380 segPar20 = float(iSample2) / fltNrSamples2 381 segPar21 = float(iSample2 + 1) / fltNrSamples2 382 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 383 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 384 385 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR) 386 if intersectionPointData is None: 387 continue 388 389 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 390 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 391 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 392 intersectionSegment1Parameter, 393 worldPoint1) 394 rvIntersections1.append(intersectionPoint1) 395 396 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 397 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 398 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 399 intersectionSegment2Parameter, 400 worldPoint2) 401 rvIntersections2.append(intersectionPoint2) 402 403 return [rvIntersections1, rvIntersections2] 404 405 def CalcIntersectionsFromViewPOV(self, nrSamples1, nrSamples2): 406 global algoPOV 407 408 rvIntersections1 = [] 409 rvIntersections2 = [] 410 411 fltNrSamples1 = float(nrSamples1) 412 fltNrSamples2 = float(nrSamples2) 413 414 for iSample1 in range(nrSamples1): 415 segPar10 = float(iSample1) / fltNrSamples1 416 segPar11 = float(iSample1 + 1) / fltNrSamples1 417 P0 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar10) 418 P1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=segPar11) 419 420 for iSample2 in range(nrSamples2): 421 segPar20 = float(iSample2) / fltNrSamples2 422 segPar21 = float(iSample2 + 1) / fltNrSamples2 423 Q0 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar20) 424 Q1 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=segPar21) 425 426 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV) 427 if intersectionPointData is None: 428 continue 429 430 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1) 431 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter) 432 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1, 433 intersectionSegment1Parameter, 434 worldPoint1) 435 rvIntersections1.append(intersectionPoint1) 436 437 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2) 438 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter) 439 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2, 440 intersectionSegment2Parameter, 441 worldPoint2) 442 rvIntersections2.append(intersectionPoint2) 443 444 return [rvIntersections1, rvIntersections2] 445 446 447class BezierSplineIntersectionPoint: 448 def __init__(self, spline, bezierSegmentIntersectionPoint): 449 self.spline = spline 450 self.bezierSegmentIntersectionPoint = bezierSegmentIntersectionPoint 451 452 453class BezierSplinesIntersector: 454 def __init__(self, spline1, spline2, worldMatrix1, worldMatrix2): 455 self.spline1 = spline1 456 self.spline2 = spline2 457 self.worldMatrix1 = worldMatrix1 458 self.worldMatrix2 = worldMatrix2 459 460 def CalcIntersections(self): 461 rvIntersections1 = [] 462 rvIntersections2 = [] 463 464 try: 465 nrSamplesPerSegment1 = int(self.spline1.resolution / self.spline1.nrSegments) 466 except: 467 nrSamplesPerSegment1 = 2 468 if nrSamplesPerSegment1 < 2: 469 nrSamplesPerSegment1 = 2 470 471 try: 472 nrSamplesPerSegment2 = int(self.spline2.resolution / self.spline2.nrSegments) 473 except: 474 nrSamplesPerSegment2 = 2 475 if nrSamplesPerSegment2 < 2: 476 nrSamplesPerSegment2 = 2 477 478 for segment1 in self.spline1.segments: 479 for segment2 in self.spline2.segments: 480 segmentsIntersector = BezierSegmentsIntersector(segment1, segment2, 481 self.worldMatrix1, self.worldMatrix2) 482 segmentIntersections = segmentsIntersector.CalcIntersections(nrSamplesPerSegment1, nrSamplesPerSegment2) 483 if segmentIntersections is None: 484 continue 485 486 segment1Intersections = segmentIntersections[0] 487 for segmentIntersection in segment1Intersections: 488 splineIntersection = BezierSplineIntersectionPoint(self.spline1, segmentIntersection) 489 rvIntersections1.append(splineIntersection) 490 491 segment2Intersections = segmentIntersections[1] 492 for segmentIntersection in segment2Intersections: 493 splineIntersection = BezierSplineIntersectionPoint(self.spline2, segmentIntersection) 494 rvIntersections2.append(splineIntersection) 495 496 return [rvIntersections1, rvIntersections2] 497 498 499class CurvesIntersector: 500 @staticmethod 501 def FromSelection(): 502 selObjects = bpy.context.selected_objects 503 if len(selObjects) != 2: 504 raise Exception("len(selObjects) != 2") # shouldn't be possible 505 506 blenderActiveCurve = bpy.context.active_object 507 blenderOtherCurve = selObjects[0] 508 if blenderActiveCurve == blenderOtherCurve: 509 blenderOtherCurve = selObjects[1] 510 511 aCurve = curves.Curve(blenderActiveCurve) 512 oCurve = curves.Curve(blenderOtherCurve) 513 514 return CurvesIntersector(aCurve, oCurve) 515 516 @staticmethod 517 def ResetGlobals(): 518 global algoPOV 519 algoPOV = None 520 global algoDIR 521 algoDIR = None 522 523 @staticmethod 524 def InitGlobals(): 525 CurvesIntersector.ResetGlobals() 526 global algoPOV 527 global algoDIR 528 529 algo = bpy.context.scene.curvetools.IntersectCurvesAlgorithm 530 if algo == 'From_View': 531 regionView3D = util.GetFirstRegionView3D() 532 if regionView3D is None: 533 print("### ERROR: regionView3D is None. Stopping.") 534 return 535 536 viewPerspective = regionView3D.view_perspective 537 print("--", "viewPerspective:", viewPerspective) 538 539 if viewPerspective == 'ORTHO': 540 viewMatrix = regionView3D.view_matrix 541 print("--", "viewMatrix:") 542 print(viewMatrix) 543 544 algoDIR = Vector((viewMatrix[2][0], viewMatrix[2][1], viewMatrix[2][2])) 545 print("--", "algoDIR:", algoDIR) 546 547 # ## TODO: doesn't work properly 548 if viewPerspective == 'PERSP': 549 viewMatrix = regionView3D.view_matrix 550 print("--", "viewMatrix:") 551 print(viewMatrix) 552 553 algoPOV = regionView3D.view_location.copy() 554 print("--", "algoPOV:", algoPOV) 555 556 otherPOV = Vector((viewMatrix[0][3], viewMatrix[1][3], viewMatrix[2][3])) 557 print("--", "otherPOV:", otherPOV) 558 559 localPOV = Vector((0, 0, 0)) 560 globalPOV = viewMatrix * localPOV 561 print("--", "globalPOV:", globalPOV) 562 563 perspMatrix = regionView3D.perspective_matrix 564 print("--", "perspMatrix:") 565 print(perspMatrix) 566 567 globalPOVPersp = perspMatrix * localPOV 568 print("--", "globalPOVPersp:", globalPOVPersp) 569 570 if viewPerspective == 'CAMERA': 571 camera = bpy.context.scene.camera 572 if camera is None: 573 print("### ERROR: camera is None. Stopping.") 574 return 575 576 print("--", "camera:", camera) 577 cameraData = camera.data 578 print("--", "cameraData.type:", cameraData.type) 579 580 cameraMatrix = camera.matrix_world 581 print("--", "cameraMatrix:") 582 print(cameraMatrix) 583 584 if cameraData.type == 'ORTHO': 585 cameraMatrix = camera.matrix_world 586 # algoDIR = Vector((cameraMatrix[2][0], cameraMatrix[2][1], cameraMatrix[2][2])) 587 algoDIR = Vector((- cameraMatrix[0][2], - cameraMatrix[1][2], - cameraMatrix[2][2])) 588 print("--", "algoDIR:", algoDIR) 589 590 if cameraData.type == 'PERSP': 591 algoPOV = camera.location.copy() 592 print("--", "algoPOV:", algoPOV) 593 594 def __init__(self, activeCurve, otherCurve): 595 self.activeCurve = activeCurve 596 self.otherCurve = otherCurve 597 598 CurvesIntersector.InitGlobals() 599 600 def CalcIntersections(self): 601 rvIntersections1 = [] 602 rvIntersections2 = [] 603 604 worldMatrix1 = self.activeCurve.curve.matrix_world 605 worldMatrix2 = self.otherCurve.curve.matrix_world 606 607 for spline1 in self.activeCurve.splines: 608 for spline2 in self.otherCurve.splines: 609 splineIntersector = BezierSplinesIntersector(spline1, spline2, worldMatrix1, worldMatrix2) 610 splineIntersections = splineIntersector.CalcIntersections() 611 if splineIntersections is None: 612 continue 613 614 spline1Intersections = splineIntersections[0] 615 for splineIntersection in spline1Intersections: 616 rvIntersections1.append(splineIntersection) 617 618 spline2Intersections = splineIntersections[1] 619 for splineIntersection in spline2Intersections: 620 rvIntersections2.append(splineIntersection) 621 622 return [rvIntersections1, rvIntersections2] 623 624 def CalcAndApplyIntersections(self): 625 mode = bpy.context.scene.curvetools.IntersectCurvesMode 626 627 if mode == 'Empty': 628 return self.CalcAndApplyEmptyAtIntersections() 629 if mode == 'Insert': 630 return self.CalcAndApplyInsertAtIntersections() 631 if mode == 'Split': 632 return self.CalcAndApplySplitAtIntersections() 633 634 return [0, 0] 635 636 def CalcAndApplyEmptyAtIntersections(self): 637 intersections = self.CalcIntersections() 638 intersectionsActive = intersections[0] 639 intersectionsOther = intersections[1] 640 641 nrActive = 0 642 nrOther = 0 643 644 affect = bpy.context.scene.curvetools.IntersectCurvesAffect 645 646 if (affect == 'Both') or (affect == 'Active'): 647 for splineIntersection in intersectionsActive: 648 iPoint = splineIntersection.bezierSegmentIntersectionPoint.intersectionPoint 649 bpy.ops.object.empty_add(type='PLAIN_AXES', 650 align='WORLD', 651 location=(iPoint.x, iPoint.y, iPoint.z), rotation=(0, 0, 0)) 652 nrActive += 1 653 654 if (affect == 'Both') or (affect == 'Other'): 655 for splineIntersection in intersectionsOther: 656 iPoint = splineIntersection.bezierSegmentIntersectionPoint.intersectionPoint 657 bpy.ops.object.empty_add(type='PLAIN_AXES', 658 align='WORLD', 659 location=(iPoint.x, iPoint.y, iPoint.z), rotation=(0, 0, 0)) 660 nrOther += 1 661 662 return [nrActive, nrOther] 663 664 def CalcAndApplyInsertAtIntersections(self): 665 nrActive = 0 666 nrOther = 0 667 668 affect = bpy.context.scene.curvetools.IntersectCurvesAffect 669 affectA = (affect == 'Both') or (affect == 'Active') 670 affectO = (affect == 'Both') or (affect == 'Other') 671 672 for iSplineA in range(len(self.activeCurve.splines)): 673 splineA = self.activeCurve.splines[iSplineA] 674 nrSegmentsA = len(splineA.segments) 675 resPerSegA = splineA.resolutionPerSegment 676 677 for iSplineO in range(len(self.otherCurve.splines)): 678 splineO = self.otherCurve.splines[iSplineO] 679 nrSegmentsO = len(splineO.segments) 680 resPerSegO = splineO.resolutionPerSegment 681 682 iSegA = 0 683 while True: 684 segA = splineA.segments[iSegA] 685 686 iSegO = 0 687 while True: 688 segO = splineO.segments[iSegO] 689 690 segIntersector = BezierSegmentsIntersector(segA, segO, 691 self.activeCurve.worldMatrix, 692 self.otherCurve.worldMatrix) 693 segFirstIntersection = segIntersector.CalcFirstIntersection(resPerSegA, resPerSegO) 694 695 if segFirstIntersection is not None: 696 intPointA = segFirstIntersection[0] 697 intPointO = segFirstIntersection[1] 698 # else does something weird if 1 of them is None.. 699 if (intPointA is not None) and (intPointO is not None): 700 if affectA: 701 if intPointA is not None: 702 splineA.InsertPoint(segA, intPointA.parameter) 703 704 nrActive += 1 705 nrSegmentsA += 1 706 707 if affectO: 708 if intPointO is not None: 709 splineO.InsertPoint(segO, intPointO.parameter) 710 711 nrOther += 1 712 nrSegmentsO += 1 713 714 iSegO += 1 715 if not (iSegO < nrSegmentsO): 716 break 717 718 iSegA += 1 719 if not (iSegA < nrSegmentsA): 720 break 721 722 if affectO: 723 splineO.RefreshInScene() 724 725 if affectA: 726 splineA.RefreshInScene() 727 728 return [nrActive, nrOther] 729 730 def CalcAndApplySplitAtIntersections(self): 731 nrActive = 0 732 nrOther = 0 733 734 affect = bpy.context.scene.curvetools.IntersectCurvesAffect 735 affectA = (affect == 'Both') or (affect == 'Active') 736 affectO = (affect == 'Both') or (affect == 'Other') 737 738 nrSplinesA = len(self.activeCurve.splines) 739 nrSplinesO = len(self.otherCurve.splines) 740 741 iSplineA = 0 742 while True: 743 splineA = self.activeCurve.splines[iSplineA] 744 nrSegmentsA = len(splineA.segments) 745 resPerSegA = splineA.resolutionPerSegment 746 747 iSplineO = 0 748 while True: 749 splineO = self.otherCurve.splines[iSplineO] 750 nrSegmentsO = len(splineO.segments) 751 resPerSegO = splineO.resolutionPerSegment 752 753 iSegA = 0 754 while True: 755 segA = splineA.segments[iSegA] 756 757 iSegO = 0 758 while True: 759 segO = splineO.segments[iSegO] 760 761 segIntersector = BezierSegmentsIntersector(segA, segO, 762 self.activeCurve.worldMatrix, 763 self.otherCurve.worldMatrix) 764 segFirstIntersection = segIntersector.CalcFirstIntersection(resPerSegA, resPerSegO) 765 766 if segFirstIntersection is not None: 767 intPointA = segFirstIntersection[0] 768 intPointO = segFirstIntersection[1] 769 # else does something weird if 1 of them is None.. 770 if (intPointA is not None) and (intPointO is not None): 771 if affectA: 772 if intPointA is not None: 773 print("--", "splineA.Split():") 774 newSplinesA = splineA.Split(segA, intPointA.parameter) 775 if newSplinesA is not None: 776 newResolutions = splineA.CalcDivideResolution(segA, intPointA.parameter) 777 newSplinesA[0].resolution = newResolutions[0] 778 newSplinesA[1].resolution = newResolutions[1] 779 780 splineA = newSplinesA[0] 781 self.activeCurve.splines[iSplineA] = splineA 782 self.activeCurve.splines.insert(iSplineA + 1, newSplinesA[1]) 783 784 nrActive += 1 785 786 if affectO: 787 if intPointO is not None: 788 print("--", "splineO.Split():") 789 newSplinesO = splineO.Split(segO, intPointO.parameter) 790 if newSplinesO is not None: 791 newResolutions = splineO.CalcDivideResolution(segO, intPointO.parameter) 792 newSplinesO[0].resolution = newResolutions[0] 793 newSplinesO[1].resolution = newResolutions[1] 794 795 splineO = newSplinesO[0] 796 self.otherCurve.splines[iSplineO] = splineO 797 self.otherCurve.splines.insert(iSplineO + 1, newSplinesO[1]) 798 799 nrOther += 1 800 801 nrSegmentsO = len(splineO.segments) 802 iSegO += 1 803 if not (iSegO < nrSegmentsO): 804 break 805 806 nrSegmentsA = len(splineA.segments) 807 iSegA += 1 808 if not (iSegA < nrSegmentsA): 809 break 810 811 nrSplinesO = len(self.otherCurve.splines) 812 iSplineO += 1 813 if not (iSplineO < nrSplinesO): 814 break 815 816 nrSplinesA = len(self.activeCurve.splines) 817 iSplineA += 1 818 if not (iSplineA < nrSplinesA): 819 break 820 821 if affectA: 822 print("") 823 print("--", "self.activeCurve.RebuildInScene():") 824 self.activeCurve.RebuildInScene() 825 if affectO: 826 print("") 827 print("--", "self.otherCurve.RebuildInScene():") 828 self.otherCurve.RebuildInScene() 829 830 return [nrActive, nrOther] 831