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 #include<cmath>
27 #include <QAction>
28 #include <QMouseEvent>
29 #include "rs_actiondrawellipseaxis.h"
30 
31 #include "rs_dialogfactory.h"
32 #include "rs_graphicview.h"
33 #include "rs_commandevent.h"
34 #include "rs_ellipse.h"
35 #include "rs_line.h"
36 #include "rs_coordinateevent.h"
37 #include "rs_math.h"
38 #include "rs_preview.h"
39 #include "rs_debug.h"
40 
41 struct RS_ActionDrawEllipseAxis::Points {
42 	/** Center of ellipse */
43 	RS_Vector center;
44 	/** Endpoint of major axis */
45 	RS_Vector m_vMajorP;
46 	/** Ratio major / minor */
47 	double ratio;
48 	/** Start angle */
49 	double angle1;
50 	/** End angle */
51 	double angle2;
52 	/** Do we produce an arc (true) or full ellipse (false) */
53 	bool isArc;
54 };
55 
56 /**
57  * Constructor.
58  *
59  * @param isArc true if this action will produce an ellipse arc.
60  *              false if it will produce a full ellipse.
61  */
RS_ActionDrawEllipseAxis(RS_EntityContainer & container,RS_GraphicView & graphicView,bool isArc)62 RS_ActionDrawEllipseAxis::RS_ActionDrawEllipseAxis(
63 		RS_EntityContainer& container,
64 		RS_GraphicView& graphicView,
65 		bool isArc)
66 	:RS_PreviewActionInterface("Draw ellipse with axis",
67 							   container, graphicView)
68 	,pPoints(new Points{{}, {}, 0.5, 0., isArc?2.*M_PI:0., isArc})
69 {
70 	actionType=isArc?RS2::ActionDrawEllipseArcAxis:RS2::ActionDrawEllipseAxis;
71 }
72 
73 RS_ActionDrawEllipseAxis::~RS_ActionDrawEllipseAxis() = default;
74 
75 
init(int status)76 void RS_ActionDrawEllipseAxis::init(int status) {
77     RS_PreviewActionInterface::init(status);
78 
79     if (status==SetCenter) {
80 		pPoints->center = {};
81     }
82     if (status<=SetMajor) {
83 		pPoints->m_vMajorP = {};
84     }
85     if (status<=SetMinor) {
86 		pPoints->ratio = 0.5;
87     }
88     if (status<=SetAngle1) {
89 		pPoints->angle1 = 0.0;
90     }
91     if (status<=SetAngle2) {
92 		pPoints->angle2 = 0.0;
93     }
94 }
95 
96 
97 
trigger()98 void RS_ActionDrawEllipseAxis::trigger() {
99     RS_PreviewActionInterface::trigger();
100 
101 	RS_Ellipse* ellipse = new RS_Ellipse{container,
102 	{pPoints->center, pPoints->m_vMajorP, pPoints->ratio,
103 			pPoints->angle1, pPoints->angle2, false}
104 };
105 	if (pPoints->ratio > 1.){
106         ellipse->switchMajorMinor();
107     }
108     ellipse->setLayerToActive();
109     ellipse->setPenToActive();
110 
111     container->addEntity(ellipse);
112 
113     // upd. undo list:
114     if (document) {
115         document->startUndoCycle();
116         document->addUndoable(ellipse);
117         document->endUndoCycle();
118     }
119 
120     RS_Vector rz = graphicView->getRelativeZero();
121     graphicView->redraw(RS2::RedrawDrawing);
122     graphicView->moveRelativeZero(rz);
123     drawSnapper();
124 
125     setStatus(SetCenter);
126 
127     RS_DEBUG->print("RS_ActionDrawEllipseAxis::trigger():"
128                     " entity added: %d", ellipse->getId());
129 }
130 
131 
132 
mouseMoveEvent(QMouseEvent * e)133 void RS_ActionDrawEllipseAxis::mouseMoveEvent(QMouseEvent* e) {
134     RS_DEBUG->print("RS_ActionDrawEllipseAxis::mouseMoveEvent begin");
135 
136     RS_Vector mouse = snapPoint(e);
137 
138     switch (getStatus()) {
139 //    case SetCenter:
140 //        break;
141 
142     case SetMajor:
143 		if (pPoints->center.valid) {
144             deletePreview();
145 			preview->addEntity(new RS_Ellipse{preview.get(),
146 				{pPoints->center, mouse-pPoints->center, 0.5, 0.0,
147 											  pPoints->isArc?2.*M_PI:0.
148 							   , false}});
149             drawPreview();
150         }
151         break;
152 
153     case SetMinor:
154 		if (pPoints->center.valid && pPoints->m_vMajorP.valid) {
155             deletePreview();
156 			RS_Line line{pPoints->center-pPoints->m_vMajorP, pPoints->center+pPoints->m_vMajorP};
157             double d = line.getDistanceToPoint(mouse);
158 			pPoints->ratio = d/(line.getLength()/2);
159 			preview->addEntity(new RS_Ellipse{preview.get(),
160 											  {pPoints->center, pPoints->m_vMajorP, pPoints->ratio, 0., pPoints->isArc?2.*M_PI:0.
161 											   , false}});
162 			drawPreview();
163         }
164         break;
165 
166     case SetAngle1:
167 		if (pPoints->center.valid && pPoints->m_vMajorP.valid) {
168             deletePreview();
169 
170             //angle1 = center.angleTo(mouse);
171 
172                         RS_Vector m = mouse;
173 						m.rotate(pPoints->center, -pPoints->m_vMajorP.angle());
174 						RS_Vector v = m-pPoints->center;
175 						v.y /= pPoints->ratio;
176 						pPoints->angle1 = v.angle(); // + m_vMajorP.angle();
177 
178 			preview->addEntity(new RS_Line{preview.get(), pPoints->center, mouse});
179 
180 			preview->addEntity(new RS_Ellipse{preview.get(),
181 				{pPoints->center, pPoints->m_vMajorP, pPoints->ratio, pPoints->angle1, pPoints->angle1+1.0
182 				 , false}});
183             drawPreview();
184         }
185         break;
186 
187     case SetAngle2:
188 		if (pPoints->center.valid && pPoints->m_vMajorP.valid) {
189             deletePreview();
190             //angle2 = center.angleTo(mouse);
191 
192                         RS_Vector m = mouse;
193 						m.rotate(pPoints->center, -pPoints->m_vMajorP.angle());
194 						RS_Vector v = m-pPoints->center;
195 						v.y /= pPoints->ratio;
196 						pPoints->angle2 = v.angle(); // + m_vMajorP.angle();
197 
198 			preview->addEntity(new RS_Line{preview.get(), pPoints->center, mouse});
199 
200 			preview->addEntity(new RS_Ellipse{preview.get(),
201 											  {pPoints->center, pPoints->m_vMajorP,
202 												  pPoints->ratio,
203 												  pPoints->angle1, pPoints->angle2
204 							   , false}});
205             drawPreview();
206         }
207 
208     default:
209         break;
210     }
211 
212     RS_DEBUG->print("RS_ActionDrawEllipseAxis::mouseMoveEvent end");
213 }
214 
215 
216 
mouseReleaseEvent(QMouseEvent * e)217 void RS_ActionDrawEllipseAxis::mouseReleaseEvent(QMouseEvent* e) {
218     // Proceed to next status
219     if (e->button()==Qt::LeftButton) {
220         RS_CoordinateEvent ce(snapPoint(e));
221         coordinateEvent(&ce);
222     }
223 
224     // Return to last status:
225     else if (e->button()==Qt::RightButton) {
226         deletePreview();
227         init(getStatus()-1);
228     }
229 }
230 
231 
coordinateEvent(RS_CoordinateEvent * e)232 void RS_ActionDrawEllipseAxis::coordinateEvent(RS_CoordinateEvent* e) {
233 	if (!e) return;
234 	RS_Vector const& mouse = e->getCoordinate();
235 
236     switch (getStatus()) {
237     case SetCenter:
238 		pPoints->center = mouse;
239         graphicView->moveRelativeZero(mouse);
240         setStatus(SetMajor);
241         break;
242 
243     case SetMajor:
244 		pPoints->m_vMajorP = mouse-	pPoints->center;
245         setStatus(SetMinor);
246         break;
247 
248     case SetMinor: {
249 			RS_Line line{pPoints->center-pPoints->m_vMajorP, pPoints->center+pPoints->m_vMajorP};
250             double d = line.getDistanceToPoint(mouse);
251 			pPoints->ratio = d/(line.getLength()/2);
252 			if (!pPoints->isArc) {
253                 trigger();
254                 setStatus(SetCenter);
255             } else {
256                 setStatus(SetAngle1);
257             }
258         }
259         break;
260 
261     case SetAngle1: {
262         //angle1 = center.angleTo(mouse);
263                 RS_Vector m = mouse;
264 				m.rotate(pPoints->center, -pPoints->m_vMajorP.angle());
265 				RS_Vector v = m-pPoints->center;
266 				v.y /= pPoints->ratio;
267 				pPoints->angle1 = v.angle();
268         setStatus(SetAngle2);
269                 } break;
270 
271     case SetAngle2: {
272         //angle2 = center.angleTo(mouse);
273                 RS_Vector m = mouse;
274 				m.rotate(pPoints->center, -pPoints->m_vMajorP.angle());
275 				RS_Vector v = m-pPoints->center;
276 				v.y /= pPoints->ratio;
277 				pPoints->angle2 = v.angle();
278         trigger();
279 		}
280 		break;
281 
282     default:
283         break;
284     }
285 }
286 
287 
288 
commandEvent(RS_CommandEvent * e)289 void RS_ActionDrawEllipseAxis::commandEvent(RS_CommandEvent* e) {
290     QString c = e->getCommand().toLower();
291 
292 	if (checkCommand("help", c)) {
293 		RS_DIALOGFACTORY->commandMessage(msgAvailableCommands()
294 										 + getAvailableCommands().join(", "));
295 		return;
296 	}
297 
298     switch (getStatus()) {
299     case SetMinor: {
300             bool ok;
301             double m = RS_Math::eval(c, &ok);
302             if (ok) {
303                 e->accept();
304 				pPoints->ratio = m / pPoints->m_vMajorP.magnitude();
305 				if (!pPoints->isArc) {
306                     trigger();
307                 } else {
308                     setStatus(SetAngle1);
309                 }
310 			} else
311 				RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
312         }
313         break;
314 
315     case SetAngle1: {
316             bool ok;
317             double a = RS_Math::eval(c, &ok);
318             if (ok) {
319                 e->accept();
320 				pPoints->angle1 = RS_Math::deg2rad(a);
321                 setStatus(SetAngle2);
322 			} else
323 				RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
324         }
325         break;
326 
327     case SetAngle2: {
328             bool ok;
329             double a = RS_Math::eval(c, &ok);
330             if (ok) {
331                 e->accept();
332 				pPoints->angle2 = RS_Math::deg2rad(a);
333                 trigger();
334 			} else
335 				RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
336         }
337         break;
338 
339     default:
340         break;
341     }
342 }
343 
344 
345 
getAvailableCommands()346 QStringList RS_ActionDrawEllipseAxis::getAvailableCommands() {
347     QStringList cmd;
348     return cmd;
349 }
350 
351 
352 
updateMouseButtonHints()353 void RS_ActionDrawEllipseAxis::updateMouseButtonHints() {
354 	switch (getStatus()) {
355 	case SetCenter:
356 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify ellipse center"),
357 											tr("Cancel"));
358 		break;
359 
360 	case SetMajor:
361 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify endpoint of major axis"),
362 											tr("Back"));
363 		break;
364 
365 	case SetMinor:
366 		RS_DIALOGFACTORY->updateMouseWidget(
367 					tr("Specify endpoint or length of minor axis:"),
368 					tr("Back"));
369 		break;
370 
371 	case SetAngle1:
372 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify start angle"),
373 											tr("Back"));
374 		break;
375 
376 	case SetAngle2:
377 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify end angle"),
378 											tr("Back"));
379 		break;
380 
381 	default:
382 		RS_DIALOGFACTORY->updateMouseWidget();
383 		break;
384 	}
385 }
386 
387 
388 
updateMouseCursor()389 void RS_ActionDrawEllipseAxis::updateMouseCursor() {
390     graphicView->setMouseCursor(RS2::CadCursor);
391 }
392 
393 // EOF
394