]> Shamusworld >> Repos - architektonas/blob - src/base/line.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / line.cpp
1 // line.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  06/01/2010  Added this text. :-)
15 //
16
17 #include "line.h"
18
19 #include "debug.h"
20 #include "drawing.h"
21 #include "graphicview.h"
22 #include "linetypepattern.h"
23 #include "paintinterface.h"
24
25 /**
26  * Constructor.
27  */
28 Line::Line(EntityContainer * parent, const LineData & d):
29         AtomicEntity(parent), data(d)
30 {
31         calculateBorders();
32 }
33
34 /**
35  * Destructor.
36  */
37 Line::~Line()
38 {
39 }
40
41 Entity * Line::clone()
42 {
43         Line * l = new Line(*this);
44         l->initId();
45         return l;
46 }
47
48 void Line::calculateBorders()
49 {
50         minV = Vector::minimum(data.startpoint, data.endpoint);
51         maxV = Vector::maximum(data.startpoint, data.endpoint);
52 }
53
54 /**     @return RS2::EntityLine */
55 RS2::EntityType Line::rtti() const
56 {
57         return RS2::EntityLine;
58 }
59
60 /** @return true */
61 bool Line::isEdge() const
62 {
63         return true;
64 }
65
66 /** @return Copy of data that defines the line. */
67 LineData Line::getData() const
68 {
69         return data;
70 }
71
72 VectorSolutions Line::getRefPoints()
73 {
74         VectorSolutions ret(data.startpoint, data.endpoint);
75         return ret;
76 }
77
78 /** @return Start point of the entity */
79 Vector Line::getStartpoint() const
80 {
81         return data.startpoint;
82 }
83
84 /** @return End point of the entity */
85 Vector Line::getEndpoint() const
86 {
87         return data.endpoint;
88 }
89
90 /** Sets the startpoint */
91 void Line::setStartpoint(Vector s)
92 {
93         data.startpoint = s;
94         calculateBorders();
95 }
96
97 /** Sets the endpoint */
98 void Line::setEndpoint(Vector e)
99 {
100         data.endpoint = e;
101         calculateBorders();
102 }
103
104 /**
105 * @return Direction 1. The angle at which the line starts at
106 * the startpoint.
107 */
108 double Line::getDirection1() const
109 {
110         return getAngle1();
111 }
112
113 /**
114 * @return Direction 2. The angle at which the line starts at
115 * the endpoint.
116 */
117 double Line::getDirection2() const
118 {
119         return getAngle2();
120 }
121
122 Vector Line::getNearestEndpoint(const Vector & coord, double * dist)
123 {
124         double dist1, dist2;
125         Vector * nearerPoint;
126
127         dist1 = data.startpoint.distanceTo(coord);
128         dist2 = data.endpoint.distanceTo(coord);
129
130         if (dist2 < dist1)
131         {
132                 if (dist != NULL)
133                         *dist = dist2;
134
135                 nearerPoint = &data.endpoint;
136         }
137         else
138         {
139                 if (dist != NULL)
140                         *dist = dist1;
141
142                 nearerPoint = &data.startpoint;
143         }
144
145         return *nearerPoint;
146 }
147
148 Vector Line::getNearestPointOnEntity(const Vector & coord,
149         bool onEntity, double * dist, Entity ** entity)
150 {
151         if (entity != NULL)
152                 *entity = this;
153
154         Vector ae = data.endpoint-data.startpoint;
155         Vector ea = data.startpoint-data.endpoint;
156         Vector ap = coord-data.startpoint;
157         Vector ep = coord-data.endpoint;
158
159         if (ae.magnitude() < 1.0e-6 || ea.magnitude() < 1.0e-6)
160         {
161                 if (dist != NULL)
162                         *dist = RS_MAXDOUBLE;
163
164                 return Vector(false);
165         }
166
167         // Orthogonal projection from both sides:
168         Vector ba = ae * Vector::dotP(ae, ap) / (ae.magnitude() * ae.magnitude());
169         Vector be = ea * Vector::dotP(ea, ep) / (ea.magnitude() * ea.magnitude());
170
171         // Check if the projection is within this line:
172         if (onEntity == true && (ba.magnitude() > ae.magnitude() || be.magnitude() > ea.magnitude()))
173         {
174                 return getNearestEndpoint(coord, dist);
175         }
176         else
177         {
178                 if (dist != NULL)
179                         *dist = coord.distanceTo(data.startpoint + ba);
180
181                 return data.startpoint + ba;
182         }
183 }
184
185 Vector Line::getNearestCenter(const Vector & coord, double * dist)
186 {
187         Vector p = (data.startpoint + data.endpoint) / 2.0;
188
189         if (dist != NULL)
190                 *dist = p.distanceTo(coord);
191
192         return p;
193 }
194
195 Vector Line::getNearestMiddle(const Vector & coord, double * dist)
196 {
197         return getNearestCenter(coord, dist);
198 }
199
200 Vector Line::getNearestDist(double distance, const Vector & coord, double * dist)
201 {
202         double a1 = getAngle1();
203
204         Vector dv;
205         dv.setPolar(distance, a1);
206
207         Vector p1 = data.startpoint + dv;
208         Vector p2 = data.endpoint - dv;
209
210         double dist1, dist2;
211         Vector * nearerPoint;
212
213         dist1 = p1.distanceTo(coord);
214         dist2 = p2.distanceTo(coord);
215
216         if (dist2 < dist1)
217         {
218                 if (dist != NULL)
219                 {
220                         *dist = dist2;
221                 }
222
223                 nearerPoint = &p2;
224         }
225         else
226         {
227                 if (dist != NULL)
228                 {
229                         *dist = dist1;
230                 }
231
232                 nearerPoint = &p1;
233         }
234
235         return *nearerPoint;
236 }
237
238 Vector Line::getNearestDist(double distance, bool startp)
239 {
240         double a1 = getAngle1();
241
242         Vector dv;
243         dv.setPolar(distance, a1);
244         Vector ret;
245
246         if (startp)
247                 ret = data.startpoint + dv;
248         else
249                 ret = data.endpoint - dv;
250
251         return ret;
252 }
253
254 /*Vector Line::getNearestRef(const Vector& coord, double* dist)
255 {
256         double d1, d2, d;
257         Vector p;
258         Vector p1 = getNearestEndpoint(coord, &d1);
259         Vector p2 = getNearestMiddle(coord, &d2);
260
261         if (d1<d2) {
262                 d = d1;
263                 p = p1;
264         } else {
265                 d = d2;
266                 p = p2;
267         }
268
269         if (dist!=NULL) {
270                 *dist = d;
271         }
272
273         return p;
274 }*/
275
276 double Line::getDistanceToPoint(const Vector & coord, Entity ** entity,
277         RS2::ResolveLevel /*level*/, double /*solidDist*/)
278 {
279         DEBUG->print("Line::getDistanceToPoint");
280
281         if (entity != NULL)
282                 *entity = this;
283
284         // check endpoints first:
285         double dist = coord.distanceTo(getStartpoint());
286
287         if (dist < 1.0e-4)
288         {
289                 DEBUG->print("Line::getDistanceToPoint: OK1");
290                 return dist;
291         }
292
293         dist = coord.distanceTo(getEndpoint());
294
295         if (dist < 1.0e-4)
296         {
297                 DEBUG->print("Line::getDistanceToPoint: OK2");
298                 return dist;
299         }
300
301         dist = RS_MAXDOUBLE;
302         Vector ae = data.endpoint-data.startpoint;
303         Vector ea = data.startpoint-data.endpoint;
304         Vector ap = coord-data.startpoint;
305         Vector ep = coord-data.endpoint;
306
307         if (ae.magnitude() < 1.0e-6 || ea.magnitude() < 1.0e-6)
308         {
309                 DEBUG->print("Line::getDistanceToPoint: OK2a");
310                 return dist;
311         }
312
313         // Orthogonal projection from both sides:
314         Vector ba = ae * Vector::dotP(ae, ap) / Math::pow(ae.magnitude(), 2);
315         Vector be = ea * Vector::dotP(ea, ep) / Math::pow(ea.magnitude(), 2);
316
317         // Check if the projection is outside this line:
318         if (ba.magnitude() > ae.magnitude() || be.magnitude() > ea.magnitude())
319         {
320                 // return distance to endpoint
321                 getNearestEndpoint(coord, &dist);
322                 DEBUG->print("Line::getDistanceToPoint: OK3");
323                 return dist;
324         }
325         //DEBUG->print("ba: %f", ba.magnitude());
326         //DEBUG->print("ae: %f", ae.magnitude());
327
328         Vector cp = Vector::crossP(ap, ae);
329         dist = cp.magnitude() / ae.magnitude();
330
331         DEBUG->print("Line::getDistanceToPoint: OK4");
332
333         return dist;
334 }
335
336 void Line::moveStartpoint(const Vector & pos)
337 {
338         data.startpoint = pos;
339         calculateBorders();
340 }
341
342 void Line::moveEndpoint(const Vector & pos)
343 {
344         data.endpoint = pos;
345         calculateBorders();
346 }
347
348 RS2::Ending Line::getTrimPoint(const Vector & coord, const Vector & trimPoint)
349 {
350         double angEl = getAngle1();
351         double angM = trimPoint.angleTo(coord);
352         double angDif = angEl - angM;
353
354         if (angDif < 0.0)
355                 angDif *= -1.0;
356
357         if (angDif > M_PI)
358                 angDif = 2 * M_PI - angDif;
359
360         if (angDif < M_PI / 2.0)
361                 return RS2::EndingStart;
362         else
363                 return RS2::EndingEnd;
364 }
365
366 void Line::reverse()
367 {
368         Vector v = data.startpoint;
369         data.startpoint = data.endpoint;
370         data.endpoint = v;
371 }
372
373 /** @return the center point of the line. */
374 Vector Line::getMiddlepoint()
375 {
376         return (data.startpoint + data.endpoint) / 2.0;
377 }
378
379 /** Sets the y coordinate of the startpoint */
380 void Line::setStartpointY(double val)
381 {
382         data.startpoint.y = val;
383         calculateBorders();
384 }
385
386 /** Sets the y coordinate of the endpoint */
387 void Line::setEndpointY(double val)
388 {
389         data.endpoint.y = val;
390         calculateBorders();
391 }
392
393 /**
394  * @return The length of the line.
395  */
396 double Line::getLength()
397 {
398         return data.startpoint.distanceTo(data.endpoint);
399 }
400
401 /**
402  * @return The angle of the line (from start to endpoint).
403  */
404 double Line::getAngle1() const
405 {
406         return data.startpoint.angleTo(data.endpoint);
407 }
408
409 /**
410  * @return The angle of the line (from end to startpoint).
411  */
412 double Line::getAngle2() const
413 {
414         return data.endpoint.angleTo(data.startpoint);
415 }
416
417 bool Line::hasEndpointsWithinWindow(Vector v1, Vector v2)
418 {
419         if (data.startpoint.isInWindow(v1, v2) || data.endpoint.isInWindow(v1, v2))
420                 return true;
421
422         return false;
423 }
424
425 void Line::move(Vector offset)
426 {
427         DEBUG->print("Line::move1: sp: %f/%f, ep: %f/%f",
428                 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
429         DEBUG->print("Line::move1: offset: %f/%f", offset.x, offset.y);
430         data.startpoint.move(offset);
431         data.endpoint.move(offset);
432         calculateBorders();
433         DEBUG->print("Line::move2: sp: %f/%f, ep: %f/%f",
434                 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
435 }
436
437 void Line::rotate(Vector center, double angle)
438 {
439         DEBUG->print("Line::rotate");
440         DEBUG->print("Line::rotate1: sp: %f/%f, ep: %f/%f",
441                 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
442         data.startpoint.rotate(center, angle);
443         data.endpoint.rotate(center, angle);
444         DEBUG->print("Line::rotate2: sp: %f/%f, ep: %f/%f",
445                 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
446         calculateBorders();
447         DEBUG->print("Line::rotate: OK");
448 }
449
450 void Line::scale(Vector center, Vector factor)
451 {
452         DEBUG->print("Line::scale1: sp: %f/%f, ep: %f/%f",
453                 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
454         data.startpoint.scale(center, factor);
455         data.endpoint.scale(center, factor);
456         DEBUG->print("Line::scale2: sp: %f/%f, ep: %f/%f",
457                 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
458         calculateBorders();
459 }
460
461 void Line::mirror(Vector axisPoint1, Vector axisPoint2)
462 {
463         data.startpoint.mirror(axisPoint1, axisPoint2);
464         data.endpoint.mirror(axisPoint1, axisPoint2);
465         calculateBorders();
466 }
467
468 /**
469  * Stretches the given range of the entity by the given offset.
470  */
471 void Line::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
472 {
473         if (getStartpoint().isInWindow(firstCorner, secondCorner))
474                 moveStartpoint(getStartpoint() + offset);
475
476         if (getEndpoint().isInWindow(firstCorner, secondCorner))
477                 moveEndpoint(getEndpoint() + offset);
478 }
479
480 void Line::moveRef(const Vector& ref, const Vector& offset)
481 {
482     if (ref.distanceTo(data.startpoint)<1.0e-4) {
483         moveStartpoint(data.startpoint+offset);
484     }
485     if (ref.distanceTo(data.endpoint)<1.0e-4) {
486         moveEndpoint(data.endpoint+offset);
487     }
488 }
489
490 void Line::draw(PaintInterface * painter, GraphicView * view, double patternOffset)
491 {
492         if (!painter || !view)
493 //{
494 //printf("Line::draw(): Bailing out!!! painter=%08X, view=%08X\n", painter, view);
495                 return;
496 //}
497
498         double styleFactor = getStyleFactor(view);
499
500         if (getPen().getLineType() == RS2::SolidLine || isSelected()
501                 || view->getDrawingMode() == RS2::ModePreview
502                 || styleFactor < 0.0)
503         {
504 //printf("Line::draw(): Drawing line...\n");
505                 painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint()));
506                 return;
507         }
508
509         // Pattern:
510 #if 0
511         LineTypePattern * pat;
512
513         if (isSelected())
514                 pat = &patternSelected;
515         else
516                 pat = view->getPattern(getPen().getLineType());
517 #else
518         LineTypePattern * pat = (isSelected() ? &patternSelected : view->getPattern(getPen().getLineType()));
519 #endif
520
521         if (!pat)
522         {
523 //printf("Line::draw(): Pattern == NULL!\n");
524                 DEBUG->print(Debug::D_WARNING, "Line::draw: Invalid line pattern");
525                 return;
526         }
527
528 //printf("Line::draw(): Drawing a patterned line...(?)\n");
529         // Pen to draw pattern is always solid:
530         Pen pen = painter->getPen();
531         pen.setLineType(RS2::SolidLine);
532         painter->setPen(pen);
533
534         // index counter
535         int i;
536
537         // line data:
538         double length = getLength();
539         double angle = getAngle1();
540
541         // pattern segment length:
542         double patternSegmentLength = 0.0;
543
544         // create pattern:
545         Vector * dp = new Vector[pat->num];
546
547         for (i=0; i<pat->num; ++i)
548         {
549                 dp[i] = Vector(cos(angle) * fabs(pat->pattern[i] * styleFactor),
550                         sin(angle) * fabs(pat->pattern[i] * styleFactor));
551
552                 patternSegmentLength += fabs(pat->pattern[i] * styleFactor);
553         }
554
555         // handle pattern offset:
556         int m;
557
558         if (patternOffset < 0.0)
559                 m = (int)ceil(patternOffset / patternSegmentLength);
560         else
561                 m = (int)floor(patternOffset / patternSegmentLength);
562
563         patternOffset -= (m * patternSegmentLength);
564         //if (patternOffset<0.0) {
565         //      patternOffset+=patternSegmentLength;
566         //}
567         //DEBUG->print("pattern. offset: %f", patternOffset);
568         Vector patternOffsetVec;
569         patternOffsetVec.setPolar(patternOffset, angle);
570
571         double tot = patternOffset;
572         i = 0;
573 //      bool cutStartpoint, cutEndpoint, drop;
574         Vector curP = getStartpoint() + patternOffsetVec;
575         bool done = false;
576
577         do
578         {
579                 // line segment (otherwise space segment)
580                 if (pat->pattern[i] > 0.0)
581                 {
582                         bool cutStartpoint = false;
583                         bool cutEndpoint = false;
584                         bool drop = false;
585
586                         // drop the whole pattern segment line:
587                         if ((tot + pat->pattern[i] * styleFactor) < 0.0)
588                         {
589                                 drop = true;
590                         }
591                         else
592                         {
593                                 // trim startpoint of pattern segment line to line startpoint
594                                 if (tot < 0.0)
595                                         cutStartpoint = true;
596
597                                 // trim endpoint of pattern segment line to line endpoint
598                                 if ((tot + pat->pattern[i] * styleFactor) > length)
599                                         cutEndpoint = true;
600                         }
601
602                         if (!drop)
603                         {
604                                 Vector p1 = curP;
605                                 Vector p2 = curP + dp[i];
606
607                                 if (cutStartpoint)
608                                         p1 = getStartpoint();
609
610                                 if (cutEndpoint)
611                                         p2 = getEndpoint();
612
613                                 painter->drawLine(view->toGui(p1), view->toGui(p2));
614                         }
615                 }
616
617                 curP += dp[i];
618                 tot += fabs(pat->pattern[i] * styleFactor);
619                 //DEBUG->print("pattern. tot: %f", tot);
620
621                 i++;
622
623                 if (i >= pat->num)
624                         i = 0;
625
626                 done = (tot > length);
627         }
628         while (!done);
629
630         delete[] dp;
631 }
632
633 /**
634  * Dumps the point's data to stdout.
635  */
636 std::ostream & operator<<(std::ostream & os, const Line & l)
637 {
638         os << " Line: " << l.getData() << "\n";
639         return os;
640 }