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. :-)
22 #include "graphicview.h"
23 #include "paintinterface.h"
28 Polyline::Polyline(EntityContainer * parent): EntityContainer(parent),
29 closingEntity(NULL), nextBulge(0.0)
35 * @param d Polyline data
37 Polyline::Polyline(EntityContainer* parent, const PolylineData& d):
38 EntityContainer(parent), data(d)
52 /*virtual*/ Entity * Polyline::clone()
54 Polyline * p = new Polyline(*this);
55 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
56 // p->entities.setAutoDelete(entities.autoDelete());
62 /** @return RS2::EntityPolyline */
63 /*virtual*/ RS2::EntityType Polyline::rtti() const
65 return RS2::EntityPolyline;
68 /** @return Copy of data that defines the polyline. */
69 PolylineData Polyline::getData() const
74 /** sets a new start point of the polyline */
75 void Polyline::setStartpoint(Vector & v)
79 if (!data.endpoint.valid)
83 /** @return Start point of the entity */
84 Vector Polyline::getStartpoint()
86 return data.startpoint;
89 /** sets a new end point of the polyline */
90 void Polyline::setEndpoint(Vector & v)
95 /** @return End point of the entity */
96 Vector Polyline::getEndpoint()
102 * Removes the last vertex of this polyline.
104 void Polyline::removeLastVertex()
106 Entity * last = lastEntity();
115 if (last->isAtomic())
116 data.endpoint = ((AtomicEntity*)last)->getEndpoint();
118 DEBUG->print(Debug::D_WARNING, "Polyline::removeLastVertex: "
119 "polyline contains non-atomic entity");
125 * Adds a vertex from the endpoint of the last segment or
126 * from the startpoint of the first segment to 'v' or
127 * sets the startpoint to the point 'v'.
129 * The very first vertex added with this method is the startpoint.
131 * @param v vertex coordinate to be added
132 * @param bulge The bulge of the arc or 0 for a line segment (see DXF documentation)
133 * @param prepend true: prepend at start instead of append at end
135 * @return Pointer to the entity that was addded or NULL if this
136 * was the first vertex added.
138 Entity * Polyline::addVertex(const Vector & v, double bulge, bool prepend)
140 Entity * entity = NULL;
141 //static double nextBulge = 0.0;
143 // very first vertex:
144 if (!data.startpoint.valid)
146 data.startpoint = data.endpoint = v;
150 // consequent vertices:
152 // add entity to the polyline:
153 entity = createVertex(v, nextBulge, prepend);
155 if (prepend==false) {
156 EntityContainer::addEntity(entity);
160 EntityContainer::insertEntity(0, entity);
175 * Creates a vertex from the endpoint of the last element or
176 * sets the startpoint to the point 'v'.
178 * The very first vertex added is the starting point.
180 * @param v vertex coordinate
181 * @param bulge The bulge of the arc (see DXF documentation)
182 * @param prepend true: Prepend instead of append at end
184 * @return Pointer to the entity that was created or NULL if this
185 * was the first vertex added.
187 Entity* Polyline::createVertex(const Vector& v, double bulge, bool prepend) {
191 DEBUG->print("Polyline::createVertex: %f/%f to %f/%f bulge: %f",
192 data.endpoint.x, data.endpoint.y, v.x, v.y, bulge);
194 // create line for the polyline:
195 if (fabs(bulge)<RS_TOLERANCE) {
196 if (prepend==false) {
197 entity = new Line(this, LineData(data.endpoint, v));
200 entity = new Line(this, LineData(v, data.startpoint));
202 entity->setSelected(isSelected());
203 entity->setPen(Pen(RS2::FlagInvalid));
204 entity->setLayer(NULL);
205 //EntityContainer::addEntity(entity);
209 // create arc for the polyline:
211 bool reversed = (bulge<0.0);
212 double alpha = atan(bulge)*4.0;
220 if (prepend==false) {
221 middle = (data.endpoint+v)/2.0;
222 dist = data.endpoint.distanceTo(v)/2.0;
223 angle = data.endpoint.angleTo(v);
226 middle = (data.startpoint+v)/2.0;
227 dist = data.startpoint.distanceTo(v)/2.0;
228 angle = v.angleTo(data.startpoint);
231 // alpha can't be 0.0 at this point
232 radius = fabs(dist / sin(alpha/2.0));
234 double wu = fabs(Math::pow(radius, 2.0) - Math::pow(dist, 2.0));
243 if (fabs(alpha)>M_PI) {
247 center.setPolar(h, angle);
253 if (prepend==false) {
254 a1 = center.angleTo(data.endpoint);
255 a2 = center.angleTo(v);
258 a1 = center.angleTo(v);
259 a2 = center.angleTo(data.startpoint);
262 ArcData d(center, radius,
266 entity = new Arc(this, d);
267 entity->setSelected(isSelected());
268 entity->setPen(Pen(RS2::FlagInvalid));
269 entity->setLayer(NULL);
276 * Ends polyline and adds the last entity if the polyline is closed
278 void Polyline::endPolyline()
280 DEBUG->print("Polyline::endPolyline");
284 DEBUG->print("Polyline::endPolyline: adding closing entity");
286 // remove old closing entity:
287 if (closingEntity!=NULL)
289 removeEntity(closingEntity);
292 // add closing entity to the polyline:
293 closingEntity = createVertex(data.startpoint, nextBulge);
294 if (closingEntity!=NULL)
296 EntityContainer::addEntity(closingEntity);
297 //data.endpoint = data.startpoint;
303 * @return The bulge of the closing entity.
305 double Polyline::getClosingBulge()
308 Entity* e = lastEntity();
309 if (e!=NULL && e->rtti()==RS2::EntityArc) {
310 return ((Arc*)e)->getBulge();
318 * Sets the polylines start and endpoint to match the first and last vertex.
320 void Polyline::updateEndpoints()
322 Entity * e1 = firstEntity();
324 if (e1 != NULL && e1->isAtomic())
326 Vector v = ((AtomicEntity *)e1)->getStartpoint();
330 Entity * e2 = lastEntity();
337 if (e2 != NULL && e2->isAtomic())
339 Vector v = ((AtomicEntity *)e2)->getEndpoint();
344 /** @return true if the polyline is closed. false otherwise */
345 bool Polyline::isClosed() const
347 return data.getFlag(RS2::FlagClosed);
350 void Polyline::setClosed(bool cl)
353 data.setFlag(RS2::FlagClosed);
355 data.delFlag(RS2::FlagClosed);
359 * Reimplementation of the addEntity method for a normal container.
360 * This reimplementation deletes the given entity!
362 * To add entities use addVertex() or addSegment() instead.
364 void Polyline::addEntity(Entity * entity)
366 DEBUG->print(Debug::D_WARNING, "Polyline::addEntity: should never be called");
374 /*virtual*/ void Polyline::setNextBulge(double bulge)
380 * Adds a segment to the polyline.
382 /*void Polyline::addSegment(Entity* entity) {
383 EntityContainer::addEntity(entity);
384 // TODO: reorder and check polyline
387 VectorSolutions Polyline::getRefPoints()
389 VectorSolutions ret(count()+1);
392 ret.set(0, data.startpoint);
395 for (Entity* e=firstEntity(RS2::ResolveNone);
397 e = nextEntity(RS2::ResolveNone), i++) {
399 ret.set(i, ((AtomicEntity*)e)->getEndpoint());
403 ret.set(count(), data.endpoint);
408 Vector Polyline::getNearestRef(const Vector& coord,
411 return Entity::getNearestRef(coord, dist);
414 Vector Polyline::getNearestSelectedRef(const Vector& coord,
417 return Entity::getNearestSelectedRef(coord, dist);
423 void Polyline::reorder() {
436 void Polyline::move(Vector offset) {
437 EntityContainer::move(offset);
438 data.startpoint.move(offset);
439 data.endpoint.move(offset);
444 void Polyline::rotate(Vector center, double angle) {
445 EntityContainer::rotate(center, angle);
446 data.startpoint.rotate(center, angle);
447 data.endpoint.rotate(center, angle);
452 void Polyline::scale(Vector center, Vector factor) {
453 EntityContainer::scale(center, factor);
454 data.startpoint.scale(center, factor);
455 data.endpoint.scale(center, factor);
460 void Polyline::mirror(Vector axisPoint1, Vector axisPoint2) {
461 EntityContainer::mirror(axisPoint1, axisPoint2);
462 data.startpoint.mirror(axisPoint1, axisPoint2);
463 data.endpoint.mirror(axisPoint1, axisPoint2);
466 void Polyline::moveRef(const Vector & ref, const Vector & offset)
468 EntityContainer::moveRef(ref, offset);
470 if (ref.distanceTo(data.startpoint) < 1.0e-4)
471 data.startpoint.move(offset);
473 if (ref.distanceTo(data.endpoint) < 1.0e-4)
474 data.endpoint.move(offset);
479 void Polyline::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
481 if (data.startpoint.isInWindow(firstCorner, secondCorner))
482 data.startpoint.move(offset);
484 if (data.endpoint.isInWindow(firstCorner, secondCorner))
485 data.endpoint.move(offset);
487 EntityContainer::stretch(firstCorner, secondCorner, offset);
491 * Slightly optimized drawing for polylines.
493 void Polyline::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
495 if (!painter || !view)
498 // draw first entity and set correct pen:
499 Entity * e = firstEntity(RS2::ResolveNone);
502 // draw subsequent entities with same pen:
503 for(Entity * e=nextEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone))
504 view->drawEntityPlain(e);
508 * Dumps the point's data to stdout.
510 std::ostream & operator<<(std::ostream & os, const Polyline & l)
512 os << " Polyline: " << l.getData() << " {\n";
513 os << (EntityContainer &)l;