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