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. :-)
15 #include "rs_polyline.h"
20 #include "rs_graphicview.h"
21 #include "paintintf.h"
26 RS_Polyline::RS_Polyline(RS_EntityContainer * parent): RS_EntityContainer(parent),
27 closingEntity(NULL), nextBulge(0.0)
33 * @param d Polyline data
35 RS_Polyline::RS_Polyline(RS_EntityContainer* parent, const RS_PolylineData& d):
36 RS_EntityContainer(parent), data(d)
46 RS_Polyline::~RS_Polyline()
50 /*virtual*/ RS_Entity * RS_Polyline::clone()
52 RS_Polyline * p = new RS_Polyline(*this);
53 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
54 // p->entities.setAutoDelete(entities.autoDelete());
60 /** @return RS2::EntityPolyline */
61 /*virtual*/ RS2::EntityType RS_Polyline::rtti() const
63 return RS2::EntityPolyline;
66 /** @return Copy of data that defines the polyline. */
67 RS_PolylineData RS_Polyline::getData() const
72 /** sets a new start point of the polyline */
73 void RS_Polyline::setStartpoint(Vector & v)
77 if (!data.endpoint.valid)
81 /** @return Start point of the entity */
82 Vector RS_Polyline::getStartpoint()
84 return data.startpoint;
87 /** sets a new end point of the polyline */
88 void RS_Polyline::setEndpoint(Vector & v)
93 /** @return End point of the entity */
94 Vector RS_Polyline::getEndpoint()
100 * Removes the last vertex of this polyline.
102 void RS_Polyline::removeLastVertex()
104 RS_Entity * last = lastEntity();
113 if (last->isAtomic())
114 data.endpoint = ((RS_AtomicEntity*)last)->getEndpoint();
116 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Polyline::removeLastVertex: "
117 "polyline contains non-atomic entity");
123 * Adds a vertex from the endpoint of the last segment or
124 * from the startpoint of the first segment to 'v' or
125 * sets the startpoint to the point 'v'.
127 * The very first vertex added with this method is the startpoint.
129 * @param v vertex coordinate to be added
130 * @param bulge The bulge of the arc or 0 for a line segment (see DXF documentation)
131 * @param prepend true: prepend at start instead of append at end
133 * @return Pointer to the entity that was addded or NULL if this
134 * was the first vertex added.
136 RS_Entity * RS_Polyline::addVertex(const Vector & v, double bulge, bool prepend)
138 RS_Entity * entity = NULL;
139 //static double nextBulge = 0.0;
141 // very first vertex:
142 if (!data.startpoint.valid)
144 data.startpoint = data.endpoint = v;
148 // consequent vertices:
150 // add entity to the polyline:
151 entity = createVertex(v, nextBulge, prepend);
153 if (prepend==false) {
154 RS_EntityContainer::addEntity(entity);
158 RS_EntityContainer::insertEntity(0, entity);
173 * Creates a vertex from the endpoint of the last element or
174 * sets the startpoint to the point 'v'.
176 * The very first vertex added is the starting point.
178 * @param v vertex coordinate
179 * @param bulge The bulge of the arc (see DXF documentation)
180 * @param prepend true: Prepend instead of append at end
182 * @return Pointer to the entity that was created or NULL if this
183 * was the first vertex added.
185 RS_Entity* RS_Polyline::createVertex(const Vector& v, double bulge, bool prepend) {
187 RS_Entity* entity=NULL;
189 RS_DEBUG->print("RS_Polyline::createVertex: %f/%f to %f/%f bulge: %f",
190 data.endpoint.x, data.endpoint.y, v.x, v.y, bulge);
192 // create line for the polyline:
193 if (fabs(bulge)<RS_TOLERANCE) {
194 if (prepend==false) {
195 entity = new RS_Line(this, RS_LineData(data.endpoint, v));
198 entity = new RS_Line(this, RS_LineData(v, data.startpoint));
200 entity->setSelected(isSelected());
201 entity->setPen(RS_Pen(RS2::FlagInvalid));
202 entity->setLayer(NULL);
203 //RS_EntityContainer::addEntity(entity);
207 // create arc for the polyline:
209 bool reversed = (bulge<0.0);
210 double alpha = atan(bulge)*4.0;
218 if (prepend==false) {
219 middle = (data.endpoint+v)/2.0;
220 dist = data.endpoint.distanceTo(v)/2.0;
221 angle = data.endpoint.angleTo(v);
224 middle = (data.startpoint+v)/2.0;
225 dist = data.startpoint.distanceTo(v)/2.0;
226 angle = v.angleTo(data.startpoint);
229 // alpha can't be 0.0 at this point
230 radius = fabs(dist / sin(alpha/2.0));
232 double wu = fabs(RS_Math::pow(radius, 2.0) - RS_Math::pow(dist, 2.0));
241 if (fabs(alpha)>M_PI) {
245 center.setPolar(h, angle);
251 if (prepend==false) {
252 a1 = center.angleTo(data.endpoint);
253 a2 = center.angleTo(v);
256 a1 = center.angleTo(v);
257 a2 = center.angleTo(data.startpoint);
260 RS_ArcData d(center, radius,
264 entity = new RS_Arc(this, d);
265 entity->setSelected(isSelected());
266 entity->setPen(RS_Pen(RS2::FlagInvalid));
267 entity->setLayer(NULL);
274 * Ends polyline and adds the last entity if the polyline is closed
276 void RS_Polyline::endPolyline()
278 RS_DEBUG->print("RS_Polyline::endPolyline");
282 RS_DEBUG->print("RS_Polyline::endPolyline: adding closing entity");
284 // remove old closing entity:
285 if (closingEntity!=NULL)
287 removeEntity(closingEntity);
290 // add closing entity to the polyline:
291 closingEntity = createVertex(data.startpoint, nextBulge);
292 if (closingEntity!=NULL)
294 RS_EntityContainer::addEntity(closingEntity);
295 //data.endpoint = data.startpoint;
301 * @return The bulge of the closing entity.
303 double RS_Polyline::getClosingBulge()
306 RS_Entity* e = lastEntity();
307 if (e!=NULL && e->rtti()==RS2::EntityArc) {
308 return ((RS_Arc*)e)->getBulge();
316 * Sets the polylines start and endpoint to match the first and last vertex.
318 void RS_Polyline::updateEndpoints()
320 RS_Entity * e1 = firstEntity();
322 if (e1 != NULL && e1->isAtomic())
324 Vector v = ((RS_AtomicEntity *)e1)->getStartpoint();
328 RS_Entity * e2 = lastEntity();
335 if (e2 != NULL && e2->isAtomic())
337 Vector v = ((RS_AtomicEntity *)e2)->getEndpoint();
342 /** @return true if the polyline is closed. false otherwise */
343 bool RS_Polyline::isClosed() const
345 return data.getFlag(RS2::FlagClosed);
348 void RS_Polyline::setClosed(bool cl)
351 data.setFlag(RS2::FlagClosed);
353 data.delFlag(RS2::FlagClosed);
357 * Reimplementation of the addEntity method for a normal container.
358 * This reimplementation deletes the given entity!
360 * To add entities use addVertex() or addSegment() instead.
362 void RS_Polyline::addEntity(RS_Entity * entity)
364 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Polyline::addEntity: should never be called");
372 /*virtual*/ void RS_Polyline::setNextBulge(double bulge)
378 * Adds a segment to the polyline.
380 /*void RS_Polyline::addSegment(RS_Entity* entity) {
381 RS_EntityContainer::addEntity(entity);
382 // TODO: reorder and check polyline
385 VectorSolutions RS_Polyline::getRefPoints()
387 VectorSolutions ret(count()+1);
390 ret.set(0, data.startpoint);
393 for (RS_Entity* e=firstEntity(RS2::ResolveNone);
395 e = nextEntity(RS2::ResolveNone), i++) {
397 ret.set(i, ((RS_AtomicEntity*)e)->getEndpoint());
401 ret.set(count(), data.endpoint);
406 Vector RS_Polyline::getNearestRef(const Vector& coord,
409 return RS_Entity::getNearestRef(coord, dist);
412 Vector RS_Polyline::getNearestSelectedRef(const Vector& coord,
415 return RS_Entity::getNearestSelectedRef(coord, dist);
421 void RS_Polyline::reorder() {
434 void RS_Polyline::move(Vector offset) {
435 RS_EntityContainer::move(offset);
436 data.startpoint.move(offset);
437 data.endpoint.move(offset);
442 void RS_Polyline::rotate(Vector center, double angle) {
443 RS_EntityContainer::rotate(center, angle);
444 data.startpoint.rotate(center, angle);
445 data.endpoint.rotate(center, angle);
450 void RS_Polyline::scale(Vector center, Vector factor) {
451 RS_EntityContainer::scale(center, factor);
452 data.startpoint.scale(center, factor);
453 data.endpoint.scale(center, factor);
458 void RS_Polyline::mirror(Vector axisPoint1, Vector axisPoint2) {
459 RS_EntityContainer::mirror(axisPoint1, axisPoint2);
460 data.startpoint.mirror(axisPoint1, axisPoint2);
461 data.endpoint.mirror(axisPoint1, axisPoint2);
464 void RS_Polyline::moveRef(const Vector & ref, const Vector & offset)
466 RS_EntityContainer::moveRef(ref, offset);
468 if (ref.distanceTo(data.startpoint) < 1.0e-4)
469 data.startpoint.move(offset);
471 if (ref.distanceTo(data.endpoint) < 1.0e-4)
472 data.endpoint.move(offset);
477 void RS_Polyline::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
479 if (data.startpoint.isInWindow(firstCorner, secondCorner))
480 data.startpoint.move(offset);
482 if (data.endpoint.isInWindow(firstCorner, secondCorner))
483 data.endpoint.move(offset);
485 RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
489 * Slightly optimized drawing for polylines.
491 //void RS_Polyline::draw(RS_Painter * painter, RS_GraphicView * view, double /*patternOffset*/)
492 void RS_Polyline::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
494 if (painter == NULL || view == NULL)
497 // draw first entity and set correct pen:
498 RS_Entity * e = firstEntity(RS2::ResolveNone);
501 // draw subsequent entities with same pen:
502 for(RS_Entity * e=nextEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone))
503 view->drawEntityPlain(e);
507 * Dumps the point's data to stdout.
509 std::ostream & operator<<(std::ostream & os, const RS_Polyline & l)
511 os << " Polyline: " << l.getData() << " {\n";
512 os << (RS_EntityContainer &)l;