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
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 06/01/2010 Added this text. :-)
19 #include "graphicview.h"
20 #include "rs_linetypepattern.h"
21 #include "paintintf.h"
26 RS_Line::RS_Line(RS_EntityContainer * parent, const RS_LineData & d):
27 RS_AtomicEntity(parent), data(d)
39 RS_Entity * RS_Line::clone()
41 RS_Line * l = new RS_Line(*this);
46 void RS_Line::calculateBorders()
48 minV = Vector::minimum(data.startpoint, data.endpoint);
49 maxV = Vector::maximum(data.startpoint, data.endpoint);
52 /** @return RS2::EntityLine */
53 RS2::EntityType RS_Line::rtti() const
55 return RS2::EntityLine;
59 bool RS_Line::isEdge() const
64 /** @return Copy of data that defines the line. */
65 RS_LineData RS_Line::getData() const
70 VectorSolutions RS_Line::getRefPoints()
72 VectorSolutions ret(data.startpoint, data.endpoint);
76 /** @return Start point of the entity */
77 Vector RS_Line::getStartpoint() const
79 return data.startpoint;
82 /** @return End point of the entity */
83 Vector RS_Line::getEndpoint() const
88 /** Sets the startpoint */
89 void RS_Line::setStartpoint(Vector s)
95 /** Sets the endpoint */
96 void RS_Line::setEndpoint(Vector e)
103 * @return Direction 1. The angle at which the line starts at
106 double RS_Line::getDirection1() const
112 * @return Direction 2. The angle at which the line starts at
115 double RS_Line::getDirection2() const
120 Vector RS_Line::getNearestEndpoint(const Vector & coord, double * dist)
123 Vector * nearerPoint;
125 dist1 = data.startpoint.distanceTo(coord);
126 dist2 = data.endpoint.distanceTo(coord);
133 nearerPoint = &data.endpoint;
140 nearerPoint = &data.startpoint;
146 Vector RS_Line::getNearestPointOnEntity(const Vector & coord,
147 bool onEntity, double * dist, RS_Entity ** entity)
152 Vector ae = data.endpoint-data.startpoint;
153 Vector ea = data.startpoint-data.endpoint;
154 Vector ap = coord-data.startpoint;
155 Vector ep = coord-data.endpoint;
157 if (ae.magnitude() < 1.0e-6 || ea.magnitude() < 1.0e-6)
160 *dist = RS_MAXDOUBLE;
162 return Vector(false);
165 // Orthogonal projection from both sides:
166 Vector ba = ae * Vector::dotP(ae, ap) / (ae.magnitude() * ae.magnitude());
167 Vector be = ea * Vector::dotP(ea, ep) / (ea.magnitude() * ea.magnitude());
169 // Check if the projection is within this line:
170 if (onEntity == true && (ba.magnitude() > ae.magnitude() || be.magnitude() > ea.magnitude()))
172 return getNearestEndpoint(coord, dist);
177 *dist = coord.distanceTo(data.startpoint + ba);
179 return data.startpoint + ba;
183 Vector RS_Line::getNearestCenter(const Vector & coord, double * dist)
185 Vector p = (data.startpoint + data.endpoint) / 2.0;
188 *dist = p.distanceTo(coord);
193 Vector RS_Line::getNearestMiddle(const Vector & coord, double * dist)
195 return getNearestCenter(coord, dist);
198 Vector RS_Line::getNearestDist(double distance, const Vector & coord, double * dist)
200 double a1 = getAngle1();
203 dv.setPolar(distance, a1);
205 Vector p1 = data.startpoint + dv;
206 Vector p2 = data.endpoint - dv;
209 Vector * nearerPoint;
211 dist1 = p1.distanceTo(coord);
212 dist2 = p2.distanceTo(coord);
236 Vector RS_Line::getNearestDist(double distance, bool startp)
238 double a1 = getAngle1();
241 dv.setPolar(distance, a1);
245 ret = data.startpoint + dv;
247 ret = data.endpoint - dv;
252 /*Vector RS_Line::getNearestRef(const Vector& coord, double* dist)
256 Vector p1 = getNearestEndpoint(coord, &d1);
257 Vector p2 = getNearestMiddle(coord, &d2);
274 double RS_Line::getDistanceToPoint(const Vector & coord, RS_Entity ** entity,
275 RS2::ResolveLevel /*level*/, double /*solidDist*/)
277 RS_DEBUG->print("RS_Line::getDistanceToPoint");
282 // check endpoints first:
283 double dist = coord.distanceTo(getStartpoint());
287 RS_DEBUG->print("RS_Line::getDistanceToPoint: OK1");
291 dist = coord.distanceTo(getEndpoint());
295 RS_DEBUG->print("RS_Line::getDistanceToPoint: OK2");
300 Vector ae = data.endpoint-data.startpoint;
301 Vector ea = data.startpoint-data.endpoint;
302 Vector ap = coord-data.startpoint;
303 Vector ep = coord-data.endpoint;
305 if (ae.magnitude() < 1.0e-6 || ea.magnitude() < 1.0e-6)
307 RS_DEBUG->print("RS_Line::getDistanceToPoint: OK2a");
311 // Orthogonal projection from both sides:
312 Vector ba = ae * Vector::dotP(ae, ap) / RS_Math::pow(ae.magnitude(), 2);
313 Vector be = ea * Vector::dotP(ea, ep) / RS_Math::pow(ea.magnitude(), 2);
315 // Check if the projection is outside this line:
316 if (ba.magnitude() > ae.magnitude() || be.magnitude() > ea.magnitude())
318 // return distance to endpoint
319 getNearestEndpoint(coord, &dist);
320 RS_DEBUG->print("RS_Line::getDistanceToPoint: OK3");
323 //RS_DEBUG->print("ba: %f", ba.magnitude());
324 //RS_DEBUG->print("ae: %f", ae.magnitude());
326 Vector cp = Vector::crossP(ap, ae);
327 dist = cp.magnitude() / ae.magnitude();
329 RS_DEBUG->print("RS_Line::getDistanceToPoint: OK4");
334 void RS_Line::moveStartpoint(const Vector & pos)
336 data.startpoint = pos;
340 void RS_Line::moveEndpoint(const Vector & pos)
346 RS2::Ending RS_Line::getTrimPoint(const Vector & coord, const Vector & trimPoint)
348 double angEl = getAngle1();
349 double angM = trimPoint.angleTo(coord);
350 double angDif = angEl - angM;
356 angDif = 2 * M_PI - angDif;
358 if (angDif < M_PI / 2.0)
359 return RS2::EndingStart;
361 return RS2::EndingEnd;
364 void RS_Line::reverse()
366 Vector v = data.startpoint;
367 data.startpoint = data.endpoint;
371 /** @return the center point of the line. */
372 Vector RS_Line::getMiddlepoint()
374 return (data.startpoint + data.endpoint) / 2.0;
377 /** Sets the y coordinate of the startpoint */
378 void RS_Line::setStartpointY(double val)
380 data.startpoint.y = val;
384 /** Sets the y coordinate of the endpoint */
385 void RS_Line::setEndpointY(double val)
387 data.endpoint.y = val;
392 * @return The length of the line.
394 double RS_Line::getLength()
396 return data.startpoint.distanceTo(data.endpoint);
400 * @return The angle of the line (from start to endpoint).
402 double RS_Line::getAngle1() const
404 return data.startpoint.angleTo(data.endpoint);
408 * @return The angle of the line (from end to startpoint).
410 double RS_Line::getAngle2() const
412 return data.endpoint.angleTo(data.startpoint);
415 bool RS_Line::hasEndpointsWithinWindow(Vector v1, Vector v2)
417 if (data.startpoint.isInWindow(v1, v2) || data.endpoint.isInWindow(v1, v2))
423 void RS_Line::move(Vector offset)
425 RS_DEBUG->print("RS_Line::move1: sp: %f/%f, ep: %f/%f",
426 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
427 RS_DEBUG->print("RS_Line::move1: offset: %f/%f", offset.x, offset.y);
428 data.startpoint.move(offset);
429 data.endpoint.move(offset);
431 RS_DEBUG->print("RS_Line::move2: sp: %f/%f, ep: %f/%f",
432 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
435 void RS_Line::rotate(Vector center, double angle)
437 RS_DEBUG->print("RS_Line::rotate");
438 RS_DEBUG->print("RS_Line::rotate1: sp: %f/%f, ep: %f/%f",
439 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
440 data.startpoint.rotate(center, angle);
441 data.endpoint.rotate(center, angle);
442 RS_DEBUG->print("RS_Line::rotate2: sp: %f/%f, ep: %f/%f",
443 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
445 RS_DEBUG->print("RS_Line::rotate: OK");
448 void RS_Line::scale(Vector center, Vector factor)
450 RS_DEBUG->print("RS_Line::scale1: sp: %f/%f, ep: %f/%f",
451 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
452 data.startpoint.scale(center, factor);
453 data.endpoint.scale(center, factor);
454 RS_DEBUG->print("RS_Line::scale2: sp: %f/%f, ep: %f/%f",
455 data.startpoint.x, data.startpoint.y, data.endpoint.x, data.endpoint.y);
459 void RS_Line::mirror(Vector axisPoint1, Vector axisPoint2)
461 data.startpoint.mirror(axisPoint1, axisPoint2);
462 data.endpoint.mirror(axisPoint1, axisPoint2);
467 * Stretches the given range of the entity by the given offset.
469 void RS_Line::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
471 if (getStartpoint().isInWindow(firstCorner, secondCorner))
472 moveStartpoint(getStartpoint() + offset);
474 if (getEndpoint().isInWindow(firstCorner, secondCorner))
475 moveEndpoint(getEndpoint() + offset);
478 void RS_Line::moveRef(const Vector& ref, const Vector& offset)
480 if (ref.distanceTo(data.startpoint)<1.0e-4) {
481 moveStartpoint(data.startpoint+offset);
483 if (ref.distanceTo(data.endpoint)<1.0e-4) {
484 moveEndpoint(data.endpoint+offset);
488 void RS_Line::draw(PaintInterface * painter, GraphicView * view, double patternOffset)
490 if (painter == NULL || view == NULL)
492 //printf("RS_Line::draw(): Bailing out!!! painter=%08X, view=%08X\n", painter, view);
496 double styleFactor = getStyleFactor(view);
498 if (getPen().getLineType() == RS2::SolidLine || isSelected()
499 || view->getDrawingMode() == RS2::ModePreview
500 || styleFactor < 0.0)
502 //printf("RS_Line::draw(): Drawing line...\n");
503 painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint()));
509 RS_LineTypePattern * pat;
512 pat = &patternSelected;
514 pat = view->getPattern(getPen().getLineType());
516 RS_LineTypePattern * pat = (isSelected() ? &patternSelected : view->getPattern(getPen().getLineType()));
521 //printf("RS_Line::draw(): Pattern == NULL!\n");
522 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern");
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);
536 double length = getLength();
537 double angle = getAngle1();
539 // pattern segment length:
540 double patternSegmentLength = 0.0;
543 Vector * dp = new Vector[pat->num];
545 for (i=0; i<pat->num; ++i)
547 dp[i] = Vector(cos(angle) * fabs(pat->pattern[i] * styleFactor),
548 sin(angle) * fabs(pat->pattern[i] * styleFactor));
550 patternSegmentLength += fabs(pat->pattern[i] * styleFactor);
553 // handle pattern offset:
556 if (patternOffset < 0.0)
557 m = (int)ceil(patternOffset / patternSegmentLength);
559 m = (int)floor(patternOffset / patternSegmentLength);
561 patternOffset -= (m * patternSegmentLength);
562 //if (patternOffset<0.0) {
563 // patternOffset+=patternSegmentLength;
565 //RS_DEBUG->print("pattern. offset: %f", patternOffset);
566 Vector patternOffsetVec;
567 patternOffsetVec.setPolar(patternOffset, angle);
569 double tot = patternOffset;
571 // bool cutStartpoint, cutEndpoint, drop;
572 Vector curP = getStartpoint() + patternOffsetVec;
577 // line segment (otherwise space segment)
578 if (pat->pattern[i] > 0.0)
580 bool cutStartpoint = false;
581 bool cutEndpoint = false;
584 // drop the whole pattern segment line:
585 if ((tot + pat->pattern[i] * styleFactor) < 0.0)
591 // trim startpoint of pattern segment line to line startpoint
593 cutStartpoint = true;
595 // trim endpoint of pattern segment line to line endpoint
596 if ((tot + pat->pattern[i] * styleFactor) > length)
603 Vector p2 = curP + dp[i];
606 p1 = getStartpoint();
611 painter->drawLine(view->toGui(p1), view->toGui(p2));
616 tot += fabs(pat->pattern[i] * styleFactor);
617 //RS_DEBUG->print("pattern. tot: %f", tot);
624 done = (tot > length);
632 * Dumps the point's data to stdout.
634 std::ostream & operator<<(std::ostream & os, const RS_Line & l)
636 os << " Line: " << l.getData() << "\n";