]> Shamusworld >> Repos - architektonas/blob - src/line.cpp
Initial bring-back of line to line intersection detection.
[architektonas] / src / line.cpp
1 // line.cpp: Line object
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/22/2011  Created this file
12 // JLH  04/11/2011  Fixed attached dimensions to stay at correct length when
13 //                  "Fixed Length" button is down
14 // JLH  04/27/2011  Fixed attached dimension to stay a correct length when
15 //                  "Fixed Length" button is *not* down ;-)
16 // JLH  05/29/2011  Added (some) mouseover hints
17 //
18
19 #include "line.h"
20
21 #include <QtGui>
22 #include "container.h"
23 #include "dimension.h"
24 #include "geometry.h"
25 #include "mathconstants.h"
26 #include "painter.h"
27
28
29 Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p),
30         /*type(OTLine),*/ endpoint(p2),
31         draggingLine(false), draggingHandle1(false), draggingHandle2(false), //needUpdate(false),
32         length(Vector::Magnitude(p2, p1)), angle(Vector(endpoint - position).Unit()),
33         hitPoint1(false), hitPoint2(false), hitLine(false)
34 {
35         type = OTLine;
36 }
37
38
39 Line::~Line()
40 {
41 // Taking care of connections should be done by the Container, as we don't know
42 // anything about any other object connected to this one.
43 #if 0
44         // If there are any attached Dimensions, we must set the attachment points
45         // to NULL since they will no longer be valid.
46         if (attachedDimension)
47         {
48                 attachedDimension->SetPoint1(NULL);
49                 attachedDimension->SetPoint2(NULL);
50         }
51         // IT WOULD BE NICE to have any object points attached to this line automagically
52         // connect to this dimension object at this point, instead of just becoming
53         // detached.
54 #endif
55 //actually not true, we know the object pointer and parameter!
56 //actuall, the Object base class does this for us...!
57 #if 0
58         std::vector<Connection>::iterator i;
59
60         for(i=connected.begin(); i!=connected.end(); i++)
61         {
62                 (*i).object->Disconnect(this, (*i).t);
63         }
64 #endif
65 }
66
67
68 /*virtual*/ void Line::Draw(Painter * painter)
69 {
70         painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
71
72         if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
73                 painter->DrawHandle(position);
74
75         if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
76                 painter->DrawHandle(endpoint);
77
78         if ((state == OSInactive) && !hitLine)
79                 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
80
81         if (Object::fixedLength && (draggingHandle1 || draggingHandle2))
82         {
83                 Vector point1 = (draggingHandle1 ? endpoint : position);
84                 Vector point2 = (draggingHandle1 ? position : endpoint);
85
86                 Vector current(point2 - point1);
87                 Vector v = current.Unit() * length;
88                 Vector v2 = point1 + v;
89                 painter->DrawLine(point1, v2);
90
91                 if (current.Magnitude() > length)
92                 {
93                         painter->SetPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
94                         painter->DrawLine(v2, point2);
95                 }
96         }
97         else
98                 painter->DrawLine(position, endpoint);
99
100         // If we're dragging an endpoint, draw an information panel showing both
101         // the length and angle being set.
102         if (draggingHandle1 || draggingHandle2)
103         {
104                 double absAngle = (Vector(endpoint - position).Angle()) * RADIANS_TO_DEGREES;
105                 double absLength = Vector(position - endpoint).Magnitude();
106
107                 QString text = QObject::tr("Length: %1 in.\n") + QChar(0x2221) + QObject::tr(": %2");
108                 text = text.arg(absLength).arg(absAngle);
109                 painter->DrawInformativeText(text);
110         }
111 }
112
113 /*virtual*/ Vector Line::Center(void)
114 {
115         // Technically, this is the midpoint but who are we to quibble? :-)
116         Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
117         return endpoint + v;
118 }
119
120 /*virtual*/ bool Line::Collided(Vector point)
121 {
122 /*
123 what we can do here is set ignoreClicks to true to keep other objects that are
124 selected from deselecting themselves. Will that fuck up something else? Not sure
125 yet... :-/
126 Actually, this is done here to keep tools from selecting stuff inadvertantly...
127 */
128         // We can assume this, since this is a mouse down event here.
129         objectWasDragged = false;
130         bool hit = HitTest(point);
131
132         // Someone told us to fuck off, so we'll fuck off. :-)
133         if (ignoreClicks)
134                 return hit;
135
136         // Now that we've done our hit testing on the non-snapped point, snap it if
137         // necessary...
138         if (snapToGrid)
139                 point = SnapPointToGrid(point);
140
141         if (snapPointIsValid)
142                 point = snapPoint;
143
144 // this is shite. this should be checked for in the Container, not here!
145 #warning "!!! This should be checked for in Container, not here !!!"
146         // If we're part of a non-top-level container, send this signal to it
147         if (parent->type == OTContainer && !((Container *)parent)->isTopLevelContainer
148                 && (hitLine || hitPoint1 || hitPoint2))
149         {
150                 parent->state = OSSelected;
151                 return true;
152         }
153
154         if (state == OSInactive)
155         {
156 //How to translate this into pixels from Document space???
157 //Maybe we need to pass a scaling factor in here from the caller? That would
158 //make sense, as the caller knows about the zoom factor and all that good kinda
159 //crap
160 //I think what's needed is an Object class variable/method that can be changed
161 //by the TLC and called in derived classes to properly scale the location to
162 //the current zoom level. That *should* work.
163
164 // ALSO: Need to code a global (read: Object class) variable that tells use
165 //       whether a modifier key was pressed in addition to the mouse click, so
166 //       we can do stuff like, say, hold down CTRL and be able to do multiple
167 //       selecting of objects (in that case, we would keep the Object state
168 //       from changing).
169                 if (hitPoint1)
170                 {
171                         oldState = state;
172                         state = OSSelected;
173                         oldPoint = position;
174                         draggingHandle1 = true;
175                         return true;
176                 }
177                 else if (hitPoint2)
178                 {
179                         oldState = state;
180                         state = OSSelected;
181                         oldPoint = endpoint;
182                         draggingHandle2 = true;
183                         return true;
184                 }
185                 else if (hitLine)
186                 {
187                         oldState = state;
188                         state = OSSelected;
189                         oldPoint = point;
190                         draggingLine = true;
191                         return true;
192                 }
193         }
194         else if (state == OSSelected)
195         {
196                 if (hitLine)
197                 {
198                         oldState = state;
199 //                      state = OSInactive;
200                         oldPoint = point;
201                         draggingLine = true;
202
203                         // Toggle selected state if CTRL held
204                         if (qApp->keyboardModifiers() == Qt::ControlModifier)
205                                 state = OSInactive;
206
207                         return true;
208                 }
209         }
210
211         // If CTRL is held, then we bypass the "turn off" code. Still didn't hit
212         // *this* object though. :-)
213         if (qApp->keyboardModifiers() == Qt::ControlModifier)
214                 return false;
215
216         // If we got here, we clicked on nothing, so set the object to inactive.
217         // (Once we can read key modifiers, we can override this to allow multiple selection.)
218         state = OSInactive;
219         return false;
220 }
221
222
223 /*virtual*/ bool Line::PointerMoved(Vector point)
224 {
225         if (selectionInProgress)
226         {
227                 // Check for whether or not the rect contains this line
228                 if (selection.contains(position.x, position.y)
229                         && selection.contains(endpoint.x, endpoint.y))
230                         state = OSSelected;
231                 else
232                         state = OSInactive;
233
234                 return false;
235         }
236
237         // Hit test tells us what we hit (if anything) through boolean variables.
238         SaveHitState();
239         bool hovered = HitTest(point);
240         needUpdate = HitStateChanged();
241
242         if (snapToGrid)
243                 point = SnapPointToGrid(point);
244
245         if (snapPointIsValid)
246                 point = snapPoint;
247
248         objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
249
250         if (objectWasDragged)
251         {
252                 Vector delta = point - oldPoint;
253
254                 if (draggingHandle1 || draggingLine)
255                         position += delta;
256
257                 if (draggingHandle2 || draggingLine)
258                         endpoint += delta;
259
260                 oldPoint = point;
261                 needUpdate = true;
262
263 //doesn't work          QMainWindow::statusBar()->setText("You are manipulating a line");
264
265                 // Tell connected objects to move themselves...
266                 if (draggingLine)
267                 {
268                         std::vector<Connection>::iterator i;
269
270                         for(i=connected.begin(); i!=connected.end(); i++)
271                         {
272                                 if ((*i).object->type == OTLine)
273                                         ((Line *)((*i).object))->MovePointAtParameter((*i).t, delta);
274                                 else if ((*i).object->type == OTDimension)
275                                         ((Dimension *)((*i).object))->MovePointAtParameter((*i).t, delta);
276                         }
277                 }
278         }
279
280 /*
281 We can't count on any coupling between the dimension object and us, so how do we do this???
282 Also, there may be more than one Dimension object connected to a single endpoint!
283
284 Ugly ways to do it:
285  - Keep track of the state of the connected dimension
286  - Pass the Dimension the point that's being changed and the delta
287
288 More elegant ways:
289  - Pass the point in a notification function (how?)
290  - Pass the point as a reference to the class instance object (&endpoint). This
291    way, the line doesn't have to care about keeping track of Dimensions
292    connected to it. But still have to care about other connected entities
293    (other Lines, Circles, Arcs, Splines, Texts, etc). I think I'd be OK with
294    this. Since the Dimension has a pointer to our object, all we have to do is
295    update our coordinates and the Dimension object will adjust itself on the
296    next repaint. Problem solved, and we don't have to know anything about how
297    many Dimensions are connected to us, or where! \o/
298    The question then becomes, how do we do this kind of coupling???
299
300 We need to know about connected entities so that we can have them either move
301 in expected ways or constrain the movement of this Line object. This is how we
302 will be a cut above all other CAD software currently out there: the GUI will
303 try to do the right thing, most of the time. :-)
304 */
305         if (needUpdate)
306         {
307 // should only do this if "Fixed Length" is set... !!! FIX !!! [DONE]
308                 Vector point1 = (draggingHandle1 ? endpoint : position);
309                 Vector point2 = (draggingHandle1 ? position : endpoint);
310
311                 if (Object::fixedAngle)
312                 {
313                         // Here we calculate the component of the current vector along the fixed angle.
314                         // A_compB = (A . Bu) * Bu
315                         double magnitudeAlongB = Vector::Dot(Vector(point2 - point1), angle);
316 /*
317 Actually, this isn't quite right. What we want to do is look for the intersection along either
318 the horizontal line or vertical line that intersects from the current mouse position.
319 */
320
321                         if (draggingHandle1)
322                                 position = endpoint + (angle * magnitudeAlongB);
323
324                         if (draggingHandle2)
325                                 endpoint = position + (angle * magnitudeAlongB);
326                 }
327 //              else
328 //                      v2 = point2;
329
330 //If we tell the dimension to flip sides, this is no longer a valid
331 //assumption. !!! FIX !!!
332 //Ideally, we should just send the point that's changing to the Dimension object
333 //and have it figure out which point needs to move... Or is it???
334 // Ideally, we shouldn't have to fuck around with this shit. We need to fix the rendering code
335 // so that we don't have to wait until the dragging is done to correct the position of the
336 // point in question, but we'd need another variable tho.
337 #if 0
338                 if (dimPoint1)
339                         dimPoint1->SetPoint1(draggingHandle1 ? v2 : position);
340                 
341                 if (dimPoint2)
342                         dimPoint2->SetPoint2(draggingHandle2 ? v2 : endpoint);
343 #endif
344         }
345
346         return hovered;
347 }
348
349
350 /*virtual*/ void Line::PointerReleased(void)
351 {
352         if (draggingHandle1 || draggingHandle2)
353         {
354                 // Set the length (in case the global state was set to fixed (or not))
355                 if (Object::fixedLength)
356                 {
357                         if (draggingHandle1)    // startpoint
358                         {
359                                 Vector v = Vector(position - endpoint).Unit() * length;
360                                 position = endpoint + v;
361                         }
362                         else                                    // endpoint
363                         {
364                                 Vector v = Vector(endpoint - position).Unit() * length;
365                                 endpoint = position + v;
366                         }
367                 }
368                 else
369                 {
370                         // Otherwise, we calculate the new length, just in case on the next
371                         // move it turns out to have a fixed length. :-)
372                         length = Vector(endpoint - position).Magnitude();
373                 }
374
375                 if (!Object::fixedAngle)
376                 {
377                         // Calculate the new angle, just in case on the next move it turns
378                         // out to be fixed. :-)
379                         angle = Vector(endpoint - position).Unit();
380                 }
381         }
382
383         draggingLine = false;
384         draggingHandle1 = false;
385         draggingHandle2 = false;
386
387         if (objectWasDragged)
388                 state = oldState;
389 }
390
391
392 /*virtual*/ bool Line::HitTest(Point point)
393 {
394         hitPoint1 = hitPoint2 = hitLine = false;
395         Vector lineSegment = endpoint - position;
396         Vector v1 = point - position;
397         Vector v2 = point - endpoint;
398         Vector v3 = point - Center();   // Midpoint, for snapping...
399         double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
400         double distance;
401
402         // Geometric interpretation of "distance = ?Det?(ls, v1) / |ls|":
403         // If the segment endpoints are s and e, and the point is p, then the test
404         // for the perpendicular intercepting the segment is equivalent to insisting
405         // that the two dot products {s-e}.{s-p} and {e-s}.{e-p} are both non-negative.
406         // Perpendicular distance from the point to the segment is computed by first
407         // computing the area of the triangle the three points form, then dividing by
408         // the length of the segment.  Distances are done just by the Pythagorean
409         // theorem. Twice the area of the triangle formed by three points is the
410         // determinant of the following matrix:
411         //
412         // sx sy 1       0  0  1       0  0  0
413         // ex ey 1  ==>  ex ey 1  ==>  ex ey 0
414         // px py 1       px py 1       px py 0
415         //
416         // By translating the start point to the origin, and subtracting row 1 from
417         // all other rows, we end up with the matrix on the right which greatly
418         // simplifies the calculation of the determinant.
419
420         if (t < 0.0)
421                 distance = v1.Magnitude();
422         else if (t > 1.0)
423                 distance = v2.Magnitude();
424         else
425                 // distance = ?Det?(ls, v1) / |ls|
426                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
427                         / lineSegment.Magnitude());
428
429         if ((v1.Magnitude() * Painter::zoom) < 8.0)
430         {
431                 hitPoint1 = true;
432                 snapPoint = position;
433                 snapPointIsValid = true;
434         }
435         else if ((v2.Magnitude() * Painter::zoom) < 8.0)
436         {
437                 hitPoint2 = true;
438                 snapPoint = endpoint;
439                 snapPointIsValid = true;
440         }
441         else if ((distance * Painter::zoom) < 5.0)
442                 hitLine = true;
443
444         // Not a manipulation point, but a snapping point:
445         if ((v3.Magnitude() * Painter::zoom) < 8.0)
446         {
447                 snapPoint = Center();
448                 snapPointIsValid = true;
449         }
450
451         return (hitPoint1 || hitPoint2 || hitLine ? true : false);
452 }
453
454
455 // Check to see if the point passed in coincides with any we have. If so, return a
456 // pointer to it; otherwise, return NULL.
457 /*virtual*/ Vector * Line::GetPointAt(Vector v)
458 {
459         if (v == position)
460                 return &position;
461         else if (v == endpoint)
462                 return &endpoint;
463
464         return 0;
465 }
466
467
468 /*virtual*/ void Line::Enumerate(FILE * file)
469 {
470         fprintf(file, "LINE %i (%lf,%lf) (%lf,%lf)\n", layer, position.x, position.y, endpoint.x, endpoint.y);
471 }
472
473
474 /*virtual*/ Object * Line::Copy(void)
475 {
476 #warning "!!! This doesn't take care of attached Dimensions !!!"
477 /*
478 This is a real problem. While having a pointer in the Dimension to this line's points
479 is fast & easy, it creates a huge problem when trying to replicate an object like this.
480
481 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
482 way, if you copy them, ... you might still have problems. Because you can't be sure if
483 a copy will be persistant or not, you then *definitely* do not want them to have the
484 same reference number.
485 */
486         return new Line(position, endpoint, parent);
487 }
488
489
490 /*virtual*/ Vector Line::GetPointAtParameter(double parameter)
491 {
492 // Is there any real reason to clamp this to the endpoints?
493 // (hey, whaddya know? this was masking a bug!)
494 #if 0
495         if (parameter <= 0)
496                 return position;
497         else if (parameter >= 1.0)
498                 return endpoint;
499 #endif
500
501         // The parameter is a percentage of the length of the vector, so all we
502         // have to do is scale the vector by it to find the point.
503         return position + (Vector(position, endpoint) * parameter);
504 }
505
506
507 /*virtual*/ void Line::MovePointAtParameter(double parameter, Vector v)
508 {
509         if (parameter == 0)
510                 position += v;
511         else if (parameter == 1.0)
512                 endpoint += v;
513         else
514                 {} // Not sure how to handle this case :-P
515 }
516
517
518 /*virtual*/ QRectF Line::Extents(void)
519 {
520         QRectF rect(QPointF(position.x, position.y), QPointF(endpoint.x, endpoint.y));
521         return rect.normalized();
522 }
523
524
525 /*virtual*/ void Line::Translate(Vector amount)
526 {
527         position += amount;
528         endpoint += amount;
529 }
530
531
532 /*virtual*/ void Line::Rotate(Point point, double angle)
533 {
534         Point l1 = Geometry::RotatePointAroundPoint(position, point, angle);
535         Point l2 = Geometry::RotatePointAroundPoint(endpoint, point, angle);
536         position = l1;
537         endpoint = l2;
538 }
539
540
541 /*virtual*/ void Line::Scale(Point point, double amount)
542 {
543 }
544
545
546 /*virtual*/ void Line::Mirror(Point p1, Point p2)
547 {
548         Point l1 = Geometry::MirrorPointAroundLine(position, p1, p2);
549         Point l2 = Geometry::MirrorPointAroundLine(endpoint, p1, p2);
550         position = l1;
551         endpoint = l2;
552 }
553
554
555 /*virtual*/ void Line::Save(void)
556 {
557         Object::Save();
558         oldEndpoint = endpoint;
559 }
560
561
562 /*virtual*/ void Line::Restore(void)
563 {
564         Object::Restore();
565         endpoint = oldEndpoint;
566 }
567
568
569 void Line::SetDimensionOnLine(Dimension * dimension/*= NULL*/)
570 {
571         // If they don't pass one in, create it for the caller.
572         // But ONLY if this line has a parent container!
573         // This is really bad to do here, it should be done in the parent container, always!
574 #warning "!!! Parent container should be creating Dimension object !!!"
575         if ((dimension == NULL) && (parent != NULL))
576         {
577 //printf("Line::SetDimensionOnLine(): Creating new dimension...\n");
578                 dimension = new Dimension(position, endpoint, DTLinear, parent);
579 //              dimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
580
581                 // THIS IS SERIOUS!!! WITHOUT A PARENT, THIS OBJECT IS IN LIMBO!!!
582 //              if (parent)
583 //{
584 //printf("Line::SetDimensionOnLine(): Adding to parent...\n");
585                 parent->Add(dimension);
586 //}
587         }
588
589         dimension->Connect(this, 0);
590         dimension->Connect(this, 1.0);
591
592         // Make sure the Dimension is connected to us...
593         Connect(dimension, 0);
594         Connect(dimension, 1.0);
595
596         dimension->position = position;
597         dimension->endpoint = endpoint;
598 }
599
600
601 Object * Line::FindAttachedDimension(void)
602 {
603         // Is there anything connected to this line? If not, return NULL
604         if (connected.size() < 2)
605                 return NULL;
606
607         // Otherwise, we have to search our objects to see if there's a likely
608         // candidate. In this case, we're looking for a pointer to the same object
609         // with a parameter of 0 and 1 respectively. This is O((n^2)/2).
610         for(uint i=0; i<connected.size(); i++)
611         {
612                 for(uint j=i+1; j<connected.size(); j++)
613                 {
614 //printf("Line: connected[i]=%X, connected[j]=%X, connected[i].t=%lf, connected[j].t=%lf\n", connected[i].object, connected[j].object, connected[i].t, connected[j].t);
615                         if ((connected[i].object == connected[j].object)
616                                 && ((connected[i].t == 0 && connected[j].t == 1.0)
617                                 || (connected[i].t == 1.0 && connected[j].t == 0)))
618                                 return connected[i].object;
619                 }
620         }
621
622         // Didn't find anything, so return NULL
623         return NULL;
624 }
625
626
627 void Line::SaveHitState(void)
628 {
629         oldHitPoint1 = hitPoint1;
630         oldHitPoint2 = hitPoint2;
631         oldHitLine = hitLine;
632 }
633
634
635 bool Line::HitStateChanged(void)
636 {
637         if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
638                 return true;
639
640         return false;
641 }
642
643
644 /*
645 Intersection of two lines:
646
647 Find where the lines with equations r = i + j + t (3i - j) and r = -i + s (j) intersect.
648
649 When they intersect, we can set the equations equal to one another:
650
651 i + j + t (3i - j) = -i + s (j)
652
653 Equating coefficients:
654 1 + 3t = -1 and 1 - t = s
655 So t = -2/3 and s = 5/3
656
657 The position vector of the intersection point is therefore given by putting t = -2/3 or s = 5/3 into one of the above equations. This gives -i +5j/3 .
658
659
660 so, let's say we have two lines, l1 and l2. Points are v0(p0x, p0y), v1(p1x, p1y) for l1
661 and v2(p2x, p2y), v3(p3x, p3y) for l2.
662
663 d1 = v1 - v0, d2 = v3 - v2
664
665 Our parametric equations for the line then are:
666
667 r1 = v0 + t(d1)
668 r2 = v2 + s(d2)
669
670 Set r1 = r2, thus we have:
671
672 v0 + t(d1) = v2 + s(d2)
673
674 Taking coefficients, we have:
675
676 p0x + t(d1x) = p2x + s(d2x)
677 p0y + t(d1y) = p2y + s(d2y)
678
679 rearranging we get:
680
681 t(d1x) - s(d2x) = p2x - p0x
682 t(d1y) - s(d2y) = p2y - p0y
683
684 Determinant D is ad - bc where the matrix looks like:
685
686 a b
687 c d
688
689 so D = (d1x)(d2y) - (d2x)(d1y)
690 if D = 0, the lines are parallel.
691 Dx = (p2x - p0x)(d2y) - (d2x)(p2y - p0y)
692 Dy = (d1x)(p2y - p0y) - (p2x - p0x)(d1y)
693 t = Dx/D, s = Dy/D
694
695 We only need to calculate t, as we can then multiply it by d1 to get the intersection point.
696
697 ---------------------------------------------------------------------------------------------------
698
699 The first and most preferred method for intersection calculation is the perp-product calculation. There are two vectors, v1 and v2. Create a third vector vector between the starting points of these vectors, and calculate the perp product of v2 and the two other vectors. These two scalars have to be divided to get the mulitplication ratio of v1 to reach intersection point. So:
700
701 v1 ( bx1 , by1 );
702 v2 ( bx2 , by2 );
703 v3 ( bx3 , by3 );
704
705 Perp product is equal with dot product of normal of first vector and the second vector, so we need normals:
706
707 n1 ( -by1 , bx1 );
708 n3 ( -by3 , bx3 );
709
710 Dot products:
711
712 dp1 = n3 . v2 = -by3 * bx2 + bx3 * by2;
713 dp2 = n1 . v2 = -by1 * bx2 + bx1 * by2;
714
715 ratio = dp1 / dp2;
716 crossing vector = v1 * ratio;
717
718 And that's it.
719
720 -----------------------------------
721
722 So... to code this, let's say we have two Lines: l1 & l2.
723
724 Vector v1 = l1.endpoint - l1.position;
725 Vector v2 = l2.endpoint - l2.position;
726 Vector v3 = v2 - v1;
727
728 Vector normal1(-v1.y, v1.x);
729 Vector normal3(-v3.y, v3.x);
730
731 double dotProduct1 = v2.Dot(normal1);
732 double dotProduct2 = v2.Dot(normal3);
733
734 if (dotProduct2 == 0)
735         return ParallelLines;
736 else
737 {
738         // I think we'd still have to add the intersection to the position point to get the intersection...
739         Point intersection = v1 * (dotProduct1 / dotProduct2);
740         return intersection;
741 }
742 */
743