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