1 /****************************************************************************
2 **
3  * Draw ellipse by foci and a point on ellipse
4 
5 Copyright (C) 2011 Dongxu Li (dongxuli2011@gmail.com)
6 Copyright (C) 2011 R. van Twisk (librecad@rvt.dds.nl)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 **********************************************************************/
22 
23 #include <QAction>
24 #include <QMouseEvent>
25 #include "rs_actiondrawellipse4points.h"
26 
27 #include "rs_dialogfactory.h"
28 #include "rs_graphicview.h"
29 #include "rs_commandevent.h"
30 #include "rs_circle.h"
31 #include "rs_ellipse.h"
32 #include "rs_coordinateevent.h"
33 #include "rs_preview.h"
34 #include "rs_debug.h"
35 
36 struct RS_ActionDrawEllipse4Points::Points {
37 	RS_VectorSolutions points;
38 	RS_CircleData cData;
39 	RS_EllipseData eData;
40 	bool valid,evalid;
41 	bool m_bUniqueEllipse{false}; //a message of non-unique ellipse is shown
42 };
43 
44 /**
45  * Constructor.
46  *
47  */
RS_ActionDrawEllipse4Points(RS_EntityContainer & container,RS_GraphicView & graphicView)48 RS_ActionDrawEllipse4Points::RS_ActionDrawEllipse4Points(
49 		RS_EntityContainer& container,
50 		RS_GraphicView& graphicView)
51 	:RS_PreviewActionInterface("Draw ellipse from 4 points", container,
52 							   graphicView)
53 	, pPoints(new Points{})
54 {
55 	actionType=RS2::ActionDrawEllipse4Points;
56 }
57 
58 RS_ActionDrawEllipse4Points::~RS_ActionDrawEllipse4Points() = default;
59 
init(int status)60 void RS_ActionDrawEllipse4Points::init(int status) {
61     RS_PreviewActionInterface::init(status);
62 	if(getStatus() == SetPoint1) pPoints->points.clear();
63 }
64 
trigger()65 void RS_ActionDrawEllipse4Points::trigger() {
66     RS_PreviewActionInterface::trigger();
67     RS_Entity* en;
68 	if(getStatus()==SetPoint4 && pPoints->evalid){
69 		en=new RS_Ellipse(container, pPoints->eData);
70     }else{
71 		en=new RS_Circle(container, pPoints->cData);
72     }
73 
74     // update undo list:
75     deletePreview();
76     container->addEntity(en);
77     if (document) {
78         document->startUndoCycle();
79         document->addUndoable(en);
80         document->endUndoCycle();
81     }
82     RS_Vector rz = graphicView->getRelativeZero();
83     graphicView->redraw(RS2::RedrawDrawing);
84     graphicView->moveRelativeZero(rz);
85     drawSnapper();
86     setStatus(SetPoint1);
87     //    RS_DEBUG->print("RS_ActionDrawEllipse4Point::trigger():" " entity added: %d", ellipse->getId());
88 
89     //    RS_DEBUG->print("RS_ActionDrawEllipse4Point::trigger():" " entity added: %d", ellipse->getId());
90 }
91 
92 
93 
mouseMoveEvent(QMouseEvent * e)94 void RS_ActionDrawEllipse4Points::mouseMoveEvent(QMouseEvent* e) {
95 //    RS_DEBUG->print("RS_ActionDrawEllipse4Point::mouseMoveEvent begin");
96 
97     RS_Vector mouse = snapPoint(e);
98 	pPoints->points.set(getStatus(),mouse);
99     if(preparePreview()) {
100         switch(getStatus()) {
101         case SetPoint2:
102         case SetPoint3:
103 			if(pPoints->valid){
104 				RS_Circle* circle=new RS_Circle(preview.get(), pPoints->cData);
105                 deletePreview();
106                 preview->addEntity(circle);
107                 drawPreview();
108             }
109             break;
110         case SetPoint4:
111 			if(pPoints->evalid){
112                 deletePreview();
113 				RS_Ellipse* e=new RS_Ellipse(preview.get(), pPoints->eData);
114                 preview->addEntity(e);
115                 drawPreview();
116             }
117         default:
118             break;
119         }
120 
121     }
122 //    RS_DEBUG->print("RS_ActionDrawEllipse4Point::mouseMoveEvent end");
123 }
124 
125 
preparePreview()126 bool RS_ActionDrawEllipse4Points::preparePreview(){
127 	pPoints->valid=false;
128     switch(getStatus()) {
129     case SetPoint2:
130     case SetPoint3:
131     {
132 		RS_Circle c(preview.get(), pPoints->cData);
133 		pPoints->valid= c.createFrom3P(pPoints->points);
134 		if(pPoints->valid){
135 			pPoints->cData = c.getData();
136         }
137 
138     }
139         break;
140     case SetPoint4:
141     {
142         int j=SetPoint4;
143 		pPoints->evalid=false;
144 		if ((pPoints->points.get(j) - pPoints->points.get(j-1)).squared() <RS_TOLERANCE15){
145 			RS_Circle c(preview.get(), pPoints->cData);
146 			pPoints->valid= c.createFrom3P(pPoints->points);
147 			if (pPoints->valid) {
148 				pPoints->cData = c.getData();
149 			}
150 		} else {
151 			RS_Ellipse e{preview.get(), pPoints->eData};
152 			pPoints->valid= e.createFrom4P(pPoints->points);
153 			if (pPoints->valid) {
154 				pPoints->evalid=pPoints->valid;
155 				pPoints->eData = e.getData();
156 				pPoints->m_bUniqueEllipse=false;
157 			} else {
158 				pPoints->evalid=false;
159 				if (pPoints->m_bUniqueEllipse==false) {
160                     RS_DIALOGFACTORY->commandMessage(tr("Can not determine uniquely an ellipse"));
161 					pPoints->m_bUniqueEllipse=true;
162                 }
163             }
164         }
165     }
166         break;
167     default:
168         break;
169     }
170 	return pPoints->valid;
171 }
172 
mouseReleaseEvent(QMouseEvent * e)173 void RS_ActionDrawEllipse4Points::mouseReleaseEvent(QMouseEvent* e) {
174     // Proceed to next status
175     if (e->button()==Qt::LeftButton) {
176         RS_CoordinateEvent ce(snapPoint(e));
177         coordinateEvent(&ce);
178     }
179 
180     // Return to last status:
181     else if (e->button()==Qt::RightButton) {
182         deletePreview();
183         init(getStatus()-1);
184     }
185 }
186 
187 
coordinateEvent(RS_CoordinateEvent * e)188 void RS_ActionDrawEllipse4Points::coordinateEvent(RS_CoordinateEvent* e) {
189 	if (!e) {
190         return;
191     }
192     RS_Vector mouse = e->getCoordinate();
193 	pPoints->points.alloc(getStatus()+1);
194 	pPoints->points.set(getStatus(),mouse);
195 
196     switch (getStatus()) {
197     case SetPoint1:
198         graphicView->moveRelativeZero(mouse);
199         setStatus(SetPoint2);
200         break;
201     case SetPoint2:
202     case SetPoint3:
203     case SetPoint4:
204 
205         if( preparePreview()) {
206             graphicView->moveRelativeZero(mouse);
207             if(getStatus() == SetPoint4 ||
208 					(pPoints->points.get(getStatus()) - pPoints->points.get(getStatus()-1)).squared() <RS_TOLERANCE15) {
209                 //also draw the entity, if clicked on the same point twice
210                 trigger();
211             }else{
212                 setStatus(getStatus()+1);
213             }
214         }
215 
216     default:
217         break;
218     }
219 }
220 
221 //fixme, support command line
222 
223 /*
224 void RS_ActionDrawEllipse4Point::commandEvent(RS_CommandEvent* e) {
225     QString c = e->getCommand().toLower();
226 
227     if (checkCommand("help", c)) {
228             RS_DIALOGFACTORY->commandMessage(msgAvailableCommands()
229                                              + getAvailableCommands().join(", "));
230         return;
231     }
232 
233     switch (getStatus()) {
234     case SetFocus1: {
235             bool ok;
236             double m = RS_Math::eval(c, &ok);
237             if (ok) {
238                 ratio = m / major.magnitude();
239                 if (!isArc) {
240                     trigger();
241                 } else {
242                     setStatus(SetAngle1);
243                 }
244             } else {
245                     RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
246             }
247         }
248         break;
249 
250     case SetAngle1: {
251             bool ok;
252             double a = RS_Math::eval(c, &ok);
253             if (ok) {
254                 angle1 = RS_Math::deg2rad(a);
255                 setStatus(SetAngle2);
256             } else {
257                     RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
258             }
259         }
260         break;
261 
262     case SetAngle2: {
263             bool ok;
264             double a = RS_Math::eval(c, &ok);
265             if (ok) {
266                 angle2 = RS_Math::deg2rad(a);
267                 trigger();
268             } else {
269                     RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
270             }
271         }
272         break;
273 
274     default:
275         break;
276     }
277 }
278 */
279 
280 
getAvailableCommands()281 QStringList RS_ActionDrawEllipse4Points::getAvailableCommands() {
282 	return {};
283 }
284 
updateMouseButtonHints()285 void RS_ActionDrawEllipse4Points::updateMouseButtonHints() {
286 	switch (getStatus()) {
287 	case SetPoint1:
288 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the first point on ellipse"),
289 											tr("Cancel"));
290 		break;
291 
292 	case SetPoint2:
293 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the second point on ellipse"),
294 											tr("Back"));
295 		break;
296 
297 	case SetPoint3:
298 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the third point on ellipse"),
299 											tr("Back"));
300 		break;
301 
302 	case SetPoint4:
303 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the fourth point on ellipse"),
304 											tr("Back"));
305 		break;
306 
307 	default:
308 		RS_DIALOGFACTORY->updateMouseWidget();
309 		break;
310 	}
311 }
312 
313 
314 
updateMouseCursor()315 void RS_ActionDrawEllipse4Points::updateMouseCursor() {
316 	graphicView->setMouseCursor(RS2::CadCursor);
317 }
318 
319 // EOF
320