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