]> Shamusworld >> Repos - architektonas/blob - src/base/circle.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / circle.cpp
1 // circle.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  05/28/2010  Added this text. :-)
15 //
16
17 #include "circle.h"
18
19 #include "constructionline.h"
20 #include "debug.h"
21 #include "graphicview.h"
22 #include "information.h"
23 #include "linetypepattern.h"
24 #include "paintinterface.h"
25
26 /**
27  * Default constructor.
28  */
29 Circle::Circle(EntityContainer * parent, const CircleData & d):
30         AtomicEntity(parent), data(d)
31 {
32         calculateBorders();
33 }
34
35 /*virtual*/ Circle::~Circle()
36 {
37 }
38
39 /*virtual*/ Entity * Circle::clone()
40 {
41         Circle * c = new Circle(*this);
42         c->initId();
43         return c;
44 }
45
46 /**     @return RS2::EntityCircle */
47 /*virtual*/ RS2::EntityType Circle::rtti() const
48 {
49         return RS2::EntityCircle;
50 }
51
52 /** @return true */
53 /*virtual*/ bool Circle::isEdge() const
54 {
55         return true;
56 }
57
58 /** @return Copy of data that defines the circle. **/
59 CircleData Circle::getData()
60 {
61         return data;
62 }
63
64 VectorSolutions Circle::getRefPoints()
65 {
66         Vector v1(data.radius, 0.0);
67         Vector v2(0.0, data.radius);
68
69         VectorSolutions ret(data.center, data.center + v1, data.center + v2, data.center - v1, data.center - v2);
70         return ret;
71 }
72
73 /*virtual*/ Vector Circle::getStartpoint() const
74 {
75         return data.center + Vector(data.radius, 0.0);
76 }
77
78 /*virtual*/ Vector Circle::getEndpoint() const
79 {
80         return data.center + Vector(data.radius, 0.0);
81 }
82
83 /**
84         * @return Direction 1. The angle at which the arc starts at
85         * the startpoint.
86         */
87 double Circle::getDirection1() const
88 {
89         return M_PI / 2.0;
90 }
91
92 /**
93         * @return Direction 2. The angle at which the arc starts at
94         * the endpoint.
95         */
96 double Circle::getDirection2() const
97 {
98         return M_PI / 2.0 * 3.0;
99 }
100
101 /** @return The center point (x) of this arc */
102 Vector Circle::getCenter()
103 {
104         return data.center;
105 }
106
107 /** Sets new center. */
108 void Circle::setCenter(const Vector & c)
109 {
110         data.center = c;
111 }
112
113 /** @return The radius of this arc */
114 double Circle::getRadius()
115 {
116         return data.radius;
117 }
118
119 /** Sets new radius. */
120 void Circle::setRadius(double r)
121 {
122         data.radius = r;
123 }
124
125 void Circle::calculateBorders()
126 {
127         Vector r(data.radius, data.radius, 0.0);
128         minV = data.center - r;
129         maxV = data.center + r;
130 }
131
132 /**
133  * @return Angle length in rad.
134  */
135 double Circle::getAngleLength() const
136 {
137         return 2 * M_PI;
138 }
139
140 /**
141  * @return Length of the circle which is the circumference.
142  */
143 double Circle::getLength()
144 {
145         return 2 * M_PI * data.radius;
146 }
147
148 /**
149  * Creates this circle from a center point and a radius.
150  *
151  * @param c Center.
152  * @param r Radius
153  */
154 bool Circle::createFromCR(const Vector & c, double r)
155 {
156         if (fabs(r) > RS_TOLERANCE)
157         {
158                 data.radius = fabs(r);
159                 data.center = c;
160                 return true;
161         }
162         else
163         {
164                 DEBUG->print(Debug::D_WARNING, "Circle::createFromCR(): "
165                         "Cannot create a circle with radius 0.0.");
166                 return false;
167         }
168 }
169
170 /**
171  * Creates this circle from two opposite points.
172  *
173  * @param p1 1st point.
174  * @param p2 2nd point.
175  */
176 bool Circle::createFrom2P(const Vector & p1, const Vector & p2)
177 {
178         if (p1.distanceTo(p2) > RS_TOLERANCE)
179         {
180                 data.radius = p1.distanceTo(p2) / 2.0;
181                 data.center = p1 + (p2 - p1) / 2.0;
182                 return true;
183         }
184         else
185         {
186                 DEBUG->print(Debug::D_WARNING, "Circle::createFrom2P(): "
187                         "Cannot create a circle with radius 0.0.");
188                 return false;
189         }
190 }
191
192 /**
193  * Creates this circle from 3 given points which define the circle line.
194  *
195  * @param p1 1st point.
196  * @param p2 2nd point.
197  * @param p3 3rd point.
198  */
199 bool Circle::createFrom3P(const Vector & p1, const Vector & p2, const Vector & p3)
200 {
201         if (p1.distanceTo(p2) > RS_TOLERANCE
202             && p2.distanceTo(p3) > RS_TOLERANCE
203             && p3.distanceTo(p1) > RS_TOLERANCE)
204         {
205                 // middle points between 3 points:
206                 Vector mp1, mp2;
207                 Vector dir1, dir2;
208                 double a1, a2;
209
210                 // intersection of two middle lines
211                 mp1 = (p1 + p2) / 2.0;
212                 a1 = p1.angleTo(p2) + M_PI / 2.0;
213                 dir1.setPolar(100.0, a1);
214                 mp2 = (p2 + p3) / 2.0;
215                 a2 = p2.angleTo(p3) + M_PI / 2.0;
216                 dir2.setPolar(100.0, a2);
217
218                 ConstructionLineData d1(mp1, mp1 + dir1);
219                 ConstructionLineData d2(mp2, mp2 + dir2);
220                 ConstructionLine midLine1(NULL, d1);
221                 ConstructionLine midLine2(NULL, d2);
222
223                 VectorSolutions sol =
224                         Information::getIntersection(&midLine1, &midLine2);
225
226                 data.center = sol.get(0);
227                 data.radius = data.center.distanceTo(p3);
228
229                 if (sol.get(0).valid && data.radius < 1.0e14 && data.radius > RS_TOLERANCE)
230                         return true;
231                 else
232                 {
233                         DEBUG->print(Debug::D_WARNING, "Circle::createFrom3P(): "
234                                 "Cannot create a circle with inf radius.");
235                         return false;
236                 }
237         }
238         else
239         {
240                 DEBUG->print(Debug::D_WARNING, "Circle::createFrom3P(): "
241                         "Cannot create a circle with radius 0.0.");
242                 return false;
243         }
244 }
245
246 /**
247  * @return Always an invalid vector.
248  */
249 Vector Circle::getNearestEndpoint(const Vector & /*coord*/, double * dist)
250 {
251         if (dist != NULL)
252                 *dist = RS_MAXDOUBLE;
253         return Vector(false);
254 }
255
256 Vector Circle::getNearestPointOnEntity(const Vector & coord, bool /*onEntity*/, double * dist, Entity ** entity)
257 {
258         Vector vec(false);
259
260         if (entity != NULL)
261                 *entity = this;
262
263         double angle = (coord - data.center).angle();
264         vec.setPolar(data.radius, angle);
265         vec += data.center;
266
267         if (dist != NULL)
268                 *dist = fabs((vec - data.center).magnitude() - data.radius);
269
270         return vec;
271 }
272
273 Vector Circle::getNearestCenter(const Vector & coord, double * dist)
274 {
275         if (dist != NULL)
276                 *dist = coord.distanceTo(data.center);
277         return data.center;
278 }
279
280 Vector Circle::getNearestMiddle(const Vector & /*coord*/, double * dist)
281 {
282         if (dist != NULL)
283                 *dist = RS_MAXDOUBLE;
284         return Vector(false);
285 }
286
287 Vector Circle::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
288 {
289         if (dist != NULL)
290                 *dist = RS_MAXDOUBLE;
291         return Vector(false);
292 }
293
294 Vector Circle::getNearestDist(double /*distance*/, bool /*startp*/)
295 {
296         return Vector(false);
297 }
298
299 double Circle::getDistanceToPoint(const Vector & coord, Entity * * entity, RS2::ResolveLevel, double)
300 {
301         if (entity != NULL)
302                 *entity = this;
303
304         return fabs((coord - data.center).magnitude() - data.radius);
305 }
306
307 void Circle::move(Vector offset)
308 {
309         data.center.move(offset);
310         calculateBorders();
311 }
312
313 void Circle::rotate(Vector center, double angle)
314 {
315         data.center.rotate(center, angle);
316         calculateBorders();
317 }
318
319 void Circle::scale(Vector center, Vector factor)
320 {
321         data.center.scale(center, factor);
322         data.radius *= factor.x;
323         calculateBorders();
324 }
325
326 void Circle::mirror(Vector axisPoint1, Vector axisPoint2)
327 {
328         data.center.mirror(axisPoint1, axisPoint2);
329         calculateBorders();
330 }
331
332 void Circle::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
333 {
334         if (painter == NULL || view == NULL)
335                 return;
336
337         // simple style-less lines
338         if (getPen().getLineType() == RS2::SolidLine
339             || isSelected()
340             || view->getDrawingMode() == RS2::ModePreview)
341
342                 painter->drawArc(view->toGui(getCenter()),
343                         getRadius() * view->getFactor().x,
344                         0.0, 2 * M_PI,
345                         false);
346         else
347         {
348                 double styleFactor = getStyleFactor(view);
349
350                 if (styleFactor < 0.0)
351                 {
352                         painter->drawArc(view->toGui(getCenter()),
353                                 getRadius() * view->getFactor().x,
354                                 0.0, 2 * M_PI,
355                                 false);
356                         return;
357                 }
358
359                 // Pattern:
360                 LineTypePattern * pat;
361
362                 if (isSelected())
363                         pat = &patternSelected;
364                 else
365                         pat = view->getPattern(getPen().getLineType());
366
367                 if (pat == NULL)
368                         return;
369
370                 if (getRadius() < 1.0e-6)
371                         return;
372
373                 // Pen to draw pattern is always solid:
374                 Pen pen = painter->getPen();
375                 pen.setLineType(RS2::SolidLine);
376                 painter->setPen(pen);
377
378                 double * da; // array of distances in x.
379                 int i;  // index counter
380
381                 double length = getAngleLength();
382
383                 // create pattern:
384                 da = new double[pat->num];
385
386                 for (i = 0; i < pat->num; ++i)
387                         da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
388
389                 double tot = 0.0;
390                 i = 0;
391                 bool done = false;
392                 double curA = 0.0;
393                 //double cx = getCenter().x * factor.x + offsetX;
394                 //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
395                 Vector cp = view->toGui(getCenter());
396                 double r = getRadius() * view->getFactor().x;
397
398                 do
399                 {
400                         if (pat->pattern[i] * styleFactor > 0.0)
401                         {
402                                 if (tot + fabs(da[i]) < length)
403                                         painter->drawArc(cp, r,
404                                                 curA,
405                                                 curA + da[i],
406                                                 false);
407                                 else
408                                         painter->drawArc(cp, r,
409                                                 curA,
410                                                 2 * M_PI,
411                                                 false);
412                         }
413                         curA += da[i];
414                         tot += fabs(da[i]);
415                         done = tot > length;
416
417                         i++;
418
419                         if (i >= pat->num)
420                                 i = 0;
421                 }
422                 while (!done);
423
424                 delete[] da;
425         }
426 }
427
428 void Circle::moveRef(const Vector & ref, const Vector & offset)
429 {
430         Vector v1(data.radius, 0.0);
431         Vector v2(0.0, data.radius);
432
433         if (ref.distanceTo(data.center + v1) < 1.0e-4)
434                 data.radius = data.center.distanceTo(data.center + v1 + offset);
435         else if (ref.distanceTo(data.center + v2) < 1.0e-4)
436                 data.radius = data.center.distanceTo(data.center + v2 + offset);
437         else if (ref.distanceTo(data.center - v1) < 1.0e-4)
438                 data.radius = data.center.distanceTo(data.center - v1 + offset);
439         else if (ref.distanceTo(data.center - v2) < 1.0e-4)
440                 data.radius = data.center.distanceTo(data.center - v2 + offset);
441 }
442
443 /**
444  * Dumps the circle's data to stdout.
445  */
446 std::ostream & operator<<(std::ostream & os, const Circle & a)
447 {
448         os << " Circle: " << a.data << "\n";
449         return os;
450 }