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