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