]> Shamusworld >> Repos - architektonas/blob - src/line.cpp
Initial stab at getting Circle to respond to selection rectangle.
[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 "dimension.h"
23 #include "painter.h"
24
25
26 Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p),
27         /*type(OTLine),*/ endpoint(p2),
28         draggingLine(false), draggingHandle1(false), draggingHandle2(false), //needUpdate(false),
29         length(Vector::Magnitude(p2, p1)), angle(Vector(endpoint - position).Unit()),
30         hitPoint1(false), hitPoint2(false), hitLine(false)
31 {
32         type = OTLine;
33 }
34
35
36 Line::~Line()
37 {
38 // Taking care of connections should be done by the Container, as we don't know
39 // anything about any other object connected to this one.
40 #if 0
41         // If there are any attached Dimensions, we must set the attachment points
42         // to NULL since they will no longer be valid.
43         if (attachedDimension)
44         {
45                 attachedDimension->SetPoint1(NULL);
46                 attachedDimension->SetPoint2(NULL);
47         }
48         // IT WOULD BE NICE to have any object points attached to this line automagically
49         // connect to this dimension object at this point, instead of just becoming
50         // detached.
51 #endif
52 }
53
54
55 /*virtual*/ void Line::Draw(Painter * painter)
56 {
57         painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
58
59         if ((state == OSSelected) || ((state == OSInactive) && hitPoint1))
60                 painter->DrawHandle(position);
61
62         if ((state == OSSelected) || ((state == OSInactive) && hitPoint2))
63                 painter->DrawHandle(endpoint);
64
65         if ((state == OSInactive) && !hitLine)
66                 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
67
68         if (Object::fixedLength && (draggingHandle1 || draggingHandle2))
69         {
70                 Vector point1 = (draggingHandle1 ? endpoint : position);
71                 Vector point2 = (draggingHandle1 ? position : endpoint);
72
73                 Vector current(point2 - point1);
74                 Vector v = current.Unit() * length;
75                 Vector v2 = point1 + v;
76 //              painter->DrawLine((int)point1.x, (int)point1.y, (int)v2.x, (int)v2.y);
77                 painter->DrawLine(point1, v2);
78
79                 if (current.Magnitude() > length)
80                 {
81                         painter->SetPen(QPen(QColor(128, 0, 0), 1.0, Qt::DashLine));
82 //                      painter->DrawLine((int)v2.x, (int)v2.y, (int)point2.x, (int)point2.y);
83                         painter->DrawLine(v2, point2);
84                 }
85         }
86 // Problem: when drawing at large zoom levels, this throws away precision thus
87 //          causing the line to rendered too short. !!! FIX !!! [DONE]
88         else
89 //              painter->DrawLine((int)position.x, (int)position.y, (int)endpoint.x, (int)endpoint.y);
90                 painter->DrawLine(position, endpoint);
91 }
92
93 /*virtual*/ Vector Line::Center(void)
94 {
95         // Technically, this is the midpoint but who are we to quibble? :-)
96         Vector v((position.x - endpoint.x) / 2.0, (position.y - endpoint.y) / 2.0);
97         return endpoint + v;
98 }
99
100 /*virtual*/ bool Line::Collided(Vector point)
101 {
102         // We can assume this, since this is a mouse down event here.
103         objectWasDragged = false;
104         HitTest(point);
105
106 /*
107 There's a small problem here with the implementation: You can have a dimension tied
108 to only one point while at the same time you can have a dimension sitting on this line.
109 Since there's only *one* dimPoint for each point, this can be problematic...
110
111 We solve this by allowing only *one* Dimension object to be attached to the Line,
112 Arc, etc. and by giving the Dimension object a pointer to our endpoints.
113
114 Problem still arises when we delete this object; The attached Dimension object will
115 then have bad pointers! What it *should* do is delete the object if and only if this
116 line is not attached to any other object. If it is, then one of those attachment
117 points should be sent to the dimension object (done for position & endpoint).
118
119 NOTE: The STL vector<T> *does not* take ownership of pointers, therefore is suitable
120       for our purposes
121
122 Also: It would be nice to have a preview of the dimension being drawn, with a modifier
123 key to make it draw/show on the other side...
124
125 TODO: Make Dimension preview with modifier keys for showing on other side
126 */
127         // Is the dimension tool active? Let's use it:
128         if (dimensionActive)
129         {
130                 // User clicked on the line itself (endpoint checks should preceed this one):
131                 // (Priorities are taken care of in HitTest()...)
132                 if (hitLine)
133                 {
134 #if 0
135                         if (attachedDimension == NULL)
136                         {
137                                 // How to get this object into the top level container???
138 /*
139 The real question is do we care. I think so, because if this isn't in the top
140 level container, it won't get drawn...
141 But we can fix that by making this object call any attached object's (like
142 a dimension only) Draw() function... :-/
143 */
144                                 attachedDimension = new Dimension(&position, &endpoint, DTLinear, this);
145
146                                 if (parent != NULL)
147                                         parent->Add(attachedDimension);
148                         }
149                         else
150                         {
151                                 // If there's one already there, tell it to flip sides...
152                                 attachedDimension->FlipSides();
153                         }
154 #else
155                         // New approach here: We look for connected objects.
156                         Object * attachedDimension = FindAttachedDimension();
157
158                         if (attachedDimension)
159                         {
160                                 // If there's an attached Dimension, tell it to switch sides...
161                                 ((Dimension *)attachedDimension)->FlipSides();
162                         }
163                         else
164                         {
165                                 // Otherwise, we make a new one and attach it here.
166                                 attachedDimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
167                                 connected.push_back(Connection(attachedDimension, 0));
168                                 connected.push_back(Connection(attachedDimension, 1.0));
169
170                                 if (parent != NULL)
171                                         parent->Add(attachedDimension);
172                         }
173 #endif
174
175                         return true;
176                 }
177         }
178
179
180         if (state == OSInactive)
181         {
182 //printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
183 //printf("      v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
184 //printf("      point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z);
185 //printf("      \n", );
186 //How to translate this into pixels from Document space???
187 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
188 //the caller knows about the zoom factor and all that good kinda crap
189 //I think what's needed is an Object class variable/method that can be changed by the TLC and
190 //called in derived classes to properly scale the location to the current zoom level. That *should* work.
191
192 // ALSO: Need to code a global (read: Object class) variable that tells use whether a modifier
193 //       key was pressed in addition to the mouse click, so we can do stuff like, say, hold
194 //       down CTRL and be able to do multiple selecting of objects (in that case, we would
195 //       keep the Object state from changing).
196                 if (hitPoint1)
197                 {
198                         oldState = state;
199                         state = OSSelected;
200                         oldPoint = position; //maybe "position"?
201                         draggingHandle1 = true;
202                         return true;
203                 }
204                 else if (hitPoint2)
205                 {
206                         oldState = state;
207                         state = OSSelected;
208                         oldPoint = endpoint; //maybe "position"?
209                         draggingHandle2 = true;
210                         return true;
211                 }
212                 else if (hitLine)
213                 {
214                         oldState = state;
215                         state = OSSelected;
216                         oldPoint = point;
217                         draggingLine = true;
218                         return true;
219                 }
220         }
221         else if (state == OSSelected)
222         {
223                 // Here we test for collision with handles as well! (SOON!) [I think it works...NOPE]
224 /*
225 Like so:
226                 if (v1.Magnitude() < 2.0) // Handle #1
227                 else if (v2.Magnitude() < 2.0) // Handle #2
228 */
229                 if (hitLine)
230                 {
231                         oldState = state;
232 //                      state = OSInactive;
233                         oldPoint = point;
234                         draggingLine = true;
235
236                         // Toggle selected state if CTRL held
237                         if (qApp->keyboardModifiers() == Qt::ControlModifier)
238                                 state = OSInactive;
239
240                         return true;
241                 }
242         }
243
244         // If CTRL is held, then we bypass the "turn off" code. Still didn't hit
245         // *this* object though. :-)
246         if (qApp->keyboardModifiers() == Qt::ControlModifier)
247                 return false;
248
249         // If we got here, we clicked on nothing, so set the object to inactive.
250         // (Once we can read key modifiers, we can override this to allow multiple selection.)
251         state = OSInactive;
252         return false;
253 }
254
255
256 /*virtual*/ void Line::PointerMoved(Vector point)
257 {
258         // Hit test tells us what we hit (if anything) through boolean variables. It
259         // also tells us whether or not the state changed.
260         needUpdate = HitTest(point);
261
262         objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
263
264         if (objectWasDragged)
265         {
266                 Vector delta = point - oldPoint;
267
268                 if (draggingHandle1 || draggingLine)
269                         position += delta;
270
271                 if (draggingHandle2 || draggingLine)
272                         endpoint += delta;
273
274                 oldPoint = point;
275                 needUpdate = true;
276
277 //doesn't work          QMainWindow::statusBar()->setText("You are manipulating a line");
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 0
312                 Vector current(point2, point1);
313                 Vector v = current.Unit() * length;
314                 Vector v2 = point1 + v;
315
316                 //bleh
317                 if (!Object::fixedLength)
318                         v2 = point2;
319 #endif
320
321                 if (Object::fixedAngle)
322                 {
323                         // Here we calculate the component of the current vector along the fixed angle.
324                         // A_compB = (A . Bu) * Bu
325                         double magnitudeAlongB = Vector::Dot(Vector(point2 - point1), angle);
326 /*
327 Actually, this isn't quite right. What we want to do is look for the intersection along either
328 the horizontal line or vertical line that intersects from the current mouse position.
329 */
330
331                         if (draggingHandle1)
332                                 position = endpoint + (angle * magnitudeAlongB);
333
334                         if (draggingHandle2)
335                                 endpoint = position + (angle * magnitudeAlongB);
336                 }
337 //              else
338 //                      v2 = point2;
339
340 //If we tell the dimension to flip sides, this is no longer a valid
341 //assumption. !!! FIX !!!
342 //Ideally, we should just send the point that's changing to the Dimension object
343 //and have it figure out which point needs to move... Or is it???
344 // Ideally, we shouldn't have to fuck around with this shit. We need to fix the rendering code
345 // so that we don't have to wait until the dragging is done to correct the position of the
346 // point in question, but we'd need another variable tho.
347 #if 0
348                 if (dimPoint1)
349                         dimPoint1->SetPoint1(draggingHandle1 ? v2 : position);
350                 
351                 if (dimPoint2)
352                         dimPoint2->SetPoint2(draggingHandle2 ? v2 : endpoint);
353 #endif
354         }
355 }
356
357
358 /*virtual*/ void Line::PointerReleased(void)
359 {
360         if (draggingHandle1 || draggingHandle2)
361         {
362                 // Set the length (in case the global state was set to fixed (or not))
363                 if (Object::fixedLength)
364                 {
365                         if (draggingHandle1)    // startpoint
366                         {
367                                 Vector v = Vector(position - endpoint).Unit() * length;
368                                 position = endpoint + v;
369                         }
370                         else                                    // endpoint
371                         {
372 //                              Vector v1 = endpoint - position;
373                                 Vector v = Vector(endpoint - position).Unit() * length;
374                                 endpoint = position + v;
375                         }
376                 }
377                 else
378                 {
379                         // Otherwise, we calculate the new length, just in case on the next move
380                         // it turns out to have a fixed length. :-)
381                         length = Vector(endpoint - position).Magnitude();
382                 }
383
384                 if (!Object::fixedAngle)
385                 {
386                         // Calculate the new angle, just in case on the next move it turns out to
387                         // be fixed. :-)
388                         angle = Vector(endpoint - position).Unit();
389                 }
390         }
391
392         draggingLine = false;
393         draggingHandle1 = false;
394         draggingHandle2 = false;
395
396 //      hitPoint1 = hitPoint2 = hitLine = false;
397
398         // Here we check for just a click: If object was clicked and dragged, then
399         // revert to the old state (OSInactive). Otherwise, keep the new state that
400         // we set.
401 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
402 about keeping track of old states...
403 */
404         if (objectWasDragged)
405                 state = oldState;
406 }
407
408
409 // Check to see if the point passed in coincides with any we have. If so, return a
410 // pointer to it; otherwise, return NULL.
411 /*virtual*/ Vector * Line::GetPointAt(Vector v)
412 {
413         if (v == position)
414                 return &position;
415         else if (v == endpoint)
416                 return &endpoint;
417
418         return 0;
419 }
420
421
422 /*virtual*/ void Line::Enumerate(FILE * file)
423 {
424         fprintf(file, "LINE (%lf,%lf) (%lf,%lf)\n", position.x, position.y, endpoint.x, endpoint.y);
425 }
426
427
428 /*virtual*/ Object * Line::Copy(void)
429 {
430 #warning "!!! This doesn't take care of attached Dimensions !!!"
431 /*
432 This is a real problem. While having a pointer in the Dimension to this line's points is fast & easy,
433 it creates a huge problem when trying to replicate an object like this.
434
435 Maybe a way to fix that then, is to have reference numbers instead of pointers. That way, if you copy
436 them, ... you might still have problems. Because you can't be sure if a copy will be persistant or not,
437 you then *definitely* do not want them to have the same reference number.
438 */
439         return new Line(position, endpoint, parent);
440 }
441
442
443 /*virtual*/ Vector Line::GetPointAtParameter(double parameter)
444 {
445         if (parameter <= 0)
446                 return position;
447         else if (parameter >= 1.0)
448                 return endpoint;
449
450         // Our parameter lies between zero and one, so calculate it!
451         Vector v(endpoint, position);
452         double length = v.Magnitude();
453         // We scale the magnitude of v so that it lies between 0 and 1...
454         // By multiplying the parameter by the magnitude, we obtain the point we
455         // want. No scaling necessary as it's inherent in the approach!
456         double spotOnLength = length * parameter;
457
458         // To get our point, we use the initial point of the line and add in our
459         // scaled point.
460         Vector result = position + (v * spotOnLength);
461         return result;
462 }
463
464
465 /*virtual*/ QRectF Line::Extents(void)
466 {
467         QRectF rect(QPointF(position.x, position.y), QPointF(endpoint.x, endpoint.y));
468         return rect.normalized();
469 }
470
471
472 #if 0
473 /*virtual*/ ObjectType Line::Type(void)
474 {
475         return OTLine;
476 }
477 #endif
478
479
480 void Line::SetDimensionOnLine(Dimension * dimension/*=NULL*/)
481 {
482         // If they don't pass one in, create it for the caller.
483         if (dimension == NULL)
484         {
485 //printf("Line::SetDimensionOnLine(): Creating new dimension...\n");
486 //              dimension = new Dimension(position, endpoint, DTLinear, this);
487                 dimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
488
489                 if (parent)
490 //{
491 //printf("Line::SetDimensionOnLine(): Adding to parent...\n");
492                         parent->Add(dimension);
493 //}
494         }
495         else
496         {
497                 dimension->Connect(this, 0);
498                 dimension->Connect(this, 1.0);
499         }
500
501         // Make sure the Dimension is connected to us...
502         Connect(dimension, 0);
503         Connect(dimension, 1.0);
504 }
505
506
507 Object * Line::FindAttachedDimension(void)
508 {
509         // Is there anything connected to this line? If not, return NULL
510         if (connected.size() < 2)
511                 return NULL;
512
513         // Otherwise, we have to search our objects to see if there's a likely
514         // candidate. In this case, we're looking for a pointer to the same object
515         // with a parameter of 0 and 1 respectively. This is O((n^2)/2).
516         for(uint i=0; i<connected.size(); i++)
517         {
518                 for(uint j=i+1; j<connected.size(); j++)
519                 {
520 //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);
521                         if ((connected[i].object == connected[j].object)
522                                 && ((connected[i].t == 0 && connected[j].t == 1.0)
523                                 || (connected[i].t == 1.0 && connected[j].t == 0)))
524                                 return connected[i].object;
525                 }
526         }
527
528         // Didn't find anything, so return NULL
529         return NULL;
530 }
531
532
533 bool Line::HitTest(Point point)
534 {
535         SaveState();
536
537         hitPoint1 = hitPoint2 = hitLine = false;
538         Vector lineSegment = endpoint - position;
539         Vector v1 = point - position;
540         Vector v2 = point - endpoint;
541         double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
542
543         // Geometric interpretation:
544         // The parameterized point on the vector lineSegment is where the perpendicular
545         // intersects lineSegment. If pp < 0, then the perpendicular lies beyond the 1st
546         // endpoint. If pp > length of ls, then the perpendicular lies beyond the 2nd endpoint.
547
548         if (parameterizedPoint < 0.0)
549                 distance = v1.Magnitude();
550         else if (parameterizedPoint > lineSegment.Magnitude())
551                 distance = v2.Magnitude();
552         else
553                 // distance = ?Det?(ls, v1) / |ls|
554                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
555
556         // Geometric interpretation of the above:
557         // If the segment endpoints are s and e, and the point is p, then the test
558         // for the perpendicular intercepting the segment is equivalent to insisting
559         // that the two dot products {s-e}.{s-p} and {e-s}.{e-p} are both non-negative.
560         // Perpendicular distance from the point to the segment is computed by first
561         // computing the area of the triangle the three points form, then dividing by
562         // the length of the segment.  Distances are done just by the Pythagorean
563         // theorem. Twice the area of the triangle formed by three points is the
564         // determinant of the following matrix:
565         //
566         // sx sy 1       0  0  1       0  0  0
567         // ex ey 1  ==>  ex ey 1  ==>  ex ey 0
568         // px py 1       px py 1       px py 0
569         //
570         // By translating the start point to the origin, and subtracting row 1 from
571         // all other rows, we end up with the matrix on the right which greatly
572         // simplifies the calculation of the determinant.
573
574 //How do we determine distance here? Especially if zoomed in or out???
575 //#warning "!!! Distances tested for may not be valid if zoomed in or out !!!"
576 // [FIXED]
577         if ((v1.Magnitude() * Painter::zoom) < 8.0)
578                 hitPoint1 = true;
579         else if ((v2.Magnitude() * Painter::zoom) < 8.0)
580                 hitPoint2 = true;
581         else if ((distance * Painter::zoom) < 5.0)
582                 hitLine = true;
583
584         return StateChanged();
585 }
586
587 void Line::SaveState(void)
588 {
589         oldHitPoint1 = hitPoint1;
590         oldHitPoint2 = hitPoint2;
591         oldHitLine = hitLine;
592 }
593
594 bool Line::StateChanged(void)
595 {
596         if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2) || (hitLine != oldHitLine))
597                 return true;
598
599         return false;
600 }
601
602 /*
603 Intersection of two lines:
604
605 Find where the lines with equations r = i + j + t (3i - j) and r = -i + s (j) intersect.
606
607 When they intersect, we can set the equations equal to one another:
608
609 i + j + t (3i - j) = -i + s (j)
610
611 Equating coefficients:
612 1 + 3t = -1 and 1 - t = s
613 So t = -2/3 and s = 5/3
614
615 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 .
616
617
618 so, let's say we have two lines, l1 and l2. Points are v0(p0x, p0y), v1(p1x, p1y) for l1
619 and v2(p2x, p2y), v3(p3x, p3y) for l2.
620
621 d1 = v1 - v0, d2 = v3 - v2
622
623 Our parametric equations for the line then are:
624
625 r1 = v0 + t(d1)
626 r2 = v2 + s(d2)
627
628 Set r1 = r2, thus we have:
629
630 v0 + t(d1) = v2 + s(d2)
631
632 Taking coefficients, we have:
633
634 p0x + t(d1x) = p2x + s(d2x)
635 p0y + t(d1y) = p2y + s(d2y)
636
637 rearranging we get:
638
639 t(d1x) - s(d2x) = p2x - p0x
640 t(d1y) - s(d2y) = p2y - p0y
641
642 Determinant D is ad - bc where the matrix looks like:
643
644 a b
645 c d
646
647 so D = (d1x)(d2y) - (d2x)(d1y)
648 if D = 0, the lines are parallel.
649 Dx = (p2x - p0x)(d2y) - (d2x)(p2y - p0y)
650 Dy = (d1x)(p2y - p0y) - (p2x - p0x)(d1y)
651 t = Dx/D, s = Dy/D
652
653 We only need to calculate t, as we can then multiply it by d1 to get the intersection point.
654
655 ---------------------------------------------------------------------------------------------------
656
657 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:
658
659 v1 ( bx1 , by1 );
660 v2 ( bx2 , by2 );
661 v3 ( bx3 , by3 );
662
663 Perp product is equal with dot product of normal of first vector and the second vector, so we need normals:
664
665 n1 ( -by1 , bx1 );
666 n3 ( -by3 , bx3 );
667
668 Dot products:
669
670 dp1 = n3 . v2 = -by3 * bx2 + bx3 * by2;
671 dp2 = n1 . v2 = -by1 * bx2 + bx1 * by2;
672
673 ratio = dp1 / dp2;
674 crossing vector = v1 * ratio;
675
676 And that's it.
677
678 -----------------------------------
679
680 So... to code this, let's say we have two Lines: l1 & l2.
681
682 Vector v1 = l1.endpoint - l1.position;
683 Vector v2 = l2.endpoint - l2.position;
684 Vector v3 = v2 - v1;
685
686 Vector normal1(-v1.y, v1.x);
687 Vector normal3(-v3.y, v3.x);
688
689 double dotProduct1 = v2.Dot(normal1);
690 double dotProduct2 = v2.Dot(normal3);
691
692 if (dotProduct2 == 0)
693         return ParallelLines;
694 else
695 {
696         // I think we'd still have to add the intersection to the position point to get the intersection...
697         Point intersection = v1 * (dotProduct1 / dotProduct2);
698         return intersection;
699 }
700 */
701