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