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