]> Shamusworld >> Repos - architektonas/blob - src/base/rs_polyline.cpp
GPL compliance check...
[architektonas] / src / base / rs_polyline.cpp
1 // rs_polyline.cpp
2 //
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
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  06/01/2010  Added this text. :-)
15 //
16
17 #include "rs_polyline.h"
18
19 #include "rs_debug.h"
20 #include "rs_line.h"
21 #include "rs_arc.h"
22 #include "graphicview.h"
23 #include "paintinterface.h"
24
25 /**
26  * Constructor.
27  */
28 RS_Polyline::RS_Polyline(RS_EntityContainer * parent): RS_EntityContainer(parent),
29         closingEntity(NULL), nextBulge(0.0)
30 {
31 }
32
33 /**
34  * Constructor.
35  * @param d Polyline data
36  */
37 RS_Polyline::RS_Polyline(RS_EntityContainer* parent, const RS_PolylineData& d):
38         RS_EntityContainer(parent), data(d)
39 {
40     closingEntity = NULL;
41     nextBulge = 0.0;
42     calculateBorders();
43 }
44
45 /**
46  * Destructor
47  */
48 RS_Polyline::~RS_Polyline()
49 {
50 }
51
52 /*virtual*/ RS_Entity * RS_Polyline::clone()
53 {
54         RS_Polyline * p = new RS_Polyline(*this);
55 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
56 //      p->entities.setAutoDelete(entities.autoDelete());
57         p->initId();
58         p->detach();
59         return p;
60 }
61
62 /**     @return RS2::EntityPolyline */
63 /*virtual*/ RS2::EntityType RS_Polyline::rtti() const
64 {
65         return RS2::EntityPolyline;
66 }
67
68 /** @return Copy of data that defines the polyline. */
69 RS_PolylineData RS_Polyline::getData() const
70 {
71         return data;
72 }
73
74 /** sets a new start point of the polyline */
75 void RS_Polyline::setStartpoint(Vector & v)
76 {
77         data.startpoint = v;
78
79         if (!data.endpoint.valid)
80                 data.endpoint = v;
81 }
82
83 /** @return Start point of the entity */
84 Vector RS_Polyline::getStartpoint()
85 {
86         return data.startpoint;
87 }
88
89 /** sets a new end point of the polyline */
90 void RS_Polyline::setEndpoint(Vector & v)
91 {
92         data.endpoint = v;
93 }
94
95 /** @return End point of the entity */
96 Vector RS_Polyline::getEndpoint()
97 {
98         return data.endpoint;
99 }
100
101 /**
102  * Removes the last vertex of this polyline.
103  */
104 void RS_Polyline::removeLastVertex()
105 {
106         RS_Entity * last = lastEntity();
107
108         if (last != NULL)
109         {
110                 removeEntity(last);
111                 last = lastEntity();
112
113                 if (last != NULL)
114                 {
115                         if (last->isAtomic())
116                                 data.endpoint = ((RS_AtomicEntity*)last)->getEndpoint();
117                         else
118                                 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Polyline::removeLastVertex: "
119                                         "polyline contains non-atomic entity");
120                 }
121         }
122 }
123
124 /**
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'.
128  *
129  * The very first vertex added with this method is the startpoint.
130  *
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
134  *
135  * @return Pointer to the entity that was addded or NULL if this
136  *         was the first vertex added.
137  */
138 RS_Entity * RS_Polyline::addVertex(const Vector & v, double bulge, bool prepend)
139 {
140     RS_Entity * entity = NULL;
141     //static double nextBulge = 0.0;
142
143     // very first vertex:
144     if (!data.startpoint.valid)
145         {
146         data.startpoint = data.endpoint = v;
147         nextBulge = bulge;
148     }
149
150     // consequent vertices:
151     else {
152         // add entity to the polyline:
153         entity = createVertex(v, nextBulge, prepend);
154         if (entity!=NULL) {
155                         if (prepend==false) {
156                 RS_EntityContainer::addEntity(entity);
157                                 data.endpoint = v;
158                         }
159                         else {
160                 RS_EntityContainer::insertEntity(0, entity);
161                                 data.startpoint = v;
162                         }
163         }
164         nextBulge = bulge;
165         endPolyline();
166     }
167     //data.endpoint = v;
168
169     return entity;
170 }
171
172
173
174 /**
175  * Creates a vertex from the endpoint of the last element or
176  * sets the startpoint to the point 'v'.
177  *
178  * The very first vertex added is the starting point.
179  *
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
183  *
184  * @return Pointer to the entity that was created or NULL if this
185  *         was the first vertex added.
186  */
187 RS_Entity* RS_Polyline::createVertex(const Vector& v, double bulge, bool prepend) {
188
189     RS_Entity* entity=NULL;
190
191     RS_DEBUG->print("RS_Polyline::createVertex: %f/%f to %f/%f bulge: %f",
192                     data.endpoint.x, data.endpoint.y, v.x, v.y, bulge);
193
194     // create line for the polyline:
195     if (fabs(bulge)<RS_TOLERANCE) {
196                 if (prepend==false) {
197                 entity = new RS_Line(this, RS_LineData(data.endpoint, v));
198                 }
199                 else {
200                 entity = new RS_Line(this, RS_LineData(v, data.startpoint));
201                 }
202         entity->setSelected(isSelected());
203         entity->setPen(RS_Pen(RS2::FlagInvalid));
204         entity->setLayer(NULL);
205         //RS_EntityContainer::addEntity(entity);
206         //data.endpoint = v;
207     }
208
209     // create arc for the polyline:
210     else {
211         bool reversed = (bulge<0.0);
212         double alpha = atan(bulge)*4.0;
213
214         double radius;
215         Vector center;
216         Vector middle;
217         double dist;
218         double angle;
219
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);
224                 }
225                 else {
226                 middle = (data.startpoint+v)/2.0;
227             dist = data.startpoint.distanceTo(v)/2.0;
228                 angle = v.angleTo(data.startpoint);
229                 }
230
231         // alpha can't be 0.0 at this point
232         radius = fabs(dist / sin(alpha/2.0));
233
234         double wu = fabs(RS_Math::pow(radius, 2.0) - RS_Math::pow(dist, 2.0));
235         double h = sqrt(wu);
236
237         if (bulge>0.0) {
238             angle+=M_PI/2.0;
239         } else {
240             angle-=M_PI/2.0;
241         }
242
243         if (fabs(alpha)>M_PI) {
244             h*=-1.0;
245         }
246
247         center.setPolar(h, angle);
248         center+=middle;
249
250                 double a1;
251                 double a2;
252
253                 if (prepend==false) {
254                         a1 = center.angleTo(data.endpoint);
255                         a2 = center.angleTo(v);
256                 }
257                 else {
258                         a1 = center.angleTo(v);
259                         a2 = center.angleTo(data.startpoint);
260                 }
261
262         RS_ArcData d(center, radius,
263                      a1, a2,
264                      reversed);
265
266         entity = new RS_Arc(this, d);
267         entity->setSelected(isSelected());
268         entity->setPen(RS_Pen(RS2::FlagInvalid));
269         entity->setLayer(NULL);
270     }
271
272     return entity;
273 }
274
275 /**
276  * Ends polyline and adds the last entity if the polyline is closed
277  */
278 void RS_Polyline::endPolyline()
279 {
280         RS_DEBUG->print("RS_Polyline::endPolyline");
281
282     if (isClosed())
283         {
284                 RS_DEBUG->print("RS_Polyline::endPolyline: adding closing entity");
285
286         // remove old closing entity:
287         if (closingEntity!=NULL)
288                 {
289             removeEntity(closingEntity);
290         }
291
292         // add closing entity to the polyline:
293         closingEntity = createVertex(data.startpoint, nextBulge);
294         if (closingEntity!=NULL)
295                 {
296             RS_EntityContainer::addEntity(closingEntity);
297             //data.endpoint = data.startpoint;
298         }
299     }
300 }
301
302 /**
303  * @return The bulge of the closing entity.
304  */
305 double RS_Polyline::getClosingBulge()
306 {
307     if (isClosed()) {
308                 RS_Entity* e = lastEntity();
309                 if (e!=NULL && e->rtti()==RS2::EntityArc) {
310                         return ((RS_Arc*)e)->getBulge();
311                 }
312         }
313
314         return 0.0;
315 }
316
317 /**
318  * Sets the polylines start and endpoint to match the first and last vertex.
319  */
320 void RS_Polyline::updateEndpoints()
321 {
322         RS_Entity * e1 = firstEntity();
323
324         if (e1 != NULL && e1->isAtomic())
325         {
326                 Vector v = ((RS_AtomicEntity *)e1)->getStartpoint();
327                 setStartpoint(v);
328         }
329
330         RS_Entity * e2 = lastEntity();
331
332         if (isClosed())
333         {
334                 e2 = prevEntity();
335         }
336
337         if (e2 != NULL && e2->isAtomic())
338         {
339                 Vector v = ((RS_AtomicEntity *)e2)->getEndpoint();
340                 setEndpoint(v);
341         }
342 }
343
344 /** @return true if the polyline is closed. false otherwise */
345 bool RS_Polyline::isClosed() const
346 {
347         return data.getFlag(RS2::FlagClosed);
348 }
349
350 void RS_Polyline::setClosed(bool cl)
351 {
352         if (cl)
353                 data.setFlag(RS2::FlagClosed);
354         else
355                 data.delFlag(RS2::FlagClosed);
356 }
357
358 /**
359  * Reimplementation of the addEntity method for a normal container.
360  * This reimplementation deletes the given entity!
361  *
362  * To add entities use addVertex() or addSegment() instead.
363  */
364 void RS_Polyline::addEntity(RS_Entity * entity)
365 {
366         RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Polyline::addEntity: should never be called");
367
368         if (entity == NULL)
369                 return;
370
371         delete entity;
372 }
373
374 /*virtual*/ void RS_Polyline::setNextBulge(double bulge)
375 {
376         nextBulge = bulge;
377 }
378
379 /**
380  * Adds a segment to the polyline.
381  */
382 /*void RS_Polyline::addSegment(RS_Entity* entity) {
383         RS_EntityContainer::addEntity(entity);
384         // TODO: reorder and check polyline
385 }*/
386
387 VectorSolutions RS_Polyline::getRefPoints()
388 {
389     VectorSolutions ret(count()+1);
390
391     int i=0;
392     ret.set(0, data.startpoint);
393         i++;
394
395         for (RS_Entity* e=firstEntity(RS2::ResolveNone);
396             e!=NULL;
397             e = nextEntity(RS2::ResolveNone), i++) {
398                 if (e->isAtomic()) {
399                 ret.set(i, ((RS_AtomicEntity*)e)->getEndpoint());
400                 }
401     }
402
403         ret.set(count(), data.endpoint);
404
405     return ret;
406 }
407
408 Vector RS_Polyline::getNearestRef(const Vector& coord,
409                                    double* dist) {
410
411     return RS_Entity::getNearestRef(coord, dist);
412 }
413
414 Vector RS_Polyline::getNearestSelectedRef(const Vector& coord,
415         double* dist) {
416
417     return RS_Entity::getNearestSelectedRef(coord, dist);
418 }
419
420
421
422 /*
423 void RS_Polyline::reorder() {
424         // current point:
425         Vector cp;
426
427         bool done = false;
428         do {
429
430         } while(!done);
431 }
432 */
433
434
435
436 void RS_Polyline::move(Vector offset) {
437     RS_EntityContainer::move(offset);
438     data.startpoint.move(offset);
439     data.endpoint.move(offset);
440 }
441
442
443
444 void RS_Polyline::rotate(Vector center, double angle) {
445     RS_EntityContainer::rotate(center, angle);
446     data.startpoint.rotate(center, angle);
447     data.endpoint.rotate(center, angle);
448 }
449
450
451
452 void RS_Polyline::scale(Vector center, Vector factor) {
453     RS_EntityContainer::scale(center, factor);
454     data.startpoint.scale(center, factor);
455     data.endpoint.scale(center, factor);
456 }
457
458
459
460 void RS_Polyline::mirror(Vector axisPoint1, Vector axisPoint2) {
461     RS_EntityContainer::mirror(axisPoint1, axisPoint2);
462     data.startpoint.mirror(axisPoint1, axisPoint2);
463     data.endpoint.mirror(axisPoint1, axisPoint2);
464 }
465
466 void RS_Polyline::moveRef(const Vector & ref, const Vector & offset)
467 {
468         RS_EntityContainer::moveRef(ref, offset);
469
470         if (ref.distanceTo(data.startpoint) < 1.0e-4)
471                 data.startpoint.move(offset);
472
473         if (ref.distanceTo(data.endpoint) < 1.0e-4)
474                 data.endpoint.move(offset);
475
476         //update();
477 }
478
479 void RS_Polyline::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
480 {
481         if (data.startpoint.isInWindow(firstCorner, secondCorner))
482                 data.startpoint.move(offset);
483
484         if (data.endpoint.isInWindow(firstCorner, secondCorner))
485                 data.endpoint.move(offset);
486
487         RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
488 }
489
490 /**
491  * Slightly optimized drawing for polylines.
492  */
493 void RS_Polyline::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
494 {
495         if (!painter || !view)
496                 return;
497
498         // draw first entity and set correct pen:
499         RS_Entity * e = firstEntity(RS2::ResolveNone);
500         view->drawEntity(e);
501
502         // draw subsequent entities with same pen:
503         for(RS_Entity * e=nextEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone))
504                 view->drawEntityPlain(e);
505 }
506
507 /**
508  * Dumps the point's data to stdout.
509  */
510 std::ostream & operator<<(std::ostream & os, const RS_Polyline & l)
511 {
512         os << " Polyline: " << l.getData() << " {\n";
513         os << (RS_EntityContainer &)l;
514         os << "\n}\n";
515
516         return os;
517 }