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
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 06/01/2010 Added this text. :-)
21 #include "graphicview.h"
22 #include "linetypepattern.h"
23 #include "paintinterface.h"
28 Line::Line(EntityContainer * parent, const LineData & d):
29 AtomicEntity(parent), data(d)
41 Entity * Line::clone()
43 Line * l = new Line(*this);
48 void Line::calculateBorders()
50 minV = Vector::minimum(data.startpoint, data.endpoint);
51 maxV = Vector::maximum(data.startpoint, data.endpoint);
54 /** @return RS2::EntityLine */
55 RS2::EntityType Line::rtti() const
57 return RS2::EntityLine;
61 bool Line::isEdge() const
66 /** @return Copy of data that defines the line. */
67 LineData Line::getData() const
72 VectorSolutions Line::getRefPoints()
74 VectorSolutions ret(data.startpoint, data.endpoint);
78 /** @return Start point of the entity */
79 Vector Line::getStartpoint() const
81 return data.startpoint;
84 /** @return End point of the entity */
85 Vector Line::getEndpoint() const
90 /** Sets the startpoint */
91 void Line::setStartpoint(Vector s)
97 /** Sets the endpoint */
98 void Line::setEndpoint(Vector e)
105 * @return Direction 1. The angle at which the line starts at
108 double Line::getDirection1() const
114 * @return Direction 2. The angle at which the line starts at
117 double Line::getDirection2() const
122 Vector Line::getNearestEndpoint(const Vector & coord, double * dist)
125 Vector * nearerPoint;
127 dist1 = data.startpoint.distanceTo(coord);
128 dist2 = data.endpoint.distanceTo(coord);
135 nearerPoint = &data.endpoint;
142 nearerPoint = &data.startpoint;
148 Vector Line::getNearestPointOnEntity(const Vector & coord,
149 bool onEntity, double * dist, Entity ** entity)
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;
159 if (ae.magnitude() < 1.0e-6 || ea.magnitude() < 1.0e-6)
162 *dist = RS_MAXDOUBLE;
164 return Vector(false);
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());
171 // Check if the projection is within this line:
172 if (onEntity == true && (ba.magnitude() > ae.magnitude() || be.magnitude() > ea.magnitude()))
174 return getNearestEndpoint(coord, dist);
179 *dist = coord.distanceTo(data.startpoint + ba);
181 return data.startpoint + ba;
185 Vector Line::getNearestCenter(const Vector & coord, double * dist)
187 Vector p = (data.startpoint + data.endpoint) / 2.0;
190 *dist = p.distanceTo(coord);
195 Vector Line::getNearestMiddle(const Vector & coord, double * dist)
197 return getNearestCenter(coord, dist);
200 Vector Line::getNearestDist(double distance, const Vector & coord, double * dist)
202 double a1 = getAngle1();
205 dv.setPolar(distance, a1);
207 Vector p1 = data.startpoint + dv;
208 Vector p2 = data.endpoint - dv;
211 Vector * nearerPoint;
213 dist1 = p1.distanceTo(coord);
214 dist2 = p2.distanceTo(coord);
238 Vector Line::getNearestDist(double distance, bool startp)
240 double a1 = getAngle1();
243 dv.setPolar(distance, a1);
247 ret = data.startpoint + dv;
249 ret = data.endpoint - dv;
254 /*Vector Line::getNearestRef(const Vector& coord, double* dist)
258 Vector p1 = getNearestEndpoint(coord, &d1);
259 Vector p2 = getNearestMiddle(coord, &d2);
276 double Line::getDistanceToPoint(const Vector & coord, Entity ** entity,
277 RS2::ResolveLevel /*level*/, double /*solidDist*/)
279 DEBUG->print("Line::getDistanceToPoint");
284 // check endpoints first:
285 double dist = coord.distanceTo(getStartpoint());
289 DEBUG->print("Line::getDistanceToPoint: OK1");
293 dist = coord.distanceTo(getEndpoint());
297 DEBUG->print("Line::getDistanceToPoint: OK2");
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;
307 if (ae.magnitude() < 1.0e-6 || ea.magnitude() < 1.0e-6)
309 DEBUG->print("Line::getDistanceToPoint: OK2a");
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);
317 // Check if the projection is outside this line:
318 if (ba.magnitude() > ae.magnitude() || be.magnitude() > ea.magnitude())
320 // return distance to endpoint
321 getNearestEndpoint(coord, &dist);
322 DEBUG->print("Line::getDistanceToPoint: OK3");
325 //DEBUG->print("ba: %f", ba.magnitude());
326 //DEBUG->print("ae: %f", ae.magnitude());
328 Vector cp = Vector::crossP(ap, ae);
329 dist = cp.magnitude() / ae.magnitude();
331 DEBUG->print("Line::getDistanceToPoint: OK4");
336 void Line::moveStartpoint(const Vector & pos)
338 data.startpoint = pos;
342 void Line::moveEndpoint(const Vector & pos)
348 RS2::Ending Line::getTrimPoint(const Vector & coord, const Vector & trimPoint)
350 double angEl = getAngle1();
351 double angM = trimPoint.angleTo(coord);
352 double angDif = angEl - angM;
358 angDif = 2 * M_PI - angDif;
360 if (angDif < M_PI / 2.0)
361 return RS2::EndingStart;
363 return RS2::EndingEnd;
368 Vector v = data.startpoint;
369 data.startpoint = data.endpoint;
373 /** @return the center point of the line. */
374 Vector Line::getMiddlepoint()
376 return (data.startpoint + data.endpoint) / 2.0;
379 /** Sets the y coordinate of the startpoint */
380 void Line::setStartpointY(double val)
382 data.startpoint.y = val;
386 /** Sets the y coordinate of the endpoint */
387 void Line::setEndpointY(double val)
389 data.endpoint.y = val;
394 * @return The length of the line.
396 double Line::getLength()
398 return data.startpoint.distanceTo(data.endpoint);
402 * @return The angle of the line (from start to endpoint).
404 double Line::getAngle1() const
406 return data.startpoint.angleTo(data.endpoint);
410 * @return The angle of the line (from end to startpoint).
412 double Line::getAngle2() const
414 return data.endpoint.angleTo(data.startpoint);
417 bool Line::hasEndpointsWithinWindow(Vector v1, Vector v2)
419 if (data.startpoint.isInWindow(v1, v2) || data.endpoint.isInWindow(v1, v2))
425 void Line::move(Vector offset)
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);
433 DEBUG->print("Line::move2: sp: %f/%f, ep: %f/%f",
434 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
437 void Line::rotate(Vector center, double angle)
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);
447 DEBUG->print("Line::rotate: OK");
450 void Line::scale(Vector center, Vector factor)
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);
461 void Line::mirror(Vector axisPoint1, Vector axisPoint2)
463 data.startpoint.mirror(axisPoint1, axisPoint2);
464 data.endpoint.mirror(axisPoint1, axisPoint2);
469 * Stretches the given range of the entity by the given offset.
471 void Line::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
473 if (getStartpoint().isInWindow(firstCorner, secondCorner))
474 moveStartpoint(getStartpoint() + offset);
476 if (getEndpoint().isInWindow(firstCorner, secondCorner))
477 moveEndpoint(getEndpoint() + offset);
480 void Line::moveRef(const Vector& ref, const Vector& offset)
482 if (ref.distanceTo(data.startpoint)<1.0e-4) {
483 moveStartpoint(data.startpoint+offset);
485 if (ref.distanceTo(data.endpoint)<1.0e-4) {
486 moveEndpoint(data.endpoint+offset);
490 void Line::draw(PaintInterface * painter, GraphicView * view, double patternOffset)
492 if (!painter || !view)
494 //printf("Line::draw(): Bailing out!!! painter=%08X, view=%08X\n", painter, view);
498 double styleFactor = getStyleFactor(view);
500 if (getPen().getLineType() == RS2::SolidLine || isSelected()
501 || view->getDrawingMode() == RS2::ModePreview
502 || styleFactor < 0.0)
504 //printf("Line::draw(): Drawing line...\n");
505 painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint()));
511 LineTypePattern * pat;
514 pat = &patternSelected;
516 pat = view->getPattern(getPen().getLineType());
518 LineTypePattern * pat = (isSelected() ? &patternSelected : view->getPattern(getPen().getLineType()));
523 //printf("Line::draw(): Pattern == NULL!\n");
524 DEBUG->print(Debug::D_WARNING, "Line::draw: Invalid line pattern");
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);
538 double length = getLength();
539 double angle = getAngle1();
541 // pattern segment length:
542 double patternSegmentLength = 0.0;
545 Vector * dp = new Vector[pat->num];
547 for (i=0; i<pat->num; ++i)
549 dp[i] = Vector(cos(angle) * fabs(pat->pattern[i] * styleFactor),
550 sin(angle) * fabs(pat->pattern[i] * styleFactor));
552 patternSegmentLength += fabs(pat->pattern[i] * styleFactor);
555 // handle pattern offset:
558 if (patternOffset < 0.0)
559 m = (int)ceil(patternOffset / patternSegmentLength);
561 m = (int)floor(patternOffset / patternSegmentLength);
563 patternOffset -= (m * patternSegmentLength);
564 //if (patternOffset<0.0) {
565 // patternOffset+=patternSegmentLength;
567 //DEBUG->print("pattern. offset: %f", patternOffset);
568 Vector patternOffsetVec;
569 patternOffsetVec.setPolar(patternOffset, angle);
571 double tot = patternOffset;
573 // bool cutStartpoint, cutEndpoint, drop;
574 Vector curP = getStartpoint() + patternOffsetVec;
579 // line segment (otherwise space segment)
580 if (pat->pattern[i] > 0.0)
582 bool cutStartpoint = false;
583 bool cutEndpoint = false;
586 // drop the whole pattern segment line:
587 if ((tot + pat->pattern[i] * styleFactor) < 0.0)
593 // trim startpoint of pattern segment line to line startpoint
595 cutStartpoint = true;
597 // trim endpoint of pattern segment line to line endpoint
598 if ((tot + pat->pattern[i] * styleFactor) > length)
605 Vector p2 = curP + dp[i];
608 p1 = getStartpoint();
613 painter->drawLine(view->toGui(p1), view->toGui(p2));
618 tot += fabs(pat->pattern[i] * styleFactor);
619 //DEBUG->print("pattern. tot: %f", tot);
626 done = (tot > length);
634 * Dumps the point's data to stdout.
636 std::ostream & operator<<(std::ostream & os, const Line & l)
638 os << " Line: " << l.getData() << "\n";