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