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