]> Shamusworld >> Repos - architektonas/blob - src/base/rs_creation.cpp
Adding missing implementation.
[architektonas] / src / base / rs_creation.cpp
1 // rs_creation.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_creation.h"
18
19 #include "rs_constructionline.h"
20 #include "drawing.h"
21 #include "graphicview.h"
22 #include "rs_image.h"
23 #include "rs_information.h"
24 #include "rs_insert.h"
25 #include "rs_modification.h"
26 #include "rs_units.h"
27
28 /**
29  * Default constructor.
30  *
31  * @param container The container to which we will add
32  *        entities. Usually that's an Drawing entity but
33  *        it can also be a polyline, text, ...
34  */
35 RS_Creation::RS_Creation(RS_EntityContainer * container, GraphicView * graphicView,
36         bool handleUndo)
37 {
38         this->container = container;
39         this->graphicView = graphicView;
40         this->handleUndo = handleUndo;
41
42         if (container != NULL)
43         {
44                 graphic = container->getGraphic();
45                 document = container->getDocument();
46         }
47         else
48         {
49                 graphic = NULL;
50                 document = NULL;
51         }
52 }
53
54 /**
55  * Creates a point entity.
56  *
57  * E.g.:<br>
58  * <code>
59  * creation.createPoint(Vector(10.0, 15.0));
60  * </code>
61  *
62  * @param p position
63  */
64 /*void RS_Creation::createPoint(const Vector& p) {
65     entityContainer->addEntity(new RS_Point(entityContainer, p));
66 }*/
67
68 /**
69  * Creates a line with two points given.
70  *
71  * E.g.:<br>
72  * <code>
73  * creation.createLine2P(Vector(10.0, 10.0), Vector(100.0, 200.0));
74  * </code>
75  *
76  * @param p1 start point
77  * @param p2 end point
78  */
79 /*void RS_Creation::createLine2P(const Vector& p1, const Vector& p2) {
80     entityContainer->addEntity(new RS_Line(entityContainer,
81                                            RS_LineData(p1, p2)));
82 }*/
83
84 /**
85  * Creates a rectangle with two edge points given.
86  *
87  * E.g.:<br>
88  * <code>
89  * creation.createRectangle(Vector(5.0, 2.0), Vector(7.5, 3.0));
90  * </code>
91  *
92  * @param p1 edge one
93  * @param p2 edge two
94  */
95 /*void RS_Creation::createRectangle(const Vector& e1, const Vector& e2) {
96     Vector e21(e2.x, e1.y);
97     Vector e12(e1.x, e2.y);
98     entityContainer->addEntity(new RS_Line(entityContainer,
99                                            RS_LineData(e1, e12)));
100     entityContainer->addEntity(new RS_Line(entityContainer,
101                                            RS_LineData(e12, e2)));
102     entityContainer->addEntity(new RS_Line(entityContainer,
103                                            RS_LineData(e2, e21)));
104     entityContainer->addEntity(new RS_Line(entityContainer,
105                                            RS_LineData(e21, e1)));
106 }*/
107
108 /**
109  * Creates a polyline from the given array of entities.
110  * No checking if the entities actually fit together.
111  * Currently this is like a group.
112  *
113  * E.g.:<br>
114  * <code>
115  * RS_Polyline *pl = creation.createPolyline(Vector(25.0, 55.0));<br>
116  * pl->addVertex(Vector(50.0, 75.0));<br>
117  * </code>
118  *
119  * @param entities array of entities
120  * @param startPoint Start point of the polyline
121  */
122 /*RS_Polyline* RS_Creation::createPolyline(const Vector& startPoint) {
123     RS_Polyline* pl = new RS_Polyline(entityContainer,
124                 RS_PolylineData(startPoint, Vector(0.0,0.0), 0));
125     entityContainer->addEntity(pl);
126     return pl;
127 }*/
128
129 /**
130  * Creates an entity parallel to the given entity e through the given
131  * 'coord'.
132  *
133  * @param coord Coordinate to define the distance / side (typically a
134  *              mouse coordinate).
135  * @param number Number of parallels.
136  * @param e Original entity.
137  *
138  * @return Pointer to the first created parallel or NULL if no
139  *    parallel has been created.
140  */
141 RS_Entity * RS_Creation::createParallelThrough(const Vector & coord, int number, RS_Entity * e)
142 {
143         if (e == NULL)
144                 return NULL;
145
146         double dist;
147
148         if (e->rtti() == RS2::EntityLine)
149         {
150                 RS_Line * l = (RS_Line *)e;
151                 RS_ConstructionLine cl(NULL, RS_ConstructionLineData(l->getStartpoint(),
152                         l->getEndpoint()));
153                 dist = cl.getDistanceToPoint(coord);
154         }
155         else
156         {
157                 dist = e->getDistanceToPoint(coord);
158         }
159
160         if (dist < RS_MAXDOUBLE)
161                 return createParallel(coord, dist, number, e);
162         else
163                 return NULL;
164 }
165
166 /**
167  * Creates an entity parallel to the given entity e.
168  * Out of the 2 possible parallels, the one closest to
169  * the given coordinate is returned.
170  * Lines, Arcs and Circles can have parallels.
171  *
172  * @param coord Coordinate to define which parallel we want (typically a
173  *              mouse coordinate).
174  * @param distance Distance of the parallel.
175  * @param number Number of parallels.
176  * @param e Original entity.
177  *
178  * @return Pointer to the first created parallel or NULL if no
179  *    parallel has been created.
180  */
181 RS_Entity* RS_Creation::createParallel(const Vector& coord,
182                                        double distance, int number,
183                                        RS_Entity* e) {
184     if (e==NULL) {
185         return NULL;
186     }
187
188     switch (e->rtti()) {
189     case RS2::EntityLine:
190         return createParallelLine(coord, distance, number, (RS_Line*)e);
191         break;
192
193     case RS2::EntityArc:
194         return createParallelArc(coord, distance, number, (RS_Arc*)e);
195         break;
196
197     case RS2::EntityCircle:
198         return createParallelCircle(coord, distance, number, (RS_Circle*)e);
199         break;
200
201     default:
202         break;
203     }
204
205     return NULL;
206 }
207
208
209
210 /**
211  * Creates a line parallel to the given line e.
212  * Out of the 2 possible parallels, the one closest to
213  * the given coordinate is returned.
214  *
215  * @param coord Coordinate to define which parallel we want (typically a
216  *              mouse coordinate).
217  * @param distance Distance of the parallel.
218  * @param number Number of parallels.
219  * @param e Original entity.
220  *
221  * @return Pointer to the first created parallel or NULL if no
222  *    parallel has been created.
223  */
224 RS_Line* RS_Creation::createParallelLine(const Vector& coord,
225         double distance, int number,
226         RS_Line* e) {
227
228     if (e==NULL) {
229         return NULL;
230     }
231
232     double ang = e->getAngle1() + M_PI/2.0;
233     Vector p1, p2;
234     RS_LineData parallelData;
235     RS_Line* ret = NULL;
236
237     if (document!=NULL && handleUndo) {
238         document->startUndoCycle();
239     }
240
241     for (int num=1; num<=number; ++num) {
242
243         // calculate 1st parallel:
244         p1.setPolar(distance*num, ang);
245         p1 += e->getStartpoint();
246         p2.setPolar(distance*num, ang);
247         p2 += e->getEndpoint();
248         RS_Line parallel1(NULL, RS_LineData(p1, p2));
249
250         // calculate 2nd parallel:
251         p1.setPolar(distance*num, ang+M_PI);
252         p1 += e->getStartpoint();
253         p2.setPolar(distance*num, ang+M_PI);
254         p2 += e->getEndpoint();
255         RS_Line parallel2(NULL, RS_LineData(p1, p2));
256
257         double dist1 = parallel1.getDistanceToPoint(coord);
258         double dist2 = parallel2.getDistanceToPoint(coord);
259         double minDist = std::min(dist1, dist2);
260
261         if (minDist<RS_MAXDOUBLE) {
262             if (dist1<dist2) {
263                 parallelData = parallel1.getData();
264             } else {
265                 parallelData = parallel2.getData();
266             }
267
268
269             RS_Line* newLine = new RS_Line(container, parallelData);
270             newLine->setLayerToActive();
271             newLine->setPenToActive();
272             if (ret==NULL) {
273                 ret = newLine;
274             }
275             if (container!=NULL) {
276                 container->addEntity(newLine);
277             }
278             if (document!=NULL && handleUndo) {
279                 document->addUndoable(newLine);
280                 //document->endUndoCycle();
281             }
282             if (graphicView!=NULL) {
283                 graphicView->drawEntity(newLine);
284             }
285         }
286     }
287
288     if (document!=NULL && handleUndo) {
289         document->endUndoCycle();
290     }
291
292     return ret;
293 }
294
295
296
297 /**
298  * Creates a arc parallel to the given arc e.
299  * Out of the 2 possible parallels, the one closest to
300  * the given coordinate is returned.
301  *
302  * @param coord Coordinate to define which parallel we want (typically a
303  *              mouse coordinate).
304  * @param distance Distance of the parallel.
305  * @param number Number of parallels.
306  * @param e Original entity.
307  *
308  * @return Pointer to the first created parallel or NULL if no
309  *    parallel has been created.
310  */
311 RS_Arc* RS_Creation::createParallelArc(const Vector& coord,
312                                        double distance, int number,
313                                        RS_Arc* e) {
314
315     if (e==NULL) {
316         return NULL;
317     }
318
319     RS_ArcData parallelData;
320     RS_Arc* ret = NULL;
321
322     bool inside = (e->getCenter().distanceTo(coord) < e->getRadius());
323
324     if (inside) {
325         distance *= -1;
326     }
327
328     for (int num=1; num<=number; ++num) {
329
330         // calculate parallel:
331         bool ok = true;
332         RS_Arc parallel1(NULL, e->getData());
333         parallel1.setRadius(e->getRadius() + distance*num);
334         if (parallel1.getRadius()<0.0) {
335             parallel1.setRadius(RS_MAXDOUBLE);
336             ok = false;
337         }
338
339         // calculate 2nd parallel:
340         //RS_Arc parallel2(NULL, e->getData());
341         //parallel2.setRadius(e->getRadius()+distance*num);
342
343         //double dist1 = parallel1.getDistanceToPoint(coord);
344         //double dist2 = parallel2.getDistanceToPoint(coord);
345         //double minDist = min(dist1, dist2);
346
347         //if (minDist<RS_MAXDOUBLE) {
348         if (ok==true) {
349             //if (dist1<dist2) {
350             parallelData = parallel1.getData();
351             //} else {
352             //    parallelData = parallel2.getData();
353             //}
354
355             if (document!=NULL && handleUndo) {
356                 document->startUndoCycle();
357             }
358
359             RS_Arc* newArc = new RS_Arc(container, parallelData);
360             newArc->setLayerToActive();
361             newArc->setPenToActive();
362             if (ret==NULL) {
363                 ret = newArc;
364             }
365             if (container!=NULL) {
366                 container->addEntity(newArc);
367             }
368             if (document!=NULL && handleUndo) {
369                 document->addUndoable(newArc);
370                 document->endUndoCycle();
371             }
372             if (graphicView!=NULL) {
373                 graphicView->drawEntity(newArc);
374             }
375         }
376     }
377
378     return ret;
379 }
380
381
382
383 /**
384  * Creates a circle parallel to the given circle e.
385  * Out of the 2 possible parallels, the one closest to
386  * the given coordinate is returned.
387  *
388  * @param coord Coordinate to define which parallel we want (typically a
389  *              mouse coordinate).
390  * @param distance Distance of the parallel.
391  * @param number Number of parallels.
392  * @param e Original entity.
393  *
394  * @return Pointer to the first created parallel or NULL if no
395  *    parallel has been created.
396  */
397 RS_Circle* RS_Creation::createParallelCircle(const Vector& coord,
398         double distance, int number,
399         RS_Circle* e) {
400
401     if (e==NULL) {
402         return NULL;
403     }
404
405     RS_CircleData parallelData;
406     RS_Circle* ret = NULL;
407
408     bool inside = (e->getCenter().distanceTo(coord) < e->getRadius());
409
410     if (inside) {
411         distance *= -1;
412     }
413
414     for (int num=1; num<=number; ++num) {
415
416         // calculate parallel:
417         bool ok = true;
418         RS_Circle parallel1(NULL, e->getData());
419         parallel1.setRadius(e->getRadius() + distance*num);
420         if (parallel1.getRadius()<0.0) {
421             parallel1.setRadius(RS_MAXDOUBLE);
422             ok = false;
423         }
424
425         // calculate 2nd parallel:
426         //RS_Circle parallel2(NULL, e->getData());
427         //parallel2.setRadius(e->getRadius()+distance*num);
428
429         //double dist1 = parallel1.getDistanceToPoint(coord);
430         //double dist2 = parallel2.getDistanceToPoint(coord);
431         //double minDist = min(dist1, dist2);
432
433         //if (minDist<RS_MAXDOUBLE) {
434         if (ok==true) {
435             //if (dist1<dist2) {
436             parallelData = parallel1.getData();
437             //} else {
438             //    parallelData = parallel2.getData();
439             //}
440
441             if (document!=NULL && handleUndo) {
442                 document->startUndoCycle();
443             }
444
445             RS_Circle* newCircle = new RS_Circle(container, parallelData);
446             newCircle->setLayerToActive();
447             newCircle->setPenToActive();
448             if (ret==NULL) {
449                 ret = newCircle;
450             }
451             if (container!=NULL) {
452                 container->addEntity(newCircle);
453             }
454             if (document!=NULL && handleUndo) {
455                 document->addUndoable(newCircle);
456                 document->endUndoCycle();
457             }
458             if (graphicView!=NULL) {
459                 graphicView->drawEntity(newCircle);
460             }
461         }
462     }
463     return ret;
464 }
465
466
467
468 /**
469  * Creates a bisecting line of the angle between the entities
470  * e1 and e2. Out of the 4 possible bisectors, the one closest to
471  * the given coordinate is returned.
472  *
473  * @param coord Coordinate to define which bisector we want (typically a
474  *              mouse coordinate).
475  * @param length Length of the bisecting line.
476  * @param num Number of bisectors
477  * @param l1 First line.
478  * @param l2 Second line.
479  *
480  * @return Pointer to the first bisector created or NULL if no bisectors
481  *   were created.
482  */
483 RS_Line* RS_Creation::createBisector(const Vector& coord1,
484                                      const Vector& coord2,
485                                      double length,
486                                      int num,
487                                      RS_Line* l1,
488                                      RS_Line* l2) {
489
490     VectorSolutions sol;
491
492     // check given entities:
493     if (l1==NULL || l2==NULL ||
494             l1->rtti()!=RS2::EntityLine || l2->rtti()!=RS2::EntityLine) {
495         return NULL;
496     }
497
498     // intersection between entities:
499     sol = RS_Information::getIntersection(l1, l2, false);
500     Vector inters = sol.get(0);
501     if (inters.valid==false) {
502         return NULL;
503     }
504
505     double angle1 = inters.angleTo(l1->getNearestPointOnEntity(coord1));
506     double angle2 = inters.angleTo(l2->getNearestPointOnEntity(coord2));
507     double angleDiff = RS_Math::getAngleDifference(angle1, angle2);
508     if (angleDiff>M_PI) {
509         angleDiff = angleDiff - 2*M_PI;
510     }
511     RS_Line* ret = NULL;
512
513     if (document!=NULL && handleUndo) {
514         document->startUndoCycle();
515     }
516
517     for (int n=1; n<=num; ++n) {
518
519         double angle = angle1 +
520                        (angleDiff / (num+1) * n);
521
522         RS_LineData d;
523         Vector v;
524
525         Vector c;
526         v.setPolar(length, angle);
527         d = RS_LineData(inters, inters + v);
528
529         RS_Line* newLine = new RS_Line(container, d);
530         if (container!=NULL) {
531             newLine->setLayerToActive();
532             newLine->setPenToActive();
533             container->addEntity(newLine);
534         }
535         if (document!=NULL && handleUndo) {
536             document->addUndoable(newLine);
537         }
538         if (graphicView!=NULL) {
539             graphicView->drawEntity(newLine);
540         }
541         if (ret==NULL) {
542             ret = newLine;
543         }
544     }
545     if (document!=NULL && handleUndo) {
546         document->endUndoCycle();
547     }
548
549     return ret;
550 }
551
552
553
554 /**
555  * Creates a tangent between a given point and a circle or arc.
556  * Out of the 2 possible tangents, the one closest to
557  * the given coordinate is returned.
558  *
559  * @param coord Coordinate to define which tangent we want (typically a
560  *              mouse coordinate).
561  * @param point Point.
562  * @param circle Circle, arc or ellipse entity.
563  */
564 RS_Line* RS_Creation::createTangent1(const Vector& coord,
565                                      const Vector& point,
566                                      RS_Entity* circle) {
567     RS_Line* ret = NULL;
568     Vector circleCenter;
569
570     // check given entities:
571     if (circle==NULL || !point.valid ||
572             (circle->rtti()!=RS2::EntityArc && circle->rtti()!=RS2::EntityCircle
573              && circle->rtti()!=RS2::EntityEllipse)) {
574
575         return NULL;
576     }
577
578     if (circle->rtti()==RS2::EntityCircle) {
579         circleCenter = ((RS_Circle*)circle)->getCenter();
580     } else if (circle->rtti()==RS2::EntityArc) {
581         circleCenter = ((RS_Arc*)circle)->getCenter();
582     } else if (circle->rtti()==RS2::EntityEllipse) {
583         circleCenter = ((RS_Ellipse*)circle)->getCenter();
584     }
585
586     // the two tangent points:
587     VectorSolutions sol;
588
589     // calculate tangent points for arcs / circles:
590     if (circle->rtti()!=RS2::EntityEllipse) {
591         // create temp. thales circle:
592         Vector tCenter = (point + circleCenter)/2.0;
593         double tRadius = point.distanceTo(tCenter);
594
595         RS_Circle tmp(NULL, RS_CircleData(tCenter, tRadius));
596
597         // get the two intersection points which are the tangent points:
598         sol = RS_Information::getIntersection(&tmp, circle, false);
599     }
600
601     // calculate tangent points for ellipses:
602     else {
603         RS_Ellipse* el = (RS_Ellipse*)circle;
604         sol.alloc(2);
605         //sol.set(0, circleCenter);
606         //sol.set(1, circleCenter);
607
608
609         double a = el->getMajorRadius();     // the length of the major axis / 2
610         double b = el->getMinorRadius();     // the length of the minor axis / 2
611
612                 // rotate and move point:
613                 Vector point2 = point;
614                 point2.move(-el->getCenter());
615                 point2.rotate(-el->getAngle());
616
617         double xp = point2.x;             // coordinates of the given point
618         double yp = point2.y;
619
620         double xt1;                      // Tangent point 1
621         double yt1;
622         double xt2;                      // Tangent point 2
623         double yt2;
624
625         double a2 = a * a;
626         double b2 = b * b;
627         double d = a2 / b2 * yp / xp;
628         double e = a2 / xp;
629         double af = b2 * d * d + a2;
630         double bf = -b2 * d * e * 2.0;
631         double cf = b2 * e * e - a2 * b2;
632         double t = sqrt(bf * bf - af * cf * 4.0);
633         yt1 = (t - bf) / (af * 2.0);
634         xt1 = e - d * yt1;
635         yt2 = (-t - bf) / (af * 2.0);
636         xt2 = e - d * yt2;
637
638                 Vector s1 = Vector(xt1, yt1);
639                 Vector s2 = Vector(xt2, yt2);
640
641                 s1.rotate(el->getAngle());
642                 s1.move(el->getCenter());
643
644                 s2.rotate(el->getAngle());
645                 s2.move(el->getCenter());
646
647                 sol.set(0, s1);
648                 sol.set(1, s2);
649
650
651     }
652
653     if (!sol.get(0).valid || !sol.get(1).valid) {
654         return NULL;
655     }
656
657     // create all possible tangents:
658     RS_Line* poss[2];
659
660     RS_LineData d;
661
662     d = RS_LineData(sol.get(0), point);
663     poss[0] = new RS_Line(NULL, d);
664     d = RS_LineData(sol.get(1), point);
665     poss[1] = new RS_Line(NULL, d);
666
667     // find closest tangent:
668     double minDist = RS_MAXDOUBLE;
669     double dist;
670     int idx = -1;
671     for (int i=0; i<2; ++i) {
672         dist = poss[i]->getDistanceToPoint(coord);
673         if (dist<minDist) {
674             minDist = dist;
675             idx = i;
676         }
677     }
678
679     // create the closest tangent:
680     if (idx!=-1) {
681         RS_LineData d = poss[idx]->getData();
682
683         for (int i=0; i<2; ++i) {
684             delete poss[i];
685         }
686
687         if (document!=NULL && handleUndo) {
688             document->startUndoCycle();
689         }
690
691         ret = new RS_Line(container, d);
692         ret->setLayerToActive();
693         ret->setPenToActive();
694         if (container!=NULL) {
695             container->addEntity(ret);
696         }
697         if (document!=NULL && handleUndo) {
698             document->addUndoable(ret);
699             document->endUndoCycle();
700         }
701         if (graphicView!=NULL) {
702             graphicView->drawEntity(ret);
703         }
704     } else {
705         ret = NULL;
706     }
707
708     return ret;
709 }
710
711
712
713 /**
714  * Creates a tangent between two circles or arcs.
715  * Out of the 4 possible tangents, the one closest to
716  * the given coordinate is returned.
717  *
718  * @param coord Coordinate to define which tangent we want (typically a
719  *              mouse coordinate).
720  * @param circle1 1st circle or arc entity.
721  * @param circle2 2nd circle or arc entity.
722  */
723 RS_Line* RS_Creation::createTangent2(const Vector& coord,
724                                      RS_Entity* circle1,
725                                      RS_Entity* circle2) {
726     RS_Line* ret = NULL;
727     Vector circleCenter1;
728     Vector circleCenter2;
729     double circleRadius1 = 0.0;
730     double circleRadius2 = 0.0;
731
732     // check given entities:
733     if (circle1==NULL || circle2==NULL ||
734             (circle1->rtti()!=RS2::EntityArc &&
735              circle1->rtti()!=RS2::EntityCircle) ||
736             (circle2->rtti()!=RS2::EntityArc &&
737              circle2->rtti()!=RS2::EntityCircle) ) {
738
739         return NULL;
740     }
741
742     if (circle1->rtti()==RS2::EntityCircle) {
743         circleCenter1 = ((RS_Circle*)circle1)->getCenter();
744         circleRadius1 = ((RS_Circle*)circle1)->getRadius();
745     } else if (circle1->rtti()==RS2::EntityArc) {
746         circleCenter1 = ((RS_Arc*)circle1)->getCenter();
747         circleRadius1 = ((RS_Arc*)circle1)->getRadius();
748     }
749
750     if (circle2->rtti()==RS2::EntityCircle) {
751         circleCenter2 = ((RS_Circle*)circle2)->getCenter();
752         circleRadius2 = ((RS_Circle*)circle2)->getRadius();
753     } else if (circle2->rtti()==RS2::EntityArc) {
754         circleCenter2 = ((RS_Arc*)circle2)->getCenter();
755         circleRadius2 = ((RS_Arc*)circle2)->getRadius();
756     }
757
758     // create all possible tangents:
759     RS_Line* poss[4];
760     for (int i=0; i<4; ++i) {
761         poss[i] = NULL;
762     }
763
764     RS_LineData d;
765
766     double angle1 = circleCenter1.angleTo(circleCenter2);
767     double dist1 = circleCenter1.distanceTo(circleCenter2);
768
769     if (dist1>1.0e-6) {
770         // outer tangents:
771         double dist2 = circleRadius2 - circleRadius1;
772         if (dist1>dist2) {
773             double angle2 = asin(dist2/dist1);
774             double angt1 = angle1 + angle2 + M_PI/2.0;
775             double angt2 = angle1 - angle2 - M_PI/2.0;
776             Vector offs1;
777             Vector offs2;
778
779             offs1.setPolar(circleRadius1, angt1);
780             offs2.setPolar(circleRadius2, angt1);
781
782             d = RS_LineData(circleCenter1 + offs1,
783                             circleCenter2 + offs2);
784             poss[0] = new RS_Line(NULL, d);
785
786
787             offs1.setPolar(circleRadius1, angt2);
788             offs2.setPolar(circleRadius2, angt2);
789
790             d = RS_LineData(circleCenter1 + offs1,
791                             circleCenter2 + offs2);
792             poss[1] = new RS_Line(NULL, d);
793         }
794
795         // inner tangents:
796         double dist3 = circleRadius2 + circleRadius1;
797         if (dist1>dist3) {
798             double angle3 = asin(dist3/dist1);
799             double angt3 = angle1 + angle3 + M_PI/2.0;
800             double angt4 = angle1 - angle3 - M_PI/2.0;
801             Vector offs1;
802             Vector offs2;
803
804             offs1.setPolar(circleRadius1, angt3);
805             offs2.setPolar(circleRadius2, angt3);
806
807             d = RS_LineData(circleCenter1 - offs1,
808                             circleCenter2 + offs2);
809             poss[2] = new RS_Line(NULL, d);
810
811
812             offs1.setPolar(circleRadius1, angt4);
813             offs2.setPolar(circleRadius2, angt4);
814
815             d = RS_LineData(circleCenter1 - offs1,
816                             circleCenter2 + offs2);
817             poss[3] = new RS_Line(NULL, d);
818         }
819
820     }
821
822     // find closest tangent:
823     double minDist = RS_MAXDOUBLE;
824     double dist;
825     int idx = -1;
826     for (int i=0; i<4; ++i) {
827         if (poss[i]!=NULL) {
828             dist = poss[i]->getDistanceToPoint(coord);
829             if (dist<minDist) {
830                 minDist = dist;
831                 idx = i;
832             }
833         }
834     }
835
836     if (idx!=-1) {
837         RS_LineData d = poss[idx]->getData();
838         for (int i=0; i<4; ++i) {
839             if (poss[i]!=NULL) {
840                 delete poss[i];
841             }
842         }
843
844         if (document!=NULL && handleUndo) {
845             document->startUndoCycle();
846         }
847
848         ret = new RS_Line(container, d);
849         ret->setLayerToActive();
850         ret->setPenToActive();
851         if (container!=NULL) {
852             container->addEntity(ret);
853         }
854         if (document!=NULL && handleUndo) {
855             document->addUndoable(ret);
856             document->endUndoCycle();
857         }
858         if (graphicView!=NULL) {
859             graphicView->drawEntity(ret);
860         }
861     } else {
862         ret = NULL;
863     }
864
865     return ret;
866 }
867
868
869 /**
870  * Creates a line with a relative angle to the given entity.
871  *
872  * @param coord Coordinate to define the point where the line should end.
873  *              (typically a mouse coordinate).
874  * @param entity Pointer to basis entity. The angle is relative to the
875  *               angle of this entity.
876  * @param angle Angle of the line relative to the angle of the basis entity.
877  * @param length Length of the line we're creating.
878  */
879 RS_Line* RS_Creation::createLineRelAngle(const Vector& coord,
880         RS_Entity* entity,
881         double angle,
882         double length) {
883
884     // check given entity / coord:
885     if (entity==NULL || !coord.valid ||
886             (entity->rtti()!=RS2::EntityArc && entity->rtti()!=RS2::EntityCircle
887              && entity->rtti()!=RS2::EntityLine)) {
888
889         return NULL;
890     }
891
892     double a1=0.0;
893
894     switch (entity->rtti()) {
895     case RS2::EntityLine:
896         a1 = ((RS_Line*)entity)->getAngle1();
897         break;
898     case RS2::EntityArc:
899         a1 = ((RS_Arc*)entity)->getCenter().angleTo(coord) + M_PI/2.0;
900         break;
901     case RS2::EntityCircle:
902         a1 = ((RS_Circle*)entity)->getCenter().angleTo(coord);
903         break;
904     default:
905         // never reached
906         break;
907     }
908
909     a1 += angle;
910
911     Vector v1;
912     v1.setPolar(length, a1);
913     //RS_ConstructionLineData(coord-v1, coord+v1);
914     RS_LineData d(coord-v1, coord+v1);
915     RS_Line* ret;
916
917     if (document!=NULL && handleUndo) {
918         document->startUndoCycle();
919     }
920
921     ret = new RS_Line(container, d);
922     ret->setLayerToActive();
923     ret->setPenToActive();
924     if (container!=NULL) {
925         container->addEntity(ret);
926     }
927     if (document!=NULL && handleUndo) {
928         document->addUndoable(ret);
929         document->endUndoCycle();
930     }
931     if (graphicView!=NULL) {
932         graphicView->drawEntity(ret);
933     }
934
935     return ret;
936 }
937
938
939 /**
940  * Creates a polygon with 'number' edges.
941  *
942  * @param center Center of the polygon.
943  * @param corner The first corner of the polygon
944  * @param number Number of edges / corners.
945  */
946 RS_Line* RS_Creation::createPolygon(const Vector& center,
947                                     const Vector& corner,
948                                     int number) {
949
950     // check given coords / number:
951     if (!center.valid || !corner.valid || number<3) {
952         return NULL;
953     }
954
955     RS_Line* ret = NULL;
956
957     if (document!=NULL && handleUndo) {
958         document->startUndoCycle();
959     }
960
961     Vector c1(false);
962     Vector c2 = corner;
963     RS_Line* line;
964
965     for (int n=1; n<=number; ++n) {
966         c1 = c2;
967         c2 = c2.rotate(center, (M_PI*2)/number);
968
969         line = new RS_Line(container, RS_LineData(c1, c2));
970         line->setLayerToActive();
971         line->setPenToActive();
972
973         if (ret==NULL) {
974             ret = line;
975         }
976
977         if (container!=NULL) {
978             container->addEntity(line);
979         }
980         if (document!=NULL && handleUndo) {
981             document->addUndoable(line);
982         }
983         if (graphicView!=NULL) {
984             graphicView->drawEntity(line);
985         }
986     }
987
988     if (document!=NULL && handleUndo) {
989         document->endUndoCycle();
990     }
991
992     return ret;
993 }
994
995
996
997 /**
998  * Creates a polygon with 'number' edges.
999  *
1000  * @param corner1 The first corner of the polygon.
1001  * @param corner2 The second corner of the polygon.
1002  * @param number Number of edges / corners.
1003  */
1004 RS_Line* RS_Creation::createPolygon2(const Vector& corner1,
1005                                      const Vector& corner2,
1006                                      int number) {
1007
1008     // check given coords / number:
1009     if (!corner1.valid || !corner2.valid || number<3) {
1010         return NULL;
1011     }
1012
1013     RS_Line* ret = NULL;
1014
1015     if (document!=NULL && handleUndo) {
1016         document->startUndoCycle();
1017     }
1018
1019     double len = corner1.distanceTo(corner2);
1020     double ang1 = corner1.angleTo(corner2);
1021     double ang = ang1;
1022
1023     Vector c1(false);
1024     Vector c2 = corner1;
1025     Vector edge;
1026     RS_Line* line;
1027
1028     for (int n=1; n<=number; ++n) {
1029         c1 = c2;
1030         edge.setPolar(len, ang);
1031         c2 = c1 + edge;
1032
1033         line = new RS_Line(container, RS_LineData(c1, c2));
1034         line->setLayerToActive();
1035         line->setPenToActive();
1036
1037         if (ret==NULL) {
1038             ret = line;
1039         }
1040
1041         if (container!=NULL) {
1042             container->addEntity(line);
1043         }
1044         if (document!=NULL && handleUndo) {
1045             document->addUndoable(line);
1046         }
1047         if (graphicView!=NULL) {
1048             graphicView->drawEntity(line);
1049         }
1050
1051         // more accurate than incrementing the angle:
1052         ang = ang1 + (2*M_PI)/number*n;
1053     }
1054
1055     if (document!=NULL && handleUndo) {
1056         document->endUndoCycle();
1057     }
1058
1059     return ret;
1060 }
1061
1062 /**
1063  * Creates an insert with the given data.
1064  *
1065  * @param data Insert data (position, block name, ..)
1066  */
1067 RS_Insert* RS_Creation::createInsert(RS_InsertData & data)
1068 {
1069         RS_DEBUG->print("RS_Creation::createInsert");
1070
1071         if (document != NULL && handleUndo)
1072                 document->startUndoCycle();
1073
1074         RS_Insert * ins = new RS_Insert(container, data);
1075         // inserts are also on layers
1076         ins->setLayerToActive();
1077         ins->setPenToActive();
1078
1079         if (container != NULL)
1080                 container->addEntity(ins);
1081
1082         if (document != NULL && handleUndo)
1083         {
1084                 document->addUndoable(ins);
1085                 document->endUndoCycle();
1086         }
1087
1088         if (graphicView != NULL)
1089                 graphicView->drawEntity(ins);
1090
1091         RS_DEBUG->print("RS_Creation::createInsert: OK");
1092
1093         return ins;
1094 }
1095
1096 /**
1097  * Creates an image with the given data.
1098  */
1099 RS_Image* RS_Creation::createImage(RS_ImageData& data) {
1100
1101     if (document!=NULL && handleUndo) {
1102         document->startUndoCycle();
1103     }
1104
1105     RS_Image* img = new RS_Image(container, data);
1106     img->setLayerToActive();
1107     img->setPenToActive();
1108     img->update();
1109
1110     if (container!=NULL) {
1111         container->addEntity(img);
1112     }
1113     if (document!=NULL && handleUndo) {
1114         document->addUndoable(img);
1115         document->endUndoCycle();
1116     }
1117     if (graphicView!=NULL) {
1118         graphicView->drawEntity(img);
1119     }
1120
1121     return img;
1122 }
1123
1124 /**
1125  * Creates a new block from the currently selected entitiies.
1126  *
1127  * @param referencePoint Reference point for the block.
1128  * @param name Block name
1129  * @param remove true: remove existing entities, false: don't touch entities
1130  */
1131 RS_Block * RS_Creation::createBlock(const RS_BlockData & data,
1132         const Vector & referencePoint, const bool remove)
1133 {
1134         // start undo cycle for the container if we're deleting the existing entities
1135         if (remove && document!=NULL) {
1136                 document->startUndoCycle();
1137         }
1138
1139         RS_Block* block =
1140                 new RS_Block(container,
1141                                                 RS_BlockData(data.name, data.basePoint, data.frozen));
1142
1143         // copy entities into a block
1144         for(RS_Entity * e=container->firstEntity(); e!=NULL; e=container->nextEntity())
1145         {
1146                 //for (uint i=0; i<container->count(); ++i) {
1147                 //RS_Entity* e = container->entityAt(i);
1148
1149                 if (e != NULL && e->isSelected())
1150                 {
1151                         // delete / redraw entity in graphic view:
1152                         if (remove)
1153                         {
1154                                 if (graphicView != NULL)
1155                                         graphicView->deleteEntity(e);
1156
1157                                 e->setSelected(false);
1158                         }
1159                         else
1160                         {
1161                                 if (graphicView != NULL)
1162                                         graphicView->deleteEntity(e);
1163
1164                                 e->setSelected(false);
1165
1166                                 if (graphicView != NULL)
1167                                         graphicView->drawEntity(e);
1168                         }
1169
1170                         // add entity to block:
1171                         RS_Entity * c = e->clone();
1172                         c->move(-referencePoint);
1173                         block->addEntity(c);
1174
1175                         if (remove)
1176                         {
1177                                 //container->removeEntity(e);
1178                                 //i=0;
1179                                 e->changeUndoState();
1180
1181                                 if (document != NULL)
1182                                         document->addUndoable(e);
1183                         }
1184                 }
1185         }
1186
1187         if (remove && document != NULL)
1188                 document->endUndoCycle();
1189
1190         if (graphic != NULL)
1191                 graphic->addBlock(block);
1192
1193         return block;
1194 }
1195
1196 /**
1197  * Inserts a library item from the given path into the drawing.
1198  */
1199 RS_Insert * RS_Creation::createLibraryInsert(RS_LibraryInsertData & data)
1200 {
1201         RS_DEBUG->print("RS_Creation::createLibraryInsert");
1202         Drawing g;
1203
1204         if (!g.open(data.file, RS2::FormatUnknown))
1205         {
1206                 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Creation::createLibraryInsert: Cannot open file: %s");
1207                 return NULL;
1208         }
1209
1210         // unit conversion:
1211         if (graphic != NULL)
1212         {
1213                 double uf = RS_Units::convert(1.0, g.getUnit(), graphic->getUnit());
1214                 g.scale(Vector(0.0, 0.0), Vector(uf, uf));
1215         }
1216
1217         //g.scale(Vector(data.factor, data.factor));
1218         //g.rotate(data.angle);
1219
1220 //      QString s = QFileInfo(data.file).baseName(true);
1221         QString s = QFileInfo(data.file).completeBaseName();
1222
1223         RS_Modification m(*container, graphicView);
1224         m.paste(RS_PasteData(data.insertionPoint, data.factor, data.angle, true, s), &g);
1225
1226         RS_DEBUG->print("RS_Creation::createLibraryInsert: OK");
1227
1228         return NULL;
1229 }