1 /****************************************************************************
2 **
3  * Draw circle by foci and a point on circle
4 
5 Copyright (C) 2012 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<vector>
24 #include<QAction>
25 #include <QMouseEvent>
26 #include "rs_actiondrawcircletan2.h"
27 
28 #include "rs_dialogfactory.h"
29 #include "rs_graphicview.h"
30 #include "rs_commandevent.h"
31 #include "rs_circle.h"
32 #include "rs_point.h"
33 #include "rs_preview.h"
34 #include "rs_debug.h"
35 
36 struct RS_ActionDrawCircleTan2::Points {
37 	RS_CircleData cData;
38 	RS_Vector coord;
39 	double radius{0.};
40 	bool valid{false};
41 	RS_VectorSolutions centers;
42 	std::vector<RS_AtomicEntity*> circles;
43 };
44 
45 /**
46  * Constructor.
47  *
48  */
RS_ActionDrawCircleTan2(RS_EntityContainer & container,RS_GraphicView & graphicView)49 RS_ActionDrawCircleTan2::RS_ActionDrawCircleTan2(
50         RS_EntityContainer& container,
51         RS_GraphicView& graphicView)
52     :RS_PreviewActionInterface("Draw circle inscribed",
53 							   container, graphicView)
54 	, pPoints(new Points{})
55 {
56 	actionType=RS2::ActionDrawCircleTan2;
57 }
58 
59 RS_ActionDrawCircleTan2::~RS_ActionDrawCircleTan2() = default;
60 
init(int status)61 void RS_ActionDrawCircleTan2::init(int status) {
62     RS_PreviewActionInterface::init(status);
63     if(status>=0) {
64         RS_Snapper::suspend();
65     }
66 
67     if (status==SetCircle1) {
68         pPoints->circles.clear();
69     }
70 }
71 
72 
finish(bool updateTB)73 void RS_ActionDrawCircleTan2::finish(bool updateTB){
74     if(pPoints->circles.size()>0){
75 		for(auto p: pPoints->circles){
76 			if(p) p->setHighlighted(false);
77         }
78         graphicView->redraw(RS2::RedrawDrawing);
79         pPoints->circles.clear();
80     }
81     RS_PreviewActionInterface::finish(updateTB);
82 }
83 
84 
trigger()85 void RS_ActionDrawCircleTan2::trigger() {
86 
87     RS_PreviewActionInterface::trigger();
88 
89 
90 	RS_Circle* circle=new RS_Circle(container, pPoints->cData);
91 
92     container->addEntity(circle);
93 
94     // upd. undo list:
95     if (document) {
96         document->startUndoCycle();
97         document->addUndoable(circle);
98         document->endUndoCycle();
99     }
100 
101 	for(auto p: pPoints->circles)
102 		p->setHighlighted(false);
103     graphicView->redraw(RS2::RedrawDrawing);
104     //    drawSnapper();
105 
106     pPoints->circles.clear();
107     setStatus(SetCircle1);
108 
109     RS_DEBUG->print("RS_ActionDrawCircleTan2::trigger():"
110                     " entity added: %d", circle->getId());
111 }
112 
113 
114 
mouseMoveEvent(QMouseEvent * e)115 void RS_ActionDrawCircleTan2::mouseMoveEvent(QMouseEvent* e) {
116     RS_DEBUG->print("RS_ActionDrawCircleTan2::mouseMoveEvent begin");
117 
118     switch(getStatus() ){
119     case SetCenter: {
120         //        RS_Entity*  en = catchEntity(e, enTypeList, RS2::ResolveAll);
121 		pPoints->coord= graphicView->toGraph(e->x(), e->y());
122         //        circles[getStatus()]=static_cast<RS_Line*>(en);
123         if(preparePreview()) {
124             deletePreview();
125 			RS_Circle* e=new RS_Circle(preview.get(), pPoints->cData);
126             preview->addEntity(e);
127 			for(size_t i=0; i< pPoints->centers.size(); ++i){
128 				preview->addEntity(new RS_Point(preview.get(), RS_PointData(pPoints->centers.at(i))));
129 			}
130             drawPreview();
131         }
132     }
133         break;
134     default:
135         break;
136     }
137     RS_DEBUG->print("RS_ActionDrawCircleTan2::mouseMoveEvent end");
138 }
139 
setRadius(const double & r)140 void RS_ActionDrawCircleTan2::setRadius(const double& r)
141 {
142 	pPoints->cData.radius=r;
143     if(getStatus() == SetCenter){
144 		pPoints->centers=RS_Circle::createTan2(pPoints->circles,
145 			pPoints->cData.radius);
146     }
147 }
148 
getCenters()149 bool RS_ActionDrawCircleTan2::getCenters(){
150     if(getStatus() != SetCircle2) return false;
151 	pPoints->centers=RS_Circle::createTan2(pPoints->circles,
152 				pPoints->cData.radius);
153 	pPoints->valid= (pPoints->centers.size()>0);
154 	return pPoints->valid;
155 }
156 
preparePreview()157 bool RS_ActionDrawCircleTan2::preparePreview(){
158 	if (pPoints->valid) {
159 		pPoints->cData.center=pPoints->centers.getClosest(pPoints->coord);
160     }
161 	return pPoints->valid;
162 }
163 
catchCircle(QMouseEvent * e)164 RS_Entity* RS_ActionDrawCircleTan2::catchCircle(QMouseEvent* e) {
165     RS_Entity*  en = catchEntity(e,enTypeList, RS2::ResolveAll);
166 	if (!en) return nullptr;
167 	if (!en->isVisible()) return nullptr;
168 	for (int i=0;i<getStatus();i++) {
169 		if(en->getId() == pPoints->circles[i]->getId()) return nullptr; //do not pull in the same line again
170     }
171 	if(en->getParent()) {
172         if ( en->getParent()->ignoredOnModification()){
173 			return nullptr;
174         }
175     }
176     return en;
177 }
178 
mouseReleaseEvent(QMouseEvent * e)179 void RS_ActionDrawCircleTan2::mouseReleaseEvent(QMouseEvent* e) {
180     // Proceed to next status
181     if (e->button()==Qt::LeftButton) {
182 
183         switch (getStatus()) {
184         case SetCircle1:
185         case SetCircle2: {
186             RS_Entity*  en = catchCircle(e);
187 			if (!en) return;
188             pPoints->circles.resize(getStatus());
189             pPoints->circles.push_back(static_cast<RS_AtomicEntity*>(en));
190             if(getStatus()==SetCircle1 || getCenters()){
191                 pPoints->circles.at(pPoints->circles.size()-1)->setHighlighted(true);
192                 graphicView->redraw(RS2::RedrawDrawing);
193                 setStatus(getStatus()+1);
194             }
195         }
196             break;
197         case SetCenter:
198 			pPoints->coord= graphicView->toGraph(e->x(), e->y());
199             if( preparePreview()) trigger();
200             break;
201 
202         default:
203             break;
204         }
205     } else if (e->button()==Qt::RightButton) {
206         // Return to last status:
207         if(getStatus()>0){
208             pPoints->circles[getStatus()-1]->setHighlighted(false);
209             pPoints->circles.pop_back();
210             graphicView->redraw(RS2::RedrawDrawing);
211             deletePreview();
212         }
213         init(getStatus()-1);
214     }
215 }
216 
217 
218 //void RS_ActionDrawCircleTan2::coordinateEvent(RS_CoordinateEvent* e) {
219 
220 //}
221 
222 //fixme, support command line
223 
224 /*
225 void RS_ActionDrawCircleTan2::commandEvent(RS_CommandEvent* e) {
226     QString c = e->getCommand().toLower();
227 
228 	if (checkCommand("help", c)) {
229 			RS_DIALOGFACTORY->commandMessage(msgAvailableCommands()
230 											 + getAvailableCommands().join(", "));
231 		return;
232 	}
233 
234     switch (getStatus()) {
235     case SetFocus1: {
236             bool ok;
237             double m = RS_Math::eval(c, &ok);
238 			if (ok) {
239                 ratio = m / major.magnitude();
240                 if (!isArc) {
241                     trigger();
242                 } else {
243                     setStatus(SetAngle1);
244                 }
245 			} else
246 					RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
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         break;
260 
261     case SetAngle2: {
262             bool ok;
263             double a = RS_Math::eval(c, &ok);
264 			if (ok) {
265                 angle2 = RS_Math::deg2rad(a);
266                 trigger();
267 			} else
268                     RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
269         }
270         break;
271 
272     default:
273         break;
274     }
275 }
276 */
277 
278 
showOptions()279 void RS_ActionDrawCircleTan2::showOptions() {
280 	RS_DEBUG->print("RS_ActionDrawCircleTan2::showOptions");
281 	RS_ActionInterface::showOptions();
282 
283 	RS_DIALOGFACTORY->requestOptions(this, true);
284 	RS_DEBUG->print("RS_ActionDrawCircleTan2::showOptions: OK");
285 }
286 
287 
288 
hideOptions()289 void RS_ActionDrawCircleTan2::hideOptions() {
290 	RS_ActionInterface::hideOptions();
291 
292 	RS_DIALOGFACTORY->requestOptions(this, false);
293 }
294 
295 
getAvailableCommands()296 QStringList RS_ActionDrawCircleTan2::getAvailableCommands() {
297 	return {};
298 }
299 
300 
301 
updateMouseButtonHints()302 void RS_ActionDrawCircleTan2::updateMouseButtonHints() {
303 	switch (getStatus()) {
304 	case SetCircle1:
305 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the first line/arc/circle"),
306 											tr("Cancel"));
307 		break;
308 
309 	case SetCircle2:
310 		RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the second line/arc/circle"),
311 											tr("Back"));
312 		break;
313 
314 	case SetCenter:
315 		RS_DIALOGFACTORY->updateMouseWidget(tr("Select the center of the tangent circle"),
316 											tr("Back"));
317 		break;
318 	default:
319 		RS_DIALOGFACTORY->updateMouseWidget();
320 		break;
321 	}
322 }
323 
324 
325 
updateMouseCursor()326 void RS_ActionDrawCircleTan2::updateMouseCursor() {
327     graphicView->setMouseCursor(RS2::SelectCursor);
328 }
329 
getRadius() const330 double RS_ActionDrawCircleTan2::getRadius() const{
331 	return pPoints->cData.radius;
332 }
333 
334 // EOF
335