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