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