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