]> Shamusworld >> Repos - architektonas/blob - src/base/creation.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / creation.cpp
1 // 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 "creation.h"
18
19 #include "constructionline.h"
20 #include "drawing.h"
21 #include "graphicview.h"
22 #include "image.h"
23 #include "information.h"
24 #include "insert.h"
25 #include "modification.h"
26 #include "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 Creation::Creation(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 Creation::createPoint(const Vector& p) {
65     entityContainer->addEntity(new 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 Creation::createLine2P(const Vector& p1, const Vector& p2) {
80     entityContainer->addEntity(new Line(entityContainer,
81                                            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 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 Line(entityContainer,
99                                            LineData(e1, e12)));
100     entityContainer->addEntity(new Line(entityContainer,
101                                            LineData(e12, e2)));
102     entityContainer->addEntity(new Line(entityContainer,
103                                            LineData(e2, e21)));
104     entityContainer->addEntity(new Line(entityContainer,
105                                            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  * 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 /*Polyline* Creation::createPolyline(const Vector& startPoint) {
123     Polyline* pl = new Polyline(entityContainer,
124                 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 Entity * Creation::createParallelThrough(const Vector & coord, int number, Entity * e)
142 {
143         if (e == NULL)
144                 return NULL;
145
146         double dist;
147
148         if (e->rtti() == RS2::EntityLine)
149         {
150                 Line * l = (Line *)e;
151                 ConstructionLine cl(NULL, 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 Entity* Creation::createParallel(const Vector& coord,
182                                        double distance, int number,
183                                        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, (Line*)e);
191         break;
192
193     case RS2::EntityArc:
194         return createParallelArc(coord, distance, number, (Arc*)e);
195         break;
196
197     case RS2::EntityCircle:
198         return createParallelCircle(coord, distance, number, (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 Line* Creation::createParallelLine(const Vector& coord,
225         double distance, int number,
226         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     LineData parallelData;
235     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         Line parallel1(NULL, 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         Line parallel2(NULL, 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             Line* newLine = new 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 Arc* Creation::createParallelArc(const Vector& coord,
312                                        double distance, int number,
313                                        Arc* e) {
314
315     if (e==NULL) {
316         return NULL;
317     }
318
319     ArcData parallelData;
320     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         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         //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             Arc* newArc = new 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 Circle* Creation::createParallelCircle(const Vector& coord,
398         double distance, int number,
399         Circle* e) {
400
401     if (e==NULL) {
402         return NULL;
403     }
404
405     CircleData parallelData;
406     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         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         //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             Circle* newCircle = new 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 Line * Creation::createBisector(const Vector & coord1, const Vector & coord2,
484         double length, int num, Line * l1, Line * l2)
485 {
486         // check given entities:
487         if (!l1 || !l2 || l1->rtti() != RS2::EntityLine || l2->rtti() != RS2::EntityLine)
488                 return NULL;
489
490         // intersection between entities:
491         VectorSolutions sol = Information::getIntersection(l1, l2, false);
492         Vector inters = sol.get(0);
493
494         if (!inters.valid)
495                 return NULL;
496
497         double angle1 = inters.angleTo(l1->getNearestPointOnEntity(coord1));
498         double angle2 = inters.angleTo(l2->getNearestPointOnEntity(coord2));
499         double angleDiff = Math::getAngleDifference(angle1, angle2);
500
501         if (angleDiff > M_PI)
502                 angleDiff = angleDiff - 2 * M_PI;
503
504         Line * ret = NULL;
505
506         if (document && handleUndo)
507                 document->startUndoCycle();
508
509         for(int n=1; n<=num; n++)
510         {
511                 double angle = angle1 + (angleDiff / (num + 1) * n);
512                 Vector v;
513                 v.setPolar(length, angle);
514                 LineData d = LineData(inters, inters + v);
515                 Line * newLine = new Line(container, d);
516
517                 if (container)
518                 {
519                         newLine->setLayerToActive();
520                         newLine->setPenToActive();
521                         container->addEntity(newLine);
522                 }
523
524                 if (document && handleUndo)
525                         document->addUndoable(newLine);
526
527                 if (graphicView)
528                         graphicView->drawEntity(newLine);
529
530                 if (!ret)
531                         ret = newLine;
532         }
533
534         if (document && handleUndo)
535                 document->endUndoCycle();
536
537         return ret;
538 }
539
540
541
542 /**
543  * Creates a tangent between a given point and a circle or arc.
544  * Out of the 2 possible tangents, the one closest to
545  * the given coordinate is returned.
546  *
547  * @param coord Coordinate to define which tangent we want (typically a
548  *              mouse coordinate).
549  * @param point Point.
550  * @param circle Circle, arc or ellipse entity.
551  */
552 Line* Creation::createTangent1(const Vector& coord,
553                                      const Vector& point,
554                                      Entity* circle) {
555     Line* ret = NULL;
556     Vector circleCenter;
557
558     // check given entities:
559     if (circle==NULL || !point.valid ||
560             (circle->rtti()!=RS2::EntityArc && circle->rtti()!=RS2::EntityCircle
561              && circle->rtti()!=RS2::EntityEllipse)) {
562
563         return NULL;
564     }
565
566     if (circle->rtti()==RS2::EntityCircle) {
567         circleCenter = ((Circle*)circle)->getCenter();
568     } else if (circle->rtti()==RS2::EntityArc) {
569         circleCenter = ((Arc*)circle)->getCenter();
570     } else if (circle->rtti()==RS2::EntityEllipse) {
571         circleCenter = ((Ellipse*)circle)->getCenter();
572     }
573
574     // the two tangent points:
575     VectorSolutions sol;
576
577     // calculate tangent points for arcs / circles:
578     if (circle->rtti()!=RS2::EntityEllipse) {
579         // create temp. thales circle:
580         Vector tCenter = (point + circleCenter)/2.0;
581         double tRadius = point.distanceTo(tCenter);
582
583         Circle tmp(NULL, CircleData(tCenter, tRadius));
584
585         // get the two intersection points which are the tangent points:
586         sol = Information::getIntersection(&tmp, circle, false);
587     }
588
589     // calculate tangent points for ellipses:
590     else {
591         Ellipse* el = (Ellipse*)circle;
592         sol.alloc(2);
593         //sol.set(0, circleCenter);
594         //sol.set(1, circleCenter);
595
596
597         double a = el->getMajorRadius();     // the length of the major axis / 2
598         double b = el->getMinorRadius();     // the length of the minor axis / 2
599
600                 // rotate and move point:
601                 Vector point2 = point;
602                 point2.move(-el->getCenter());
603                 point2.rotate(-el->getAngle());
604
605         double xp = point2.x;             // coordinates of the given point
606         double yp = point2.y;
607
608         double xt1;                      // Tangent point 1
609         double yt1;
610         double xt2;                      // Tangent point 2
611         double yt2;
612
613         double a2 = a * a;
614         double b2 = b * b;
615         double d = a2 / b2 * yp / xp;
616         double e = a2 / xp;
617         double af = b2 * d * d + a2;
618         double bf = -b2 * d * e * 2.0;
619         double cf = b2 * e * e - a2 * b2;
620         double t = sqrt(bf * bf - af * cf * 4.0);
621         yt1 = (t - bf) / (af * 2.0);
622         xt1 = e - d * yt1;
623         yt2 = (-t - bf) / (af * 2.0);
624         xt2 = e - d * yt2;
625
626                 Vector s1 = Vector(xt1, yt1);
627                 Vector s2 = Vector(xt2, yt2);
628
629                 s1.rotate(el->getAngle());
630                 s1.move(el->getCenter());
631
632                 s2.rotate(el->getAngle());
633                 s2.move(el->getCenter());
634
635                 sol.set(0, s1);
636                 sol.set(1, s2);
637
638
639     }
640
641     if (!sol.get(0).valid || !sol.get(1).valid) {
642         return NULL;
643     }
644
645     // create all possible tangents:
646     Line* poss[2];
647
648     LineData d;
649
650     d = LineData(sol.get(0), point);
651     poss[0] = new Line(NULL, d);
652     d = LineData(sol.get(1), point);
653     poss[1] = new Line(NULL, d);
654
655     // find closest tangent:
656     double minDist = RS_MAXDOUBLE;
657     double dist;
658     int idx = -1;
659     for (int i=0; i<2; ++i) {
660         dist = poss[i]->getDistanceToPoint(coord);
661         if (dist<minDist) {
662             minDist = dist;
663             idx = i;
664         }
665     }
666
667     // create the closest tangent:
668     if (idx!=-1) {
669         LineData d = poss[idx]->getData();
670
671         for (int i=0; i<2; ++i) {
672             delete poss[i];
673         }
674
675         if (document!=NULL && handleUndo) {
676             document->startUndoCycle();
677         }
678
679         ret = new Line(container, d);
680         ret->setLayerToActive();
681         ret->setPenToActive();
682         if (container!=NULL) {
683             container->addEntity(ret);
684         }
685         if (document!=NULL && handleUndo) {
686             document->addUndoable(ret);
687             document->endUndoCycle();
688         }
689         if (graphicView!=NULL) {
690             graphicView->drawEntity(ret);
691         }
692     } else {
693         ret = NULL;
694     }
695
696     return ret;
697 }
698
699
700
701 /**
702  * Creates a tangent between two circles or arcs.
703  * Out of the 4 possible tangents, the one closest to
704  * the given coordinate is returned.
705  *
706  * @param coord Coordinate to define which tangent we want (typically a
707  *              mouse coordinate).
708  * @param circle1 1st circle or arc entity.
709  * @param circle2 2nd circle or arc entity.
710  */
711 Line* Creation::createTangent2(const Vector& coord,
712                                      Entity* circle1,
713                                      Entity* circle2) {
714     Line* ret = NULL;
715     Vector circleCenter1;
716     Vector circleCenter2;
717     double circleRadius1 = 0.0;
718     double circleRadius2 = 0.0;
719
720     // check given entities:
721     if (circle1==NULL || circle2==NULL ||
722             (circle1->rtti()!=RS2::EntityArc &&
723              circle1->rtti()!=RS2::EntityCircle) ||
724             (circle2->rtti()!=RS2::EntityArc &&
725              circle2->rtti()!=RS2::EntityCircle) ) {
726
727         return NULL;
728     }
729
730     if (circle1->rtti()==RS2::EntityCircle) {
731         circleCenter1 = ((Circle*)circle1)->getCenter();
732         circleRadius1 = ((Circle*)circle1)->getRadius();
733     } else if (circle1->rtti()==RS2::EntityArc) {
734         circleCenter1 = ((Arc*)circle1)->getCenter();
735         circleRadius1 = ((Arc*)circle1)->getRadius();
736     }
737
738     if (circle2->rtti()==RS2::EntityCircle) {
739         circleCenter2 = ((Circle*)circle2)->getCenter();
740         circleRadius2 = ((Circle*)circle2)->getRadius();
741     } else if (circle2->rtti()==RS2::EntityArc) {
742         circleCenter2 = ((Arc*)circle2)->getCenter();
743         circleRadius2 = ((Arc*)circle2)->getRadius();
744     }
745
746     // create all possible tangents:
747     Line* poss[4];
748     for (int i=0; i<4; ++i) {
749         poss[i] = NULL;
750     }
751
752     LineData d;
753
754     double angle1 = circleCenter1.angleTo(circleCenter2);
755     double dist1 = circleCenter1.distanceTo(circleCenter2);
756
757     if (dist1>1.0e-6) {
758         // outer tangents:
759         double dist2 = circleRadius2 - circleRadius1;
760         if (dist1>dist2) {
761             double angle2 = asin(dist2/dist1);
762             double angt1 = angle1 + angle2 + M_PI/2.0;
763             double angt2 = angle1 - angle2 - M_PI/2.0;
764             Vector offs1;
765             Vector offs2;
766
767             offs1.setPolar(circleRadius1, angt1);
768             offs2.setPolar(circleRadius2, angt1);
769
770             d = LineData(circleCenter1 + offs1,
771                             circleCenter2 + offs2);
772             poss[0] = new Line(NULL, d);
773
774
775             offs1.setPolar(circleRadius1, angt2);
776             offs2.setPolar(circleRadius2, angt2);
777
778             d = LineData(circleCenter1 + offs1,
779                             circleCenter2 + offs2);
780             poss[1] = new Line(NULL, d);
781         }
782
783         // inner tangents:
784         double dist3 = circleRadius2 + circleRadius1;
785         if (dist1>dist3) {
786             double angle3 = asin(dist3/dist1);
787             double angt3 = angle1 + angle3 + M_PI/2.0;
788             double angt4 = angle1 - angle3 - M_PI/2.0;
789             Vector offs1;
790             Vector offs2;
791
792             offs1.setPolar(circleRadius1, angt3);
793             offs2.setPolar(circleRadius2, angt3);
794
795             d = LineData(circleCenter1 - offs1,
796                             circleCenter2 + offs2);
797             poss[2] = new Line(NULL, d);
798
799
800             offs1.setPolar(circleRadius1, angt4);
801             offs2.setPolar(circleRadius2, angt4);
802
803             d = LineData(circleCenter1 - offs1,
804                             circleCenter2 + offs2);
805             poss[3] = new Line(NULL, d);
806         }
807
808     }
809
810     // find closest tangent:
811     double minDist = RS_MAXDOUBLE;
812     double dist;
813     int idx = -1;
814     for (int i=0; i<4; ++i) {
815         if (poss[i]!=NULL) {
816             dist = poss[i]->getDistanceToPoint(coord);
817             if (dist<minDist) {
818                 minDist = dist;
819                 idx = i;
820             }
821         }
822     }
823
824     if (idx!=-1) {
825         LineData d = poss[idx]->getData();
826         for (int i=0; i<4; ++i) {
827             if (poss[i]!=NULL) {
828                 delete poss[i];
829             }
830         }
831
832         if (document!=NULL && handleUndo) {
833             document->startUndoCycle();
834         }
835
836         ret = new Line(container, d);
837         ret->setLayerToActive();
838         ret->setPenToActive();
839         if (container!=NULL) {
840             container->addEntity(ret);
841         }
842         if (document!=NULL && handleUndo) {
843             document->addUndoable(ret);
844             document->endUndoCycle();
845         }
846         if (graphicView!=NULL) {
847             graphicView->drawEntity(ret);
848         }
849     } else {
850         ret = NULL;
851     }
852
853     return ret;
854 }
855
856
857 /**
858  * Creates a line with a relative angle to the given entity.
859  *
860  * @param coord Coordinate to define the point where the line should end.
861  *              (typically a mouse coordinate).
862  * @param entity Pointer to basis entity. The angle is relative to the
863  *               angle of this entity.
864  * @param angle Angle of the line relative to the angle of the basis entity.
865  * @param length Length of the line we're creating.
866  */
867 Line* Creation::createLineRelAngle(const Vector& coord,
868         Entity* entity,
869         double angle,
870         double length) {
871
872     // check given entity / coord:
873     if (entity==NULL || !coord.valid ||
874             (entity->rtti()!=RS2::EntityArc && entity->rtti()!=RS2::EntityCircle
875              && entity->rtti()!=RS2::EntityLine)) {
876
877         return NULL;
878     }
879
880     double a1=0.0;
881
882     switch (entity->rtti()) {
883     case RS2::EntityLine:
884         a1 = ((Line*)entity)->getAngle1();
885         break;
886     case RS2::EntityArc:
887         a1 = ((Arc*)entity)->getCenter().angleTo(coord) + M_PI/2.0;
888         break;
889     case RS2::EntityCircle:
890         a1 = ((Circle*)entity)->getCenter().angleTo(coord);
891         break;
892     default:
893         // never reached
894         break;
895     }
896
897     a1 += angle;
898
899     Vector v1;
900     v1.setPolar(length, a1);
901     //ConstructionLineData(coord-v1, coord+v1);
902     LineData d(coord-v1, coord+v1);
903     Line* ret;
904
905     if (document!=NULL && handleUndo) {
906         document->startUndoCycle();
907     }
908
909     ret = new Line(container, d);
910     ret->setLayerToActive();
911     ret->setPenToActive();
912     if (container!=NULL) {
913         container->addEntity(ret);
914     }
915     if (document!=NULL && handleUndo) {
916         document->addUndoable(ret);
917         document->endUndoCycle();
918     }
919     if (graphicView!=NULL) {
920         graphicView->drawEntity(ret);
921     }
922
923     return ret;
924 }
925
926
927 /**
928  * Creates a polygon with 'number' edges.
929  *
930  * @param center Center of the polygon.
931  * @param corner The first corner of the polygon
932  * @param number Number of edges / corners.
933  */
934 Line* Creation::createPolygon(const Vector& center,
935                                     const Vector& corner,
936                                     int number) {
937
938     // check given coords / number:
939     if (!center.valid || !corner.valid || number<3) {
940         return NULL;
941     }
942
943     Line* ret = NULL;
944
945     if (document!=NULL && handleUndo) {
946         document->startUndoCycle();
947     }
948
949     Vector c1(false);
950     Vector c2 = corner;
951     Line* line;
952
953     for (int n=1; n<=number; ++n) {
954         c1 = c2;
955         c2 = c2.rotate(center, (M_PI*2)/number);
956
957         line = new Line(container, LineData(c1, c2));
958         line->setLayerToActive();
959         line->setPenToActive();
960
961         if (ret==NULL) {
962             ret = line;
963         }
964
965         if (container!=NULL) {
966             container->addEntity(line);
967         }
968         if (document!=NULL && handleUndo) {
969             document->addUndoable(line);
970         }
971         if (graphicView!=NULL) {
972             graphicView->drawEntity(line);
973         }
974     }
975
976     if (document!=NULL && handleUndo) {
977         document->endUndoCycle();
978     }
979
980     return ret;
981 }
982
983
984
985 /**
986  * Creates a polygon with 'number' edges.
987  *
988  * @param corner1 The first corner of the polygon.
989  * @param corner2 The second corner of the polygon.
990  * @param number Number of edges / corners.
991  */
992 Line* Creation::createPolygon2(const Vector& corner1,
993                                      const Vector& corner2,
994                                      int number) {
995
996     // check given coords / number:
997     if (!corner1.valid || !corner2.valid || number<3) {
998         return NULL;
999     }
1000
1001     Line* ret = NULL;
1002
1003     if (document!=NULL && handleUndo) {
1004         document->startUndoCycle();
1005     }
1006
1007     double len = corner1.distanceTo(corner2);
1008     double ang1 = corner1.angleTo(corner2);
1009     double ang = ang1;
1010
1011     Vector c1(false);
1012     Vector c2 = corner1;
1013     Vector edge;
1014     Line* line;
1015
1016     for (int n=1; n<=number; ++n) {
1017         c1 = c2;
1018         edge.setPolar(len, ang);
1019         c2 = c1 + edge;
1020
1021         line = new Line(container, LineData(c1, c2));
1022         line->setLayerToActive();
1023         line->setPenToActive();
1024
1025         if (ret==NULL) {
1026             ret = line;
1027         }
1028
1029         if (container!=NULL) {
1030             container->addEntity(line);
1031         }
1032         if (document!=NULL && handleUndo) {
1033             document->addUndoable(line);
1034         }
1035         if (graphicView!=NULL) {
1036             graphicView->drawEntity(line);
1037         }
1038
1039         // more accurate than incrementing the angle:
1040         ang = ang1 + (2*M_PI)/number*n;
1041     }
1042
1043     if (document!=NULL && handleUndo) {
1044         document->endUndoCycle();
1045     }
1046
1047     return ret;
1048 }
1049
1050 /**
1051  * Creates an insert with the given data.
1052  *
1053  * @param data Insert data (position, block name, ..)
1054  */
1055 Insert* Creation::createInsert(InsertData & data)
1056 {
1057         DEBUG->print("Creation::createInsert");
1058
1059         if (document != NULL && handleUndo)
1060                 document->startUndoCycle();
1061
1062         Insert * ins = new Insert(container, data);
1063         // inserts are also on layers
1064         ins->setLayerToActive();
1065         ins->setPenToActive();
1066
1067         if (container != NULL)
1068                 container->addEntity(ins);
1069
1070         if (document != NULL && handleUndo)
1071         {
1072                 document->addUndoable(ins);
1073                 document->endUndoCycle();
1074         }
1075
1076         if (graphicView != NULL)
1077                 graphicView->drawEntity(ins);
1078
1079         DEBUG->print("Creation::createInsert: OK");
1080
1081         return ins;
1082 }
1083
1084 /**
1085  * Creates an image with the given data.
1086  */
1087 Image* Creation::createImage(ImageData& data) {
1088
1089     if (document!=NULL && handleUndo) {
1090         document->startUndoCycle();
1091     }
1092
1093     Image* img = new Image(container, data);
1094     img->setLayerToActive();
1095     img->setPenToActive();
1096     img->update();
1097
1098     if (container!=NULL) {
1099         container->addEntity(img);
1100     }
1101     if (document!=NULL && handleUndo) {
1102         document->addUndoable(img);
1103         document->endUndoCycle();
1104     }
1105     if (graphicView!=NULL) {
1106         graphicView->drawEntity(img);
1107     }
1108
1109     return img;
1110 }
1111
1112 /**
1113  * Creates a new block from the currently selected entitiies.
1114  *
1115  * @param referencePoint Reference point for the block.
1116  * @param name Block name
1117  * @param remove true: remove existing entities, false: don't touch entities
1118  */
1119 Block * Creation::createBlock(const BlockData & data,
1120         const Vector & referencePoint, const bool remove)
1121 {
1122         // start undo cycle for the container if we're deleting the existing entities
1123         if (remove && document!=NULL) {
1124                 document->startUndoCycle();
1125         }
1126
1127         Block* block =
1128                 new Block(container,
1129                                                 BlockData(data.name, data.basePoint, data.frozen));
1130
1131         // copy entities into a block
1132         for(Entity * e=container->firstEntity(); e!=NULL; e=container->nextEntity())
1133         {
1134                 //for (uint i=0; i<container->count(); ++i) {
1135                 //Entity* e = container->entityAt(i);
1136
1137                 if (e && e->isSelected())
1138                 {
1139                         // delete / redraw entity in graphic view:
1140                         if (remove)
1141                         {
1142 #warning "!!! Old rendering path needs upgrading !!!"
1143 #if 0
1144                                 if (graphicView)
1145                                         graphicView->deleteEntity(e);
1146 #endif
1147
1148                                 e->setSelected(false);
1149                         }
1150                         else
1151                         {
1152 #warning "!!! Old rendering path needs upgrading !!!"
1153 #if 0
1154                                 if (graphicView)
1155                                         graphicView->deleteEntity(e);
1156 #endif
1157                                 e->setSelected(false);
1158
1159 #warning "!!! Old rendering path needs upgrading !!!"
1160 #if 0
1161                                 if (graphicView)
1162                                         graphicView->drawEntity(e);
1163 #endif
1164                         }
1165
1166                         // add entity to block:
1167                         Entity * c = e->clone();
1168                         c->move(-referencePoint);
1169                         block->addEntity(c);
1170
1171                         if (remove)
1172                         {
1173                                 //container->removeEntity(e);
1174                                 //i=0;
1175                                 e->changeUndoState();
1176
1177                                 if (document)
1178                                         document->addUndoable(e);
1179                         }
1180                 }
1181         }
1182
1183         if (remove && document != NULL)
1184                 document->endUndoCycle();
1185
1186         if (graphic != NULL)
1187                 graphic->addBlock(block);
1188
1189         return block;
1190 }
1191
1192 /**
1193  * Inserts a library item from the given path into the drawing.
1194  */
1195 Insert * Creation::createLibraryInsert(LibraryInsertData & data)
1196 {
1197         DEBUG->print("Creation::createLibraryInsert");
1198         Drawing g;
1199
1200         if (!g.open(data.file, RS2::FormatUnknown))
1201         {
1202                 DEBUG->print(Debug::D_WARNING, "Creation::createLibraryInsert: Cannot open file: %s");
1203                 return NULL;
1204         }
1205
1206         // unit conversion:
1207         if (graphic != NULL)
1208         {
1209                 double uf = Units::convert(1.0, g.getUnit(), graphic->getUnit());
1210                 g.scale(Vector(0.0, 0.0), Vector(uf, uf));
1211         }
1212
1213         //g.scale(Vector(data.factor, data.factor));
1214         //g.rotate(data.angle);
1215
1216 //      QString s = QFileInfo(data.file).baseName(true);
1217         QString s = QFileInfo(data.file).completeBaseName();
1218
1219         Modification m(*container, graphicView);
1220         m.paste(PasteData(data.insertionPoint, data.factor, data.angle, true, s), &g);
1221
1222         DEBUG->print("Creation::createLibraryInsert: OK");
1223
1224         return NULL;
1225 }