Merge branch 'blender-v2.92-release'
[blender-addons.git] / curve_tools / intersections.py
blobf0b8e96f1ca431aed1577bd1a250a0872595cadb
1 import bpy
2 from . import mathematics
3 from . import curves
4 from . import util
6 from mathutils import Vector
8 algoPOV = None
9 algoDIR = None
12 class BezierSegmentIntersectionPoint:
13 def __init__(self, segment, parameter, intersectionPoint):
14 self.segment = segment
15 self.parameter = parameter
16 self.intersectionPoint = intersectionPoint
19 class 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
26 def CalcFirstIntersection(self, nrSamples1, nrSamples2):
27 algorithm = bpy.context.scene.curvetools.IntersectCurvesAlgorithm
29 if algorithm == '3D':
30 return self.CalcFirstRealIntersection3D(nrSamples1, nrSamples2)
32 if algorithm == 'From_View':
33 global algoDIR
34 if algoDIR is not None:
35 return self.CalcFirstRealIntersectionFromViewDIR(nrSamples1, nrSamples2)
37 global algoPOV
38 if algoPOV is not None:
39 return self.CalcFirstRealIntersectionFromViewPOV(nrSamples1, nrSamples2)
41 return None
43 def CalcFirstIntersection3D(self, nrSamples1, nrSamples2):
44 fltNrSamples1 = float(nrSamples1)
45 fltNrSamples2 = float(nrSamples2)
47 limitDistance = bpy.context.scene.curvetools.LimitDistance
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)
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)
61 intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
62 if intersectionPointData is None:
63 continue
65 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1)
66 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1,
67 intersectionSegment1Parameter,
68 intersectionPointData[2])
70 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2)
71 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2,
72 intersectionSegment2Parameter,
73 intersectionPointData[3])
75 return [intersectionPoint1, intersectionPoint2]
77 return None
79 def CalcFirstRealIntersection3D(self, nrSamples1, nrSamples2):
80 fltNrSamples1 = float(nrSamples1)
81 fltNrSamples2 = float(nrSamples2)
83 limitDistance = bpy.context.scene.curvetools.LimitDistance
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)
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)
97 intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
98 if intersectionPointData is None:
99 continue
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)):
107 intersectionPoint1 = None
108 else:
109 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1,
110 intersectionSegment1Parameter,
111 worldPoint1)
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)):
118 intersectionPoint2 = None
119 else:
120 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2,
121 intersectionSegment2Parameter,
122 worldPoint2)
124 return [intersectionPoint1, intersectionPoint2]
126 return None
128 def CalcFirstIntersectionFromViewDIR(self, nrSamples1, nrSamples2):
129 global algoDIR
131 fltNrSamples1 = float(nrSamples1)
132 fltNrSamples2 = float(nrSamples2)
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)
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)
146 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
147 if intersectionPointData is None:
148 continue
150 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1)
151 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter)
152 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1,
153 intersectionSegment1Parameter,
154 worldPoint1)
156 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2)
157 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter)
158 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2,
159 intersectionSegment2Parameter,
160 worldPoint2)
162 return [intersectionPoint1, intersectionPoint2]
164 return None
166 def CalcFirstRealIntersectionFromViewDIR(self, nrSamples1, nrSamples2):
167 global algoDIR
169 fltNrSamples1 = float(nrSamples1)
170 fltNrSamples2 = float(nrSamples2)
172 limitDistance = bpy.context.scene.curvetools.LimitDistance
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)
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)
186 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
187 if intersectionPointData is None:
188 continue
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)):
196 intersectionPoint1 = None
197 else:
198 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1,
199 intersectionSegment1Parameter,
200 worldPoint1)
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)):
207 intersectionPoint2 = None
208 else:
209 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2,
210 intersectionSegment2Parameter,
211 worldPoint2)
213 return [intersectionPoint1, intersectionPoint2]
215 return None
217 def CalcFirstIntersectionFromViewPOV(self, nrSamples1, nrSamples2):
218 global algoPOV
220 fltNrSamples1 = float(nrSamples1)
221 fltNrSamples2 = float(nrSamples2)
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)
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)
235 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
236 if intersectionPointData is None:
237 continue
239 intersectionSegment1Parameter = segPar10 + (intersectionPointData[0] / fltNrSamples1)
240 worldPoint1 = self.worldMatrix1 @ self.segment1.CalcPoint(parameter=intersectionSegment1Parameter)
241 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1,
242 intersectionSegment1Parameter,
243 worldPoint1)
245 intersectionSegment2Parameter = segPar20 + (intersectionPointData[1] / fltNrSamples2)
246 worldPoint2 = self.worldMatrix2 @ self.segment2.CalcPoint(parameter=intersectionSegment2Parameter)
247 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2,
248 intersectionSegment2Parameter,
249 worldPoint2)
251 return [intersectionPoint1, intersectionPoint2]
253 return None
255 def CalcFirstRealIntersectionFromViewPOV(self, nrSamples1, nrSamples2):
256 global algoPOV
258 fltNrSamples1 = float(nrSamples1)
259 fltNrSamples2 = float(nrSamples2)
261 limitDistance = bpy.context.scene.curvetools.LimitDistance
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)
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)
275 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
276 if intersectionPointData is None:
277 continue
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)):
285 intersectionPoint1 = None
286 else:
287 intersectionPoint1 = BezierSegmentIntersectionPoint(self.segment1,
288 intersectionSegment1Parameter,
289 worldPoint1)
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)):
296 intersectionPoint2 = None
297 else:
298 intersectionPoint2 = BezierSegmentIntersectionPoint(self.segment2,
299 intersectionSegment2Parameter,
300 worldPoint2)
302 return [intersectionPoint1, intersectionPoint2]
304 return None
306 def CalcIntersections(self, nrSamples1, nrSamples2):
307 algorithm = bpy.context.scene.curvetools.IntersectCurvesAlgorithm
309 if algorithm == '3D':
310 return self.CalcIntersections3D(nrSamples1, nrSamples2)
312 if algorithm == 'From_View':
313 global algoDIR
314 if algoDIR is not None:
315 return self.CalcIntersectionsFromViewDIR(nrSamples1, nrSamples2)
317 global algoPOV
318 if algoPOV is not None:
319 return self.CalcIntersectionsFromViewPOV(nrSamples1, nrSamples2)
321 return [[], []]
323 def CalcIntersections3D(self, nrSamples1, nrSamples2):
324 rvIntersections1 = []
325 rvIntersections2 = []
327 fltNrSamples1 = float(nrSamples1)
328 fltNrSamples2 = float(nrSamples2)
330 limitDistance = bpy.context.scene.curvetools.LimitDistance
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)
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)
344 intersectionPointData = mathematics.CalcIntersectionPointLineSegments(P0, P1, Q0, Q1, limitDistance)
345 if intersectionPointData is None:
346 continue
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)
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)
362 return [rvIntersections1, rvIntersections2]
364 def CalcIntersectionsFromViewDIR(self, nrSamples1, nrSamples2):
365 global algoDIR
367 rvIntersections1 = []
368 rvIntersections2 = []
370 fltNrSamples1 = float(nrSamples1)
371 fltNrSamples2 = float(nrSamples2)
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)
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)
385 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsDIR(P0, P1, Q0, Q1, algoDIR)
386 if intersectionPointData is None:
387 continue
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)
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)
403 return [rvIntersections1, rvIntersections2]
405 def CalcIntersectionsFromViewPOV(self, nrSamples1, nrSamples2):
406 global algoPOV
408 rvIntersections1 = []
409 rvIntersections2 = []
411 fltNrSamples1 = float(nrSamples1)
412 fltNrSamples2 = float(nrSamples2)
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)
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)
426 intersectionPointData = mathematics.CalcIntersectionPointsLineSegmentsPOV(P0, P1, Q0, Q1, algoPOV)
427 if intersectionPointData is None:
428 continue
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)
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)
444 return [rvIntersections1, rvIntersections2]
447 class BezierSplineIntersectionPoint:
448 def __init__(self, spline, bezierSegmentIntersectionPoint):
449 self.spline = spline
450 self.bezierSegmentIntersectionPoint = bezierSegmentIntersectionPoint
453 class 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
460 def CalcIntersections(self):
461 rvIntersections1 = []
462 rvIntersections2 = []
464 try:
465 nrSamplesPerSegment1 = int(self.spline1.resolution / self.spline1.nrSegments)
466 except:
467 nrSamplesPerSegment1 = 2
468 if nrSamplesPerSegment1 < 2:
469 nrSamplesPerSegment1 = 2
471 try:
472 nrSamplesPerSegment2 = int(self.spline2.resolution / self.spline2.nrSegments)
473 except:
474 nrSamplesPerSegment2 = 2
475 if nrSamplesPerSegment2 < 2:
476 nrSamplesPerSegment2 = 2
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
486 segment1Intersections = segmentIntersections[0]
487 for segmentIntersection in segment1Intersections:
488 splineIntersection = BezierSplineIntersectionPoint(self.spline1, segmentIntersection)
489 rvIntersections1.append(splineIntersection)
491 segment2Intersections = segmentIntersections[1]
492 for segmentIntersection in segment2Intersections:
493 splineIntersection = BezierSplineIntersectionPoint(self.spline2, segmentIntersection)
494 rvIntersections2.append(splineIntersection)
496 return [rvIntersections1, rvIntersections2]
499 class 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
506 blenderActiveCurve = bpy.context.active_object
507 blenderOtherCurve = selObjects[0]
508 if blenderActiveCurve == blenderOtherCurve:
509 blenderOtherCurve = selObjects[1]
511 aCurve = curves.Curve(blenderActiveCurve)
512 oCurve = curves.Curve(blenderOtherCurve)
514 return CurvesIntersector(aCurve, oCurve)
516 @staticmethod
517 def ResetGlobals():
518 global algoPOV
519 algoPOV = None
520 global algoDIR
521 algoDIR = None
523 @staticmethod
524 def InitGlobals():
525 CurvesIntersector.ResetGlobals()
526 global algoPOV
527 global algoDIR
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
536 viewPerspective = regionView3D.view_perspective
537 print("--", "viewPerspective:", viewPerspective)
539 if viewPerspective == 'ORTHO':
540 viewMatrix = regionView3D.view_matrix
541 print("--", "viewMatrix:")
542 print(viewMatrix)
544 algoDIR = Vector((viewMatrix[2][0], viewMatrix[2][1], viewMatrix[2][2]))
545 print("--", "algoDIR:", algoDIR)
547 # ## TODO: doesn't work properly
548 if viewPerspective == 'PERSP':
549 viewMatrix = regionView3D.view_matrix
550 print("--", "viewMatrix:")
551 print(viewMatrix)
553 algoPOV = regionView3D.view_location.copy()
554 print("--", "algoPOV:", algoPOV)
556 otherPOV = Vector((viewMatrix[0][3], viewMatrix[1][3], viewMatrix[2][3]))
557 print("--", "otherPOV:", otherPOV)
559 localPOV = Vector((0, 0, 0))
560 globalPOV = viewMatrix * localPOV
561 print("--", "globalPOV:", globalPOV)
563 perspMatrix = regionView3D.perspective_matrix
564 print("--", "perspMatrix:")
565 print(perspMatrix)
567 globalPOVPersp = perspMatrix * localPOV
568 print("--", "globalPOVPersp:", globalPOVPersp)
570 if viewPerspective == 'CAMERA':
571 camera = bpy.context.scene.camera
572 if camera is None:
573 print("### ERROR: camera is None. Stopping.")
574 return
576 print("--", "camera:", camera)
577 cameraData = camera.data
578 print("--", "cameraData.type:", cameraData.type)
580 cameraMatrix = camera.matrix_world
581 print("--", "cameraMatrix:")
582 print(cameraMatrix)
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)
590 if cameraData.type == 'PERSP':
591 algoPOV = camera.location.copy()
592 print("--", "algoPOV:", algoPOV)
594 def __init__(self, activeCurve, otherCurve):
595 self.activeCurve = activeCurve
596 self.otherCurve = otherCurve
598 CurvesIntersector.InitGlobals()
600 def CalcIntersections(self):
601 rvIntersections1 = []
602 rvIntersections2 = []
604 worldMatrix1 = self.activeCurve.curve.matrix_world
605 worldMatrix2 = self.otherCurve.curve.matrix_world
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
614 spline1Intersections = splineIntersections[0]
615 for splineIntersection in spline1Intersections:
616 rvIntersections1.append(splineIntersection)
618 spline2Intersections = splineIntersections[1]
619 for splineIntersection in spline2Intersections:
620 rvIntersections2.append(splineIntersection)
622 return [rvIntersections1, rvIntersections2]
624 def CalcAndApplyIntersections(self):
625 mode = bpy.context.scene.curvetools.IntersectCurvesMode
627 if mode == 'Empty':
628 return self.CalcAndApplyEmptyAtIntersections()
629 if mode == 'Insert':
630 return self.CalcAndApplyInsertAtIntersections()
631 if mode == 'Split':
632 return self.CalcAndApplySplitAtIntersections()
634 return [0, 0]
636 def CalcAndApplyEmptyAtIntersections(self):
637 intersections = self.CalcIntersections()
638 intersectionsActive = intersections[0]
639 intersectionsOther = intersections[1]
641 nrActive = 0
642 nrOther = 0
644 affect = bpy.context.scene.curvetools.IntersectCurvesAffect
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
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
662 return [nrActive, nrOther]
664 def CalcAndApplyInsertAtIntersections(self):
665 nrActive = 0
666 nrOther = 0
668 affect = bpy.context.scene.curvetools.IntersectCurvesAffect
669 affectA = (affect == 'Both') or (affect == 'Active')
670 affectO = (affect == 'Both') or (affect == 'Other')
672 for iSplineA in range(len(self.activeCurve.splines)):
673 splineA = self.activeCurve.splines[iSplineA]
674 nrSegmentsA = len(splineA.segments)
675 resPerSegA = splineA.resolutionPerSegment
677 for iSplineO in range(len(self.otherCurve.splines)):
678 splineO = self.otherCurve.splines[iSplineO]
679 nrSegmentsO = len(splineO.segments)
680 resPerSegO = splineO.resolutionPerSegment
682 iSegA = 0
683 while True:
684 segA = splineA.segments[iSegA]
686 iSegO = 0
687 while True:
688 segO = splineO.segments[iSegO]
690 segIntersector = BezierSegmentsIntersector(segA, segO,
691 self.activeCurve.worldMatrix,
692 self.otherCurve.worldMatrix)
693 segFirstIntersection = segIntersector.CalcFirstIntersection(resPerSegA, resPerSegO)
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)
704 nrActive += 1
705 nrSegmentsA += 1
707 if affectO:
708 if intPointO is not None:
709 splineO.InsertPoint(segO, intPointO.parameter)
711 nrOther += 1
712 nrSegmentsO += 1
714 iSegO += 1
715 if not (iSegO < nrSegmentsO):
716 break
718 iSegA += 1
719 if not (iSegA < nrSegmentsA):
720 break
722 if affectO:
723 splineO.RefreshInScene()
725 if affectA:
726 splineA.RefreshInScene()
728 return [nrActive, nrOther]
730 def CalcAndApplySplitAtIntersections(self):
731 nrActive = 0
732 nrOther = 0
734 affect = bpy.context.scene.curvetools.IntersectCurvesAffect
735 affectA = (affect == 'Both') or (affect == 'Active')
736 affectO = (affect == 'Both') or (affect == 'Other')
738 nrSplinesA = len(self.activeCurve.splines)
739 nrSplinesO = len(self.otherCurve.splines)
741 iSplineA = 0
742 while True:
743 splineA = self.activeCurve.splines[iSplineA]
744 nrSegmentsA = len(splineA.segments)
745 resPerSegA = splineA.resolutionPerSegment
747 iSplineO = 0
748 while True:
749 splineO = self.otherCurve.splines[iSplineO]
750 nrSegmentsO = len(splineO.segments)
751 resPerSegO = splineO.resolutionPerSegment
753 iSegA = 0
754 while True:
755 segA = splineA.segments[iSegA]
757 iSegO = 0
758 while True:
759 segO = splineO.segments[iSegO]
761 segIntersector = BezierSegmentsIntersector(segA, segO,
762 self.activeCurve.worldMatrix,
763 self.otherCurve.worldMatrix)
764 segFirstIntersection = segIntersector.CalcFirstIntersection(resPerSegA, resPerSegO)
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]
780 splineA = newSplinesA[0]
781 self.activeCurve.splines[iSplineA] = splineA
782 self.activeCurve.splines.insert(iSplineA + 1, newSplinesA[1])
784 nrActive += 1
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]
795 splineO = newSplinesO[0]
796 self.otherCurve.splines[iSplineO] = splineO
797 self.otherCurve.splines.insert(iSplineO + 1, newSplinesO[1])
799 nrOther += 1
801 nrSegmentsO = len(splineO.segments)
802 iSegO += 1
803 if not (iSegO < nrSegmentsO):
804 break
806 nrSegmentsA = len(splineA.segments)
807 iSegA += 1
808 if not (iSegA < nrSegmentsA):
809 break
811 nrSplinesO = len(self.otherCurve.splines)
812 iSplineO += 1
813 if not (iSplineO < nrSplinesO):
814 break
816 nrSplinesA = len(self.activeCurve.splines)
817 iSplineA += 1
818 if not (iSplineA < nrSplinesA):
819 break
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()
830 return [nrActive, nrOther]