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