1
/****************************************************************************
3
** This file is part of the LibreCAD project, a 2D CAD program
5
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
9
** This program is free software; you can redistribute it and/or modify
10
** it under the terms of the GNU General Public License as published by
11
** the Free Software Foundation; either version 2 of the License, or
12
** (at your option) any later version.
14
** This program is distributed in the hope that it will be useful,
15
** but WITHOUT ANY WARRANTY; without even the implied warranty of
16
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
** GNU General Public License for more details.
19
** You should have received a copy of the GNU General Public License
20
** along with this program; if not, write to the Free Software
21
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
** This copyright notice MUST APPEAR in all copies of the script!
25
**********************************************************************/
28
#include "rs_polyline.h"
33
#include "rs_graphicview.h"
39
RS_Polyline::RS_Polyline(RS_EntityContainer* parent)
40
:RS_EntityContainer(parent) {
49
* @param d Polyline data
51
RS_Polyline::RS_Polyline(RS_EntityContainer* parent,
52
const RS_PolylineData& d)
53
:RS_EntityContainer(parent), data(d) {
64
RS_Polyline::~RS_Polyline() {}
68
* Removes the last vertex of this polyline.
70
void RS_Polyline::removeLastVertex() {
71
RS_Entity* last = lastEntity();
76
if (last->isAtomic()) {
77
data.endpoint = ((RS_AtomicEntity*)last)->getEndpoint();
80
RS_DEBUG->print(RS_Debug::D_WARNING,
81
"RS_Polyline::removeLastVertex: "
82
"polyline contains non-atomic entity");
90
* Adds a vertex from the endpoint of the last segment or
91
* from the startpoint of the first segment to 'v' or
92
* sets the startpoint to the point 'v'.
94
* The very first vertex added with this method is the startpoint.
96
* @param v vertex coordinate to be added
97
* @param bulge The bulge of the arc or 0 for a line segment (see DXF documentation)
98
* @param prepend true: prepend at start instead of append at end
100
* @return Pointer to the entity that was addded or NULL if this
101
* was the first vertex added.
103
RS_Entity* RS_Polyline::addVertex(const RS_Vector& v, double bulge, bool prepend) {
105
RS_Entity* entity=NULL;
106
//static double nextBulge = 0.0;
108
// very first vertex:
109
if (!data.startpoint.valid) {
110
data.startpoint = data.endpoint = v;
114
// consequent vertices:
116
// add entity to the polyline:
117
entity = createVertex(v, nextBulge, prepend);
119
if (prepend==false) {
120
RS_EntityContainer::addEntity(entity);
124
RS_EntityContainer::insertEntity(0, entity);
139
* Creates a vertex from the endpoint of the last element or
140
* sets the startpoint to the point 'v'.
142
* The very first vertex added is the starting point.
144
* @param v vertex coordinate
145
* @param bulge The bulge of the arc (see DXF documentation)
146
* @param prepend true: Prepend instead of append at end
148
* @return Pointer to the entity that was created or NULL if this
149
* was the first vertex added.
151
RS_Entity* RS_Polyline::createVertex(const RS_Vector& v, double bulge, bool prepend) {
153
RS_Entity* entity=NULL;
155
RS_DEBUG->print("RS_Polyline::createVertex: %f/%f to %f/%f bulge: %f",
156
data.endpoint.x, data.endpoint.y, v.x, v.y, bulge);
158
// create line for the polyline:
159
if (fabs(bulge)<RS_TOLERANCE) {
160
if (prepend==false) {
161
entity = new RS_Line(this, RS_LineData(data.endpoint, v));
164
entity = new RS_Line(this, RS_LineData(v, data.startpoint));
166
entity->setSelected(isSelected());
167
entity->setPen(RS_Pen(RS2::FlagInvalid));
168
entity->setLayer(NULL);
169
//RS_EntityContainer::addEntity(entity);
173
// create arc for the polyline:
175
bool reversed = (bulge<0.0);
176
double alpha = atan(bulge)*4.0;
184
if (prepend==false) {
185
middle = (data.endpoint+v)/2.0;
186
dist = data.endpoint.distanceTo(v)/2.0;
187
angle = data.endpoint.angleTo(v);
190
middle = (data.startpoint+v)/2.0;
191
dist = data.startpoint.distanceTo(v)/2.0;
192
angle = v.angleTo(data.startpoint);
195
// alpha can't be 0.0 at this point
196
radius = fabs(dist / sin(alpha/2.0));
198
double wu = fabs(RS_Math::pow(radius, 2.0) - RS_Math::pow(dist, 2.0));
207
if (fabs(alpha)>M_PI) {
211
center.setPolar(h, angle);
217
if (prepend==false) {
218
a1 = center.angleTo(data.endpoint);
219
a2 = center.angleTo(v);
222
a1 = center.angleTo(v);
223
a2 = center.angleTo(data.startpoint);
226
RS_ArcData d(center, radius,
230
entity = new RS_Arc(this, d);
231
entity->setSelected(isSelected());
232
entity->setPen(RS_Pen(RS2::FlagInvalid));
233
entity->setLayer(NULL);
241
* Ends polyline and adds the last entity if the polyline is closed
243
void RS_Polyline::endPolyline() {
244
RS_DEBUG->print("RS_Polyline::endPolyline");
247
RS_DEBUG->print("RS_Polyline::endPolyline: adding closing entity");
249
// remove old closing entity:
250
if (closingEntity!=NULL) {
251
removeEntity(closingEntity);
254
// add closing entity to the polyline:
255
closingEntity = createVertex(data.startpoint, nextBulge);
256
if (closingEntity!=NULL) {
257
RS_EntityContainer::addEntity(closingEntity);
258
//data.endpoint = data.startpoint;
266
* @return The bulge of the closing entity.
268
double RS_Polyline::getClosingBulge() {
270
RS_Entity* e = lastEntity();
271
if (e!=NULL && e->rtti()==RS2::EntityArc) {
272
return ((RS_Arc*)e)->getBulge();
281
* Sets the polylines start and endpoint to match the first and last vertex.
283
void RS_Polyline::updateEndpoints() {
284
RS_Entity* e1 = firstEntity();
285
if (e1!=NULL && e1->isAtomic()) {
286
RS_Vector v = ((RS_AtomicEntity*)e1)->getStartpoint();
290
RS_Entity* e2 = lastEntity();
294
if (e2!=NULL && e2->isAtomic()) {
295
RS_Vector v = ((RS_AtomicEntity*)e2)->getEndpoint();
303
* Reimplementation of the addEntity method for a normal container.
304
* This reimplementation deletes the given entity!
306
* To add entities use addVertex() or addSegment() instead.
308
void RS_Polyline::addEntity(RS_Entity* entity) {
309
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Polyline::addEntity:"
310
" should never be called");
320
* Adds a segment to the polyline.
322
/*void RS_Polyline::addSegment(RS_Entity* entity) {
323
RS_EntityContainer::addEntity(entity);
324
// TODO: reorder and check polyline
329
RS_VectorSolutions RS_Polyline::getRefPoints() {
330
RS_VectorSolutions ret(count()+1);
333
ret.set(0, data.startpoint);
336
for (RS_Entity* e=firstEntity(RS2::ResolveNone);
338
e = nextEntity(RS2::ResolveNone), i++) {
340
ret.set(i, ((RS_AtomicEntity*)e)->getEndpoint());
344
ret.set(count(), data.endpoint);
349
RS_Vector RS_Polyline::getNearestRef(const RS_Vector& coord,
352
return RS_Entity::getNearestRef(coord, dist);
355
RS_Vector RS_Polyline::getNearestSelectedRef(const RS_Vector& coord,
358
return RS_Entity::getNearestSelectedRef(coord, dist);
364
void RS_Polyline::reorder() {
377
void RS_Polyline::move(RS_Vector offset) {
378
RS_EntityContainer::move(offset);
379
data.startpoint.move(offset);
380
data.endpoint.move(offset);
385
void RS_Polyline::rotate(RS_Vector center, double angle) {
386
RS_EntityContainer::rotate(center, angle);
387
data.startpoint.rotate(center, angle);
388
data.endpoint.rotate(center, angle);
393
void RS_Polyline::scale(RS_Vector center, RS_Vector factor) {
394
RS_EntityContainer::scale(center, factor);
395
data.startpoint.scale(center, factor);
396
data.endpoint.scale(center, factor);
401
void RS_Polyline::mirror(RS_Vector axisPoint1, RS_Vector axisPoint2) {
402
RS_EntityContainer::mirror(axisPoint1, axisPoint2);
403
data.startpoint.mirror(axisPoint1, axisPoint2);
404
data.endpoint.mirror(axisPoint1, axisPoint2);
409
void RS_Polyline::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
410
RS_EntityContainer::moveRef(ref, offset);
411
if (ref.distanceTo(data.startpoint)<1.0e-4) {
412
data.startpoint.move(offset);
414
if (ref.distanceTo(data.endpoint)<1.0e-4) {
415
data.endpoint.move(offset);
422
void RS_Polyline::stretch(RS_Vector firstCorner,
423
RS_Vector secondCorner,
426
if (data.startpoint.isInWindow(firstCorner, secondCorner)) {
427
data.startpoint.move(offset);
429
if (data.endpoint.isInWindow(firstCorner, secondCorner)) {
430
data.endpoint.move(offset);
433
RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
438
* Slightly optimized drawing for polylines.
440
void RS_Polyline::draw(RS_Painter* painter,RS_GraphicView* view,
441
double /*patternOffset*/) {
447
// draw first entity and set correct pen:
448
RS_Entity* e = firstEntity(RS2::ResolveNone);
449
view->drawEntity(painter, e);
451
// draw subsequent entities with same pen:
452
for (RS_Entity* e=nextEntity(RS2::ResolveNone);
454
e = nextEntity(RS2::ResolveNone)) {
456
view->drawEntityPlain(painter, e);
463
* Dumps the point's data to stdout.
465
std::ostream& operator << (std::ostream& os, const RS_Polyline& l) {
466
os << " Polyline: " << l.getData() << " {\n";
468
os << (RS_EntityContainer&)l;