1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7 **
8 **
9 ** This file may be distributed and/or modified under the terms of the
10 ** GNU General Public License version 2 as published by the Free Software
11 ** Foundation and appearing in the file gpl-2.0.txt included in the
12 ** packaging of this file.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 **
23 ** This copyright notice MUST APPEAR in all copies of the script!
24 **
25 **********************************************************************/
26
27 #include<cmath>
28 #include <QString>
29 #include <QFileInfo>
30 #include "rs_creation.h"
31 #include "rs_document.h"
32 #include "rs_constructionline.h"
33 #include "rs_graphicview.h"
34 #include "rs_graphic.h"
35 #include "rs_arc.h"
36 #include "rs_block.h"
37 #include "rs_line.h"
38 #include "rs_circle.h"
39 #include "rs_ellipse.h"
40 #include "rs_insert.h"
41 #include "rs_image.h"
42 #include "lc_hyperbola.h"
43 #include "lc_splinepoints.h"
44 #include "rs_modification.h"
45 #include "rs_information.h"
46 #include "rs_math.h"
47 #include "rs_debug.h"
48 #include "lc_undosection.h"
49
50 /**
51 * Default constructor.
52 *
53 * @param container The container to which we will add
54 * entities. Usually that's an RS_Graphic entity but
55 * it can also be a polyline, text, ...
56 */
RS_Creation(RS_EntityContainer * container,RS_GraphicView * graphicView,bool handleUndo)57 RS_Creation::RS_Creation(RS_EntityContainer* container,
58 RS_GraphicView* graphicView,
59 bool handleUndo):
60 container(container)
61 ,graphic(container?container->getGraphic():nullptr)
62 ,document(container?container->getDocument():nullptr)
63 ,graphicView(graphicView)
64 ,handleUndo(handleUndo)
65 {
66 }
67
68 /**
69 * Creates an entity parallel to the given entity e through the given
70 * 'coord'.
71 *
72 * @param coord Coordinate to define the distance / side (typically a
73 * mouse coordinate).
74 * @param number Number of parallels.
75 * @param e Original entity.
76 *
77 * @return Pointer to the first created parallel or nullptr if no
78 * parallel has been created.
79 */
createParallelThrough(const RS_Vector & coord,int number,RS_Entity * e)80 RS_Entity* RS_Creation::createParallelThrough(const RS_Vector& coord,
81 int number,
82 RS_Entity* e) {
83 if (!e) {
84 return nullptr;
85 }
86
87 double dist;
88
89 if (e->rtti()==RS2::EntityLine) {
90 RS_Line* l = (RS_Line*)e;
91 RS_ConstructionLine cl(nullptr,
92 RS_ConstructionLineData(l->getStartpoint(),
93 l->getEndpoint()));
94 dist = cl.getDistanceToPoint(coord);
95 } else {
96 dist = e->getDistanceToPoint(coord);
97 }
98
99 if (dist<RS_MAXDOUBLE) {
100 return createParallel(coord, dist, number, e);
101 } else {
102 return nullptr;
103 }
104 }
105
106
107
108 /**
109 * Creates an entity parallel to the given entity e.
110 * Out of the 2 possible parallels, the one closest to
111 * the given coordinate is returned.
112 * Lines, Arcs and Circles can have parallels.
113 *
114 * @param coord Coordinate to define which parallel we want (typically a
115 * mouse coordinate).
116 * @param distance Distance of the parallel.
117 * @param number Number of parallels.
118 * @param e Original entity.
119 *
120 * @return Pointer to the first created parallel or nullptr if no
121 * parallel has been created.
122 */
createParallel(const RS_Vector & coord,double distance,int number,RS_Entity * e)123 RS_Entity* RS_Creation::createParallel(const RS_Vector& coord,
124 double distance, int number,
125 RS_Entity* e) {
126 if (!e) {
127 return nullptr;
128 }
129
130 switch (e->rtti()) {
131 case RS2::EntityLine:
132 return createParallelLine(coord, distance, number, (RS_Line*)e);
133 break;
134
135 case RS2::EntityArc:
136 return createParallelArc(coord, distance, number, (RS_Arc*)e);
137 break;
138
139 case RS2::EntityCircle:
140 return createParallelCircle(coord, distance, number, (RS_Circle*)e);
141 break;
142
143 case RS2::EntitySplinePoints:
144 return createParallelSplinePoints(coord, distance, number, (LC_SplinePoints*)e);
145 break;
146
147 default:
148 break;
149 }
150
151 return nullptr;
152 }
153
154 /**
155 * Creates a line parallel to the given line e.
156 * Out of the 2 possible parallels, the one closest to
157 * the given coordinate is returned.
158 *
159 * @param coord Coordinate to define which parallel we want (typically a
160 * mouse coordinate).
161 * @param distance Distance of the parallel.
162 * @param number Number of parallels.
163 * @param e Original entity.
164 *
165 * @return Pointer to the first created parallel or nullptr if no
166 * parallel has been created.
167 */
createParallelLine(const RS_Vector & coord,double distance,int number,RS_Line * e)168 RS_Line* RS_Creation::createParallelLine(const RS_Vector& coord,
169 double distance, int number,
170 RS_Line* e) {
171
172 if (!e) {
173 return nullptr;
174 }
175
176 double ang = e->getAngle1() + M_PI_2;
177 RS_LineData parallelData;
178 RS_Line* ret = nullptr;
179
180 LC_UndoSection undo( document, handleUndo);
181 for (int num=1; num<=number; ++num) {
182
183 // calculate 1st parallel:
184 RS_Vector p1 = RS_Vector::polar(distance*num, ang);
185 p1 += e->getStartpoint();
186 RS_Vector p2 = RS_Vector::polar(distance*num, ang);
187 p2 += e->getEndpoint();
188 RS_Line parallel1{p1, p2};
189
190 // calculate 2nd parallel:
191 p1.setPolar(distance*num, ang+M_PI);
192 p1 += e->getStartpoint();
193 p2.setPolar(distance*num, ang+M_PI);
194 p2 += e->getEndpoint();
195 RS_Line parallel2{p1, p2};
196
197 double dist1 = parallel1.getDistanceToPoint(coord);
198 double dist2 = parallel2.getDistanceToPoint(coord);
199 double minDist = std::min(dist1, dist2);
200
201 if (minDist<RS_MAXDOUBLE) {
202 if (dist1<dist2) {
203 parallelData = parallel1.getData();
204 } else {
205 parallelData = parallel2.getData();
206 }
207
208 RS_Line* newLine = new RS_Line{container, parallelData};
209 if (!ret) {
210 ret = newLine;
211 }
212 setEntity(newLine);
213 }
214 }
215
216 return ret;
217 }
218
219
220
221 /**
222 * Creates a arc parallel to the given arc e.
223 * Out of the 2 possible parallels, the one closest to
224 * the given coordinate is returned.
225 *
226 * @param coord Coordinate to define which parallel we want (typically a
227 * mouse coordinate).
228 * @param distance Distance of the parallel.
229 * @param number Number of parallels.
230 * @param e Original entity.
231 *
232 * @return Pointer to the first created parallel or nullptr if no
233 * parallel has been created.
234 */
createParallelArc(const RS_Vector & coord,double distance,int number,RS_Arc * e)235 RS_Arc* RS_Creation::createParallelArc(const RS_Vector& coord,
236 double distance, int number,
237 RS_Arc* e) {
238
239 if (!e) {
240 return nullptr;
241 }
242
243 RS_ArcData parallelData;
244 RS_Arc* ret = nullptr;
245
246 bool inside = (e->getCenter().distanceTo(coord) < e->getRadius());
247
248 if (inside) {
249 distance *= -1;
250 }
251
252 for (int num=1; num<=number; ++num) {
253
254 // calculate parallel:
255 bool ok = true;
256 RS_Arc parallel1(nullptr, e->getData());
257 parallel1.setRadius(e->getRadius() + distance*num);
258 if (parallel1.getRadius()<0.0) {
259 parallel1.setRadius(RS_MAXDOUBLE);
260 ok = false;
261 }
262
263 // calculate 2nd parallel:
264 //RS_Arc parallel2(nullptr, e->getData());
265 //parallel2.setRadius(e->getRadius()+distance*num);
266
267 //double dist1 = parallel1.getDistanceToPoint(coord);
268 //double dist2 = parallel2.getDistanceToPoint(coord);
269 //double minDist = min(dist1, dist2);
270
271 //if (minDist<RS_MAXDOUBLE) {
272 if (ok) {
273 //if (dist1<dist2) {
274 parallelData = parallel1.getData();
275 //} else {
276 // parallelData = parallel2.getData();
277 //}
278
279 LC_UndoSection undo( document, handleUndo);
280 RS_Arc* newArc = new RS_Arc(container, parallelData);
281 if (!ret) {
282 ret = newArc;
283 }
284 setEntity(newArc);
285 }
286 }
287
288 return ret;
289 }
290
291
292
293 /**
294 * Creates a circle parallel to the given circle e.
295 * Out of the 2 possible parallels, the one closest to
296 * the given coordinate is returned.
297 *
298 * @param coord Coordinate to define which parallel we want (typically a
299 * mouse coordinate).
300 * @param distance Distance of the parallel.
301 * @param number Number of parallels.
302 * @param e Original entity.
303 *
304 * @return Pointer to the first created parallel or nullptr if no
305 * parallel has been created.
306 */
createParallelCircle(const RS_Vector & coord,double distance,int number,RS_Circle * e)307 RS_Circle* RS_Creation::createParallelCircle(const RS_Vector& coord,
308 double distance, int number,
309 RS_Circle* e) {
310
311 if (!e) {
312 return nullptr;
313 }
314
315 RS_CircleData parallelData;
316 RS_Circle* ret = nullptr;
317
318 bool inside = (e->getCenter().distanceTo(coord) < e->getRadius());
319
320 if (inside) {
321 distance *= -1;
322 }
323
324 for (int num=1; num<=number; ++num) {
325
326 // calculate parallel:
327 bool ok = true;
328 RS_Circle parallel1(nullptr, e->getData());
329 parallel1.setRadius(e->getRadius() + distance*num);
330 if (parallel1.getRadius()<0.0) {
331 parallel1.setRadius(RS_MAXDOUBLE);
332 ok = false;
333 }
334
335 // calculate 2nd parallel:
336 //RS_Circle parallel2(nullptr, e->getData());
337 //parallel2.setRadius(e->getRadius()+distance*num);
338
339 //double dist1 = parallel1.getDistanceToPoint(coord);
340 //double dist2 = parallel2.getDistanceToPoint(coord);
341 //double minDist = min(dist1, dist2);
342
343 //if (minDist<RS_MAXDOUBLE) {
344 if (ok) {
345 //if (dist1<dist2) {
346 parallelData = parallel1.getData();
347 //} else {
348 // parallelData = parallel2.getData();
349 //}
350
351 LC_UndoSection undo( document, handleUndo);
352 RS_Circle* newCircle = new RS_Circle(container, parallelData);
353 if (!ret) {
354 ret = newCircle;
355 }
356 setEntity(newCircle);
357 }
358 }
359 return ret;
360 }
361
362 /**
363 * Creates a spline pseudo-parallel to the given circle e.
364 * Out of the 2 possible parallels, the one closest to
365 * the given coordinate is returned.
366 *
367 * @param coord Coordinate to define which parallel we want (typically a
368 * mouse coordinate).
369 * @param distance Distance of the parallel.
370 * @param number Number of parallels.
371 * @param e Original entity.
372 *
373 * @return Pointer to the first created parallel or nullptr if no
374 * parallel has been created.
375 */
createParallelSplinePoints(const RS_Vector & coord,double distance,int number,LC_SplinePoints * e)376 LC_SplinePoints* RS_Creation::createParallelSplinePoints(const RS_Vector& coord,
377 double distance, int number, LC_SplinePoints* e)
378 {
379 if(!e) return nullptr;
380
381 LC_SplinePoints *psp, *ret = nullptr;
382
383 LC_UndoSection undo( document, handleUndo);
384 for(int i = 1; i <= number; ++i)
385 {
386 psp = (LC_SplinePoints*)e->clone();
387 psp->offset(coord, i*distance);
388
389 psp->setParent(container);
390 if(!ret) ret = psp;
391 setEntity(psp);
392 }
393
394 return ret;
395 }
396
397
398 /**
399 * Creates a bisecting line of the angle between the entities
400 * e1 and e2. Out of the 4 possible bisectors, the one closest to
401 * the given coordinate is returned.
402 *
403 * @param coord Coordinate to define which bisector we want (typically a
404 * mouse coordinate).
405 * @param length Length of the bisecting line.
406 * @param num Number of bisectors
407 * @param l1 First line.
408 * @param l2 Second line.
409 *
410 * @return Pointer to the first bisector created or nullptr if no bisectors
411 * were created.
412 */
createBisector(const RS_Vector & coord1,const RS_Vector & coord2,double length,int num,RS_Line * l1,RS_Line * l2)413 RS_Line* RS_Creation::createBisector(const RS_Vector& coord1,
414 const RS_Vector& coord2,
415 double length,
416 int num,
417 RS_Line* l1,
418 RS_Line* l2) {
419
420 // check given entities:
421 if (!(l1 && l2))
422 return nullptr;
423 if (!(l1->rtti()==RS2::EntityLine && l2->rtti()==RS2::EntityLine))
424 return nullptr;
425
426 // intersection between entities:
427 RS_VectorSolutions const& sol =
428 RS_Information::getIntersection(l1, l2, false);
429 RS_Vector inters = sol.get(0);
430 if (!inters.valid) {
431 return nullptr;
432 }
433
434 double angle1 = inters.angleTo(l1->getNearestPointOnEntity(coord1));
435 double angle2 = inters.angleTo(l2->getNearestPointOnEntity(coord2));
436 double angleDiff = RS_Math::getAngleDifference(angle1, angle2);
437 if (angleDiff > M_PI) {
438 angleDiff = angleDiff - 2.*M_PI;
439 }
440 RS_Line* ret = nullptr;
441
442 LC_UndoSection undo( document, handleUndo);
443 for (int n=1; n <= num; ++n) {
444
445 double angle = angle1 +
446 (angleDiff / (num+1) * n);
447
448 RS_Vector const& v = RS_Vector::polar(length, angle);
449
450 RS_Line* newLine = new RS_Line{container, inters, inters + v};
451 if (!ret) ret = newLine;
452 setEntity(newLine);
453 }
454
455 return ret;
456 }
457
458 /**
459 * create a tangent line which is orthogonal to the given RS_Line(normal)
460 * @coord, the tangent line closest to this point
461 * @normal, the line orthogonal to the tangent line
462 * @circle, arc/circle/ellipse for tangent line
463 *
464 * Author: Dongxu Li
465 */
createLineOrthTan(const RS_Vector & coord,RS_Line * normal,RS_Entity * circle)466 RS_Line* RS_Creation::createLineOrthTan(const RS_Vector& coord,
467 RS_Line* normal,
468 RS_Entity* circle) {
469 RS_Line* ret = nullptr;
470
471 // check given entities:
472 if (!(circle && normal))
473 return ret;
474 if (!circle->isArc())
475 return ret;
476 //if( normal->getLength()<RS_TOLERANCE) return ret;//line too short
477 RS_Vector const& t0 = circle->getNearestOrthTan(coord,*normal,false);
478 if(!t0.valid) return ret;
479 RS_Vector const& vp=normal->getNearestPointOnEntity(t0, false);
480 LC_UndoSection undo( document, handleUndo);
481 ret = new RS_Line{container, vp, t0};
482 ret->setLayerToActive();
483 ret->setPenToActive();
484 return ret;
485 }
486
487 /**
488 * Creates a tangent between a given point and a circle or arc.
489 * Out of the 2 possible tangents, the one closest to
490 * the given coordinate is returned.
491 *
492 * @param coord Coordinate to define which tangent we want (typically a
493 * mouse coordinate).
494 * @param point Point.
495 * @param circle Circle, arc or ellipse entity.
496 */
createTangent1(const RS_Vector & coord,const RS_Vector & point,RS_Entity * circle)497 RS_Line* RS_Creation::createTangent1(const RS_Vector& coord,
498 const RS_Vector& point,
499 RS_Entity* circle) {
500 RS_Line* ret = nullptr;
501 //RS_Vector circleCenter;
502
503 // check given entities:
504 if (!(circle && point.valid)) return nullptr;
505 if (!(circle->isArc() || circle->rtti()==RS2::EntitySplinePoints)){
506 return nullptr;
507 }
508
509 // the two tangent points:
510 RS_VectorSolutions sol=circle->getTangentPoint(point);
511
512 if (!sol.getNumber())
513 return nullptr;
514 RS_Vector const vp2{sol.getClosest(coord)};
515 RS_LineData d;
516 if( (vp2-point).squared() > RS_TOLERANCE2 ) {
517 d={vp2, point};
518 }else{//the given point is a tangential point
519 d={point+circle->getTangentDirection(point), point};
520 }
521
522
523 // create the closest tangent:
524 LC_UndoSection undo( document, handleUndo);
525 ret = new RS_Line{container, d};
526 setEntity(ret);
527
528 return ret;
529 }
530
531 /**
532 * Creates a tangent between two circles or arcs.
533 * Out of the 4 possible tangents, the one closest to
534 * the given coordinate is returned.
535 *
536 * @param coord Coordinate to define which tangent we want (typically a
537 * mouse coordinate).
538 * @param circle1 1st circle or arc entity.
539 * @param circle2 2nd circle or arc entity.
540 */
createTangent2(const RS_Vector & coord,RS_Entity * circle1,RS_Entity * circle2)541 RS_Line* RS_Creation::createTangent2(const RS_Vector& coord,
542 RS_Entity* circle1,
543 RS_Entity* circle2) {
544 RS_Line* ret = nullptr;
545 RS_Vector circleCenter1;
546 RS_Vector circleCenter2;
547 double circleRadius1 = 0.0;
548 double circleRadius2 = 0.0;
549
550 // check given entities:
551 if(! (circle1 && circle2))
552 return nullptr;
553 if( !(circle1->isArc() && circle2->isArc()))
554 return nullptr;
555
556 std::vector<RS_Line*> poss;
557 // for (int i=0; i<4; ++i) {
558 // poss[i] = nullptr;
559 // }
560 RS_LineData d;
561 if( circle1->rtti() == RS2::EntityEllipse) {
562 std::swap(circle1,circle2);//move Ellipse to the second place
563 }
564 circleCenter1=circle1->getCenter();
565 circleRadius1=circle1->getRadius();
566 circleCenter2=circle2->getCenter();
567 circleRadius2=circle2->getRadius();
568 if(circle2->rtti() != RS2::EntityEllipse) {
569 //no ellipse
570
571 // create all possible tangents:
572
573 double angle1 = circleCenter1.angleTo(circleCenter2);
574 double dist1 = circleCenter1.distanceTo(circleCenter2);
575
576 if (dist1>1.0e-6) {
577 // outer tangents:
578 double dist2 = circleRadius2 - circleRadius1;
579 if (dist1>dist2) {
580 double angle2 = asin(dist2/dist1);
581 double angt1 = angle1 + angle2 + M_PI_2;
582 double angt2 = angle1 - angle2 - M_PI_2;
583 RS_Vector offs1 = RS_Vector::polar(circleRadius1, angt1);
584 RS_Vector offs2 = RS_Vector::polar(circleRadius2, angt1);
585
586 poss.push_back( new RS_Line{circleCenter1 + offs1,
587 circleCenter2 + offs2});
588
589
590 offs1.setPolar(circleRadius1, angt2);
591 offs2.setPolar(circleRadius2, angt2);
592
593 poss.push_back( new RS_Line{circleCenter1 + offs1,
594 circleCenter2 + offs2});
595 }
596
597 // inner tangents:
598 double dist3 = circleRadius2 + circleRadius1;
599 if (dist1>dist3) {
600 double angle3 = asin(dist3/dist1);
601 double angt3 = angle1 + angle3 + M_PI_2;
602 double angt4 = angle1 - angle3 - M_PI_2;
603 RS_Vector offs1;
604 RS_Vector offs2;
605
606 offs1.setPolar(circleRadius1, angt3);
607 offs2.setPolar(circleRadius2, angt3);
608
609 poss.push_back( new RS_Line{circleCenter1 - offs1,
610 circleCenter2 + offs2});
611
612
613 offs1.setPolar(circleRadius1, angt4);
614 offs2.setPolar(circleRadius2, angt4);
615
616 poss.push_back( new RS_Line{circleCenter1 - offs1,
617 circleCenter2 + offs2});
618 }
619
620 }
621 }else{
622 //circle2 is Ellipse
623 std::unique_ptr<RS_Ellipse> e2((RS_Ellipse*)circle2->clone());
624 // RS_Ellipse* e2=new RS_Ellipse(nullptr,RS_EllipseData(RS_Vector(4.,1.),RS_Vector(2.,0.),0.5,0.,0.,false));
625 // RS_Ellipse e3(nullptr,RS_EllipseData(RS_Vector(4.,1.),RS_Vector(2.,0.),0.5,0.,0.,false));
626 // RS_Ellipse* circle1=new RS_Ellipse(nullptr,RS_EllipseData(RS_Vector(0.,0.),RS_Vector(1.,0.),1.,0.,0.,false));
627 RS_Vector m0(circle1->getCenter());
628 // std::cout<<"translation: "<<-m0<<std::endl;
629 e2->move(-m0); //circle1 centered at origin
630
631 double a,b;
632 double a0(0.);
633 if(circle1->rtti() != RS2::EntityEllipse){//circle1 is either arc or circle
634 a=fabs(circle1->getRadius());
635 b=a;
636 if(fabs(a)<RS_TOLERANCE) return nullptr;
637 }else{//circle1 is ellipse
638 RS_Ellipse* e1=static_cast<RS_Ellipse*>(circle1);
639 a0=e1->getAngle();
640 // std::cout<<"rotation: "<<-a0<<std::endl;
641 e2->rotate(-a0);//e1 major axis along x-axis
642 a=e1->getMajorRadius();
643 b=e1->getRatio()*a;
644 if(fabs(a)<RS_TOLERANCE || fabs(b)<RS_TOLERANCE) return nullptr;
645 }
646 RS_Vector factor1(1./a,1./b);
647 // std::cout<<"scaling: factor1="<<factor1<<std::endl;
648 e2->scale(RS_Vector(0.,0.),factor1);//circle1 is a unit circle
649 factor1.set(a,b);
650 double a2(e2->getAngle());
651 // std::cout<<"rotation: a2="<<-a2<<std::endl;
652 e2->rotate(-a2); //ellipse2 with major axis in x-axis direction
653 a=e2->getMajorP().x;
654 b=a*e2->getRatio();
655 RS_Vector v(e2->getCenter());
656 // std::cout<<"Center: (x,y)="<<v<<std::endl;
657
658
659 std::vector<double> m(0,0.);
660 m.push_back(1./(a*a)); //ma000
661 m.push_back(1./(b*b)); //ma000
662 m.push_back(v.y*v.y-1.); //ma100
663 m.push_back(v.x*v.y); //ma101
664 m.push_back(v.x*v.x-1.); //ma111
665 m.push_back(2.*a*b*v.y); //mb10
666 m.push_back(2.*a*b*v.x); //mb11
667 m.push_back(a*a*b*b); //mc1
668
669 auto vs0=RS_Math::simultaneousQuadraticSolver(m); //to hold solutions
670 if (vs0.getNumber()<1) return nullptr;
671 // for(size_t i=0;i<vs0.getNumber();i++){
672 for(RS_Vector vpec: vs0){
673 RS_Vector vpe2(e2->getCenter()+
674 RS_Vector(vpec.y/e2->getRatio(),vpec.x*e2->getRatio()));
675 vpec.x *= -1.;//direction vector of tangent
676 RS_Vector vpe1(vpe2 - vpec*(RS_Vector::dotP(vpec,vpe2)/vpec.squared()));
677 // std::cout<<"vpe1.squared()="<<vpe1.squared()<<std::endl;
678 RS_Line *l=new RS_Line{vpe1, vpe2};
679 l->rotate(a2);
680 l->scale(factor1);
681 l->rotate(a0);
682 l->move(m0);
683 poss.push_back(l);
684
685 }
686 //debugging
687
688 }
689 // find closest tangent:
690 if(poss.size()<1) return nullptr;
691 double minDist = RS_MAXDOUBLE;
692 double dist;
693 int idx = -1;
694 for (size_t i=0; i<poss.size(); ++i) {
695 if (poss[i]) {
696 poss[i]->getNearestPointOnEntity(coord,false,&dist);
697 // std::cout<<poss.size()<<": i="<<i<<" dist="<<dist<<"\n";
698 if (dist<minDist) {
699 minDist = dist;
700 idx = i;
701 }
702 }
703 }
704 //idx=static_cast<int>(poss.size()*(random()/(double(1.0)+RAND_MAX)));
705 if (idx!=-1) {
706 RS_LineData d = poss[idx]->getData();
707 for(auto p: poss){
708 if(p)
709 delete p;
710 }
711
712 LC_UndoSection undo( document, handleUndo);
713 ret = new RS_Line{container, d};
714 setEntity(ret);
715 } else {
716 ret = nullptr;
717 }
718
719 return ret;
720 }
721
722 /**
723 * create the path of centers of common tangent circles of the two given circles
724 *@ return nullptr, if failed
725 *@ at success return either an ellipse or hyperbola
726 */
createCircleTangent2(RS_Entity * circle1,RS_Entity * circle2)727 std::vector<RS_Entity*> RS_Creation::createCircleTangent2( RS_Entity* circle1,RS_Entity* circle2)
728 {
729 std::vector<RS_Entity*> ret(0, nullptr);
730 if (!(circle1 && circle2)) return ret;
731 RS_Entity* e1=circle1;
732 RS_Entity* e2=circle2;
733
734 if (e1->getRadius() < e2->getRadius()) std::swap(e1,e2);
735
736 RS_Vector center1=e1->getCenter();
737 RS_Vector center2=e2->getCenter();
738 RS_Vector cp=(center1+center2)*0.5;
739 double dist=center1.distanceTo(center2);
740 if(dist<RS_TOLERANCE) return ret;
741 RS_Vector vp= center1 - cp;
742 double c=dist/(e1->getRadius()+e2->getRadius());
743 if( c < 1. - RS_TOLERANCE) {
744 //two circles intersection or one circle in the other, there's an ellipse path
745 ret.push_back(
746 new RS_Ellipse(nullptr,
747 {cp, vp, sqrt(1. - c*c), 0., 0., false}
748 ));
749 }
750 if( dist + e2 ->getRadius() < e1->getRadius() +RS_TOLERANCE ) {
751 //one circle inside of another, the path is an ellipse
752 return ret;
753 }
754 if(c > 1. + RS_TOLERANCE) {
755 //not circle in circle, there's a hyperbola path
756 c= (e1->getRadius() - e2->getRadius())/dist;
757 ret.push_back(new LC_Hyperbola(nullptr, LC_HyperbolaData(cp,vp*c,sqrt(1. - c*c),0.,0.,false)));
758 return ret;
759 }
760 ret.push_back(new RS_Line{cp, {cp.x - vp.y, cp.y+vp.x}});
761 return ret;
762 }
763
764 /**
765 * Creates a line with a relative angle to the given entity.
766 *
767 * @param coord Coordinate to define the point where the line should end.
768 * (typically a mouse coordinate).
769 * @param entity Pointer to basis entity. The angle is relative to the
770 * angle of this entity.
771 * @param angle Angle of the line relative to the angle of the basis entity.
772 * @param length Length of the line we're creating.
773 */
createLineRelAngle(const RS_Vector & coord,RS_Entity * entity,double angle,double length)774 RS_Line* RS_Creation::createLineRelAngle(const RS_Vector& coord,
775 RS_Entity* entity,
776 double angle,
777 double length) {
778
779 // check given entity / coord:
780 if (!(entity && coord))
781 return nullptr;
782
783 switch(entity->rtti()){
784 default:
785 return nullptr;
786 case RS2::EntityArc:
787 case RS2::EntityCircle:
788 case RS2::EntityLine:
789 case RS2::EntityEllipse:
790 break;
791 }
792
793 auto const vp = entity->getNearestPointOnEntity(coord, false);
794
795 double const a1 = angle + entity->getTangentDirection(vp).angle();
796
797 RS_Vector const v1 = RS_Vector::polar(length, a1);
798 //RS_ConstructionLineData(coord-v1, coord+v1);
799
800 LC_UndoSection undo( document, handleUndo);
801 RS_Line* ret = new RS_Line{container, coord-v1, coord+v1};
802 setEntity(ret);
803
804 return ret;
805 }
806
807
808 /**
809 * Creates a polygon with 'number' edges.
810 *
811 * @param center Center of the polygon.
812 * @param corner The first corner of the polygon
813 * @param number Number of edges / corners.
814 */
createPolygon(const RS_Vector & center,const RS_Vector & corner,int number)815 RS_Line* RS_Creation::createPolygon(const RS_Vector& center,
816 const RS_Vector& corner,
817 int number) {
818 // check given coords / number:
819 if (!center.valid || !corner.valid || number<3) {
820 return nullptr;
821 }
822
823 RS_Line* ret = nullptr;
824
825 double const r = center.distanceTo(corner);
826 double const angle0 = center.angleTo(corner);
827 double const da = 2.*M_PI/number;
828 LC_UndoSection undo( document, handleUndo);
829 for (int i=0; i < number; ++i) {
830 RS_Vector const& c0 = center +
831 RS_Vector::polar(r, angle0 + i*da);
832 RS_Vector const& c1 = center +
833 RS_Vector::polar(r, angle0 + ((i+1)%number)*da);
834
835 RS_Line* line = new RS_Line{container, c0, c1};
836 line->setLayerToActive();
837 line->setPenToActive();
838
839 if (!ret) ret = line;
840
841 if (container) {
842 container->addEntity(line);
843 }
844 undo.addUndoable(line);
845 if (graphicView) {
846 graphicView->drawEntity(line);
847 }
848 }
849
850 return ret;
851 }
852
853
854
855 /**
856 * Creates a polygon with 'number' edges.
857 *
858 * @param corner1 The first corner of the polygon.
859 * @param corner2 The second corner of the polygon.
860 * @param number Number of edges / corners.
861 */
createPolygon2(const RS_Vector & corner1,const RS_Vector & corner2,int number)862 RS_Line* RS_Creation::createPolygon2(const RS_Vector& corner1,
863 const RS_Vector& corner2,
864 int number) {
865 // check given coords / number:
866 if (!corner1.valid || !corner2.valid || number<3) {
867 return nullptr;
868 }
869
870 RS_Line* ret = nullptr;
871
872 LC_UndoSection undo( document, handleUndo);
873 double const len = corner1.distanceTo(corner2);
874 double const da = 2.*M_PI/number;
875 double const r = 0.5*len/sin(0.5*da);
876 double const angle1 = corner1.angleTo(corner2);
877 RS_Vector center = (corner1 + corner2)*0.5;
878
879 //TODO, the center or the polygon could be at left or right side
880 //left is chosen here
881 center += RS_Vector::polar(0.5*len/tan(0.5*da), angle1 + M_PI_2);
882 double const angle0 = center.angleTo(corner1);
883
884
885 for (int i=0; i<number; ++i) {
886 RS_Vector const& c0 = center +
887 RS_Vector::polar(r, angle0 + i*da);
888 RS_Vector const& c1 = center +
889 RS_Vector::polar(r, angle0 + ((i+1)%number)*da);
890
891 RS_Line* line = new RS_Line{container, c0, c1};
892 line->setLayerToActive();
893 line->setPenToActive();
894
895 if (!ret) ret = line;
896
897 if (container) {
898 container->addEntity(line);
899 }
900 undo.addUndoable(line);
901 if (graphicView) {
902 graphicView->drawEntity(line);
903 }
904
905 }
906
907 return ret;
908 }
909
910 /**
911 * Creates a polygon with 'number' edges.
912 *
913 * @param center Center of the polygon.
914 * @param tangent The first tangent of the polygon with a circle
915 * @param number Number of edges / corners.
916 */
createPolygon3(const RS_Vector & center,const RS_Vector & tangent,int number)917 RS_Line* RS_Creation::createPolygon3(const RS_Vector& center, //added by txmy
918 const RS_Vector& tangent,
919 int number) {
920 // check given coords / number:
921 if (!center.valid || !tangent.valid || number<3) {
922 return nullptr;
923 }
924
925 RS_Line* ret = nullptr;
926
927 LC_UndoSection undo( document, handleUndo);
928 RS_Vector corner(0, 0);
929 double angle = 2.*M_PI/number/2.0;
930 corner.x = tangent.x + (center.y - tangent.y) * tan(angle);
931 corner.y = tangent.y + (tangent.x - center.x) * tan(angle);
932
933 double const r = center.distanceTo(corner);
934 double const angle0 = center.angleTo(corner);
935 double const da = 2.*M_PI/number;
936
937 for (int i=0; i < number; ++i) {
938 RS_Vector const& c0 = center +
939 RS_Vector::polar(r, angle0 + i*da);
940 RS_Vector const& c1 = center +
941 RS_Vector::polar(r, angle0 + ((i+1)%number)*da);
942
943 RS_Line* line = new RS_Line{container, c0, c1};
944 line->setLayerToActive();
945 line->setPenToActive();
946
947 if (!ret) ret = line;
948
949 if (container) {
950 container->addEntity(line);
951 }
952 undo.addUndoable(line);
953 if (graphicView) {
954 graphicView->drawEntity(line);
955 }
956 }
957
958 return ret;
959 }
960
961 /**
962 * Creates an insert with the given data.
963 *
964 * @param data Insert data (position, block name, ..)
965 */
createInsert(const RS_InsertData * pdata)966 RS_Insert* RS_Creation::createInsert(const RS_InsertData* pdata) {
967
968 RS_DEBUG->print("RS_Creation::createInsert");
969
970 LC_UndoSection undo( document, handleUndo);
971 RS_Insert* ins = new RS_Insert(container, *pdata);
972 // inserts are also on layers
973 setEntity(ins);
974
975 RS_DEBUG->print("RS_Creation::createInsert: OK");
976
977 return ins;
978 }
979
980
981
982 /**
983 * Creates an image with the given data.
984 */
createImage(const RS_ImageData * data)985 RS_Image* RS_Creation::createImage(const RS_ImageData* data) {
986
987 LC_UndoSection undo( document, handleUndo);
988 RS_Image* img = new RS_Image(container, *data);
989 img->update();
990 setEntity(img);
991
992 return img;
993 }
994
995
996 /**
997 * Creates a new block from the currently selected entitiies.
998 *
999 * @param referencePoint Reference point for the block.
1000 * @param name Block name
1001 * @param remove true: remove existing entities, false: don't touch entities
1002 */
createBlock(const RS_BlockData * data,const RS_Vector & referencePoint,const bool remove)1003 RS_Block* RS_Creation::createBlock(const RS_BlockData* data,
1004 const RS_Vector& referencePoint,
1005 const bool remove) {
1006
1007 // start undo cycle for the container if we're deleting the existing entities
1008 LC_UndoSection undo(document, remove);
1009 RS_Block* block;
1010 // Block cannot contain blocks.
1011 if (container->rtti() == RS2::EntityBlock) {
1012 block = new RS_Block(container->getParent(), RS_BlockData(*data));
1013 } else {
1014 block = new RS_Block(container, RS_BlockData(*data));
1015 }
1016
1017 // copy entities into a block
1018 for(auto e: *container){
1019 //for (unsigned i=0; i<container->count(); ++i) {
1020 //RS_Entity* e = container->entityAt(i);
1021
1022 if (e && e->isSelected()) {
1023
1024 // delete / redraw entity in graphic view:
1025 if (remove) {
1026 if (graphicView) {
1027 graphicView->deleteEntity(e);
1028 }
1029 e->setSelected(false);
1030 } else {
1031 if (graphicView) {
1032 graphicView->deleteEntity(e);
1033 }
1034 e->setSelected(false);
1035 if (graphicView) {
1036 graphicView->drawEntity(e);
1037 }
1038 }
1039
1040 // add entity to block:
1041 RS_Entity* c = e->clone();
1042 c->move(-referencePoint);
1043 block->addEntity(c);
1044
1045 if (remove) {
1046 //container->removeEntity(e);
1047 //i=0;
1048 e->changeUndoState();
1049 undo.addUndoable(e);
1050 }
1051 }
1052 }
1053
1054 if (graphic) {
1055 graphic->addBlock(block);
1056 }
1057
1058 return block;
1059 }
1060
1061
1062
1063 /**
1064 * Inserts a library item from the given path into the drawing.
1065 */
createLibraryInsert(RS_LibraryInsertData & data)1066 RS_Insert* RS_Creation::createLibraryInsert(RS_LibraryInsertData& data) {
1067
1068 RS_DEBUG->print("RS_Creation::createLibraryInsert");
1069
1070 RS_Graphic g;
1071 if (!g.open(data.file, RS2::FormatUnknown)) {
1072 RS_DEBUG->print(RS_Debug::D_WARNING,
1073 "RS_Creation::createLibraryInsert: Cannot open file: %s");
1074 return nullptr;
1075 }
1076
1077 // unit conversion:
1078 if (graphic) {
1079 double uf = RS_Units::convert(1.0, g.getUnit(),
1080 graphic->getUnit());
1081 g.scale(RS_Vector(0.0, 0.0), RS_Vector(uf, uf));
1082 }
1083
1084 //g.scale(RS_Vector(data.factor, data.factor));
1085 //g.rotate(data.angle);
1086
1087 QString s;
1088 s = QFileInfo(data.file).completeBaseName();
1089
1090 RS_Modification m(*container, graphicView);
1091 m.paste(
1092 RS_PasteData(
1093 data.insertionPoint,
1094 data.factor, data.angle, true,
1095 s),
1096 &g);
1097
1098 RS_DEBUG->print("RS_Creation::createLibraryInsert: OK");
1099
1100 return nullptr;
1101 }
1102
setEntity(RS_Entity * en) const1103 void RS_Creation::setEntity(RS_Entity* en) const
1104 {
1105 en->setLayerToActive();
1106 en->setPenToActive();
1107
1108 if (container) {
1109 container->addEntity(en);
1110 }
1111 LC_UndoSection undo( document, handleUndo);
1112 undo.addUndoable(en);
1113 if (graphicView) {
1114 graphicView->drawEntity(en);
1115 }
1116 }
1117
1118
1119 // EOF
1120