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 
27 #include <QAction>
28 #include <QMouseEvent>
29 #include "rs_actionpolylinesegment.h"
30 
31 #include "rs_dialogfactory.h"
32 #include "rs_graphicview.h"
33 #include "rs_arc.h"
34 #include "rs_line.h"
35 #include "rs_polyline.h"
36 #include "rs_debug.h"
37 
38 namespace {
39 std::initializer_list<RS2::EntityType>
40 entityType{RS2::EntityLine, RS2::EntityPolyline, RS2::EntityArc};
41 }
42 
RS_ActionPolylineSegment(RS_EntityContainer & container,RS_GraphicView & graphicView)43 RS_ActionPolylineSegment::RS_ActionPolylineSegment(RS_EntityContainer& container,
44         RS_GraphicView& graphicView)
45         :RS_PreviewActionInterface("Create Polyline Existing from Segments",
46 						   container, graphicView) {
47 	actionType=RS2::ActionPolylineSegment;
48 }
49 
init(int status)50 void RS_ActionPolylineSegment::init(int status) {
51     RS_ActionInterface::init(status);
52 	targetEntity = nullptr;
53 	//Experimental feature: trigger action, if already has selected entities
54 	if (container->countSelected(true, entityType)) {
55 		//find a selected entity
56 		//TODO, find a better starting point
57 		for (RS_Entity* e : *container) {
58 			if (e->isSelected() &&
59 					std::count(entityType.begin(), entityType.end(), e->rtti())) {
60 				targetEntity = e;
61 				break;
62 			}
63 		}
64 		if (targetEntity) {
65 			convertPolyline(targetEntity, true);
66 			RS_DIALOGFACTORY->commandMessage(tr("Polyline created"));
67 			graphicView->redraw();
68 			RS_DIALOGFACTORY->updateSelectionWidget(container->countSelected(),container->totalSelectedLength());
69 			finish(false);
70 			return;
71 		}
72 	}
73 }
74 
75 /**
76  * Utility function for convertPolyline
77  * Appends in current the vertex from toAdd reversing if reversed is true
78  * The first vertex is not added and the last is returned instead of added
79  *
80  * @retval RS_Vector with the last vertex not inserted
81  *
82  * @author Rallaz
83  */
appendPol(RS_Polyline * current,RS_Polyline * toAdd,bool reversed)84 RS_Vector RS_ActionPolylineSegment::appendPol(RS_Polyline *current, RS_Polyline *toAdd, bool reversed) {
85 
86     QList<RS_Entity*> entities;
87 
88 	for(auto v: *toAdd){
89         if (reversed)
90             entities.prepend(v);
91         else
92             entities.append(v);
93     }
94 //bad polyline without vertex
95     if (entities.isEmpty())
96         return RS_Vector(false);
97 
98     double bulge=0.0;
99     RS_Entity* e = entities.takeFirst() ;
100 
101 //First polyline vertex
102     if (e->rtti() == RS2::EntityArc) {
103         if (reversed)
104             current->setNextBulge(((RS_Arc*)e)->getBulge()*-1);
105         else
106             current->setNextBulge(((RS_Arc*)e)->getBulge());
107     }
108 
109     while (!entities.isEmpty()) {
110          e = entities.takeFirst();
111          if (e->rtti()==RS2::EntityArc) {
112          if (reversed)
113              bulge = ((RS_Arc*)e)->getBulge()*-1;
114          else
115              bulge = ((RS_Arc*)e)->getBulge();
116          } else
117              bulge = 0.0;
118          if (reversed)
119              current->addVertex(e->getEndpoint(),bulge, false);
120          else
121              current->addVertex(e->getStartpoint(),bulge, false);
122     }
123     if (reversed)
124         return e->getStartpoint();
125     else
126         return e->getEndpoint();
127 }
128 
129 /**
130  * Rearranges the lines, arcs or opened polylines entities
131  *  in this container, non-recoursive.
132  * document can not be null
133  *
134  * @retval true contour are closed
135  * @retval false if the contour is not closed
136  *
137  * @author Rallaz
138  */
convertPolyline(RS_Entity * selectedEntity,bool useSelected)139 bool RS_ActionPolylineSegment::convertPolyline(RS_Entity* selectedEntity, bool useSelected) {
140 
141     RS_DEBUG->print("RS_ActionPolylineSegment::convertPolyline");
142 
143     QList<RS_Entity*> remaining;
144     QList<RS_Entity*> completed;
145     RS_Vector start = selectedEntity->getStartpoint();
146     RS_Vector end = selectedEntity->getEndpoint();
147 	if (!useSelected || (selectedEntity && selectedEntity->isSelected()))
148 		completed.append(selectedEntity);
149 //get list with useful entities
150 
151 	for (RS_Entity* e1 : *container) {
152 		if (useSelected && !e1->isSelected()) continue;
153         if (e1->isLocked() || !e1->isVisible() || e1 == selectedEntity) continue;
154         if (e1->rtti()==RS2::EntityLine || e1->rtti()==RS2::EntityArc
155                 || e1->rtti()==RS2::EntityPolyline) {
156             if (targetEntity->rtti()==RS2::EntityPolyline && ((RS_Polyline*)targetEntity)->isClosed())
157                 continue;
158             if (e1 == selectedEntity)
159                 continue;
160             remaining.append(e1);
161         }
162     }
163 
164     // find all connected entities:
165     bool done = true;
166     do {
167         done = true;
168         for (int i=(remaining.size() -1) ; i>=0; --i) {
169             RS_Entity* e=remaining.at(i);
170             if (e->getEndpoint().distanceTo(start) < 1.0e-4) {
171                 completed.prepend( e);
172                 start = e->getStartpoint();
173                 remaining.removeAt(i);
174                 done = false;
175             } else if (e->getStartpoint().distanceTo(start) < 1.0e-4) {
176                 completed.prepend( e);
177                 start = e->getEndpoint();
178                 remaining.removeAt(i);
179                 done = false;
180             } else if (e->getEndpoint().distanceTo(end) < 1.0e-4) {
181                 completed.append( e);
182                 end = e->getStartpoint();
183                 remaining.removeAt(i);
184                 done = false;
185             } else if (e->getStartpoint().distanceTo(end) < 1.0e-4) {
186                 completed.append( e);
187                 end = e->getEndpoint();
188                 remaining.removeAt(i);
189                 done = false;
190             }
191         }
192     } while (!done);
193 
194 //cleanup for no more needed list
195     remaining.clear();
196 
197     bool closed = false;
198     if (document) {
199         document->startUndoCycle();
200 
201         bool revert = false;
202         double bulge = 0.0;
203         if (end.distanceTo(start) < 1.0e-4)
204             closed = true;
205 
206         RS_Polyline* newPolyline = new RS_Polyline(container, RS_PolylineData(RS_Vector(false), RS_Vector(false), closed));
207         newPolyline->setLayerToActive();
208         newPolyline->setPenToActive();
209 
210 //complete polyline
211         end =start;
212         while (!completed.isEmpty()) {
213             RS_Entity* e2= completed.takeFirst();
214             e2->setUndoState(true);
215             document->addUndoable(e2);
216             if (e2->getStartpoint().distanceTo(end) < 1.0e-4) {
217                 revert = false;
218                 start = e2->getStartpoint();
219                 end = e2->getEndpoint();
220             } else {
221                 revert = true;
222                 start = e2->getEndpoint();
223                 end = e2->getStartpoint();
224             }
225             if (e2->rtti()==RS2::EntityArc) {
226                 if (revert)
227                     bulge = ((RS_Arc*)e2)->getBulge()*-1;
228                 else
229                     bulge = ((RS_Arc*)e2)->getBulge();
230             } else
231                 bulge = 0.0;
232             if (e2->rtti()==RS2::EntityPolyline) {
233                 newPolyline->addVertex(start, bulge);
234                 end = appendPol(newPolyline, (RS_Polyline*)e2, revert);
235             } else
236                 newPolyline->addVertex(start, bulge);
237         }
238 
239         if (closed)
240             newPolyline->setClosed(true);
241         else
242             newPolyline->addVertex(end, bulge);
243         newPolyline->endPolyline();
244         container->addEntity(newPolyline);
245 
246         if (graphicView) {
247                 graphicView->drawEntity(newPolyline);
248         }
249 
250         document->addUndoable(newPolyline);
251         document->endUndoCycle();
252     }
253     RS_DEBUG->print("RS_ActionPolylineSegment::convertPolyline: OK");
254     return closed;
255 }
256 
trigger()257 void RS_ActionPolylineSegment::trigger() {
258 
259     RS_DEBUG->print("RS_ActionPolylineSegment::trigger()");
260 
261         if (targetEntity /*&& selectedSegment && targetPoint.valid */) {
262         targetEntity->setHighlighted(false);
263         graphicView->drawEntity(targetEntity);
264 //RLZ: do not use container->optimizeContours(); because it invalidate targetEntity
265 //        container->optimizeContours();
266         convertPolyline(targetEntity);
267 
268 		targetEntity = nullptr;
269         setStatus(ChooseEntity);
270 
271         RS_DIALOGFACTORY->updateSelectionWidget(container->countSelected(),container->totalSelectedLength());
272     }
273 ////////////////////////////////////////2006/06/15
274                 graphicView->redraw();
275 ////////////////////////////////////////
276 }
277 
mouseReleaseEvent(QMouseEvent * e)278 void RS_ActionPolylineSegment::mouseReleaseEvent(QMouseEvent* e) {
279     if (e->button()==Qt::LeftButton) {
280         switch (getStatus()) {
281         case ChooseEntity:
282 			targetEntity = catchEntity(e, entityType);
283 
284 			if (targetEntity==nullptr) {
285                 RS_DIALOGFACTORY->commandMessage(tr("No Entity found."));
286 			} else if (targetEntity->rtti()==RS2::EntityPolyline && ((RS_Polyline*)targetEntity)->isClosed()){
287                 RS_DIALOGFACTORY->commandMessage(
288                         tr("Entity can not be a closed polyline."));
289             } else {
290 				//TODO, verify topology of selected
291                 targetEntity->setHighlighted(true);
292                 graphicView->drawEntity(targetEntity);
293 
294 //                setStatus(SetReferencePoint);
295 ////////////////////////////////////////2006/06/15
296                 graphicView->redraw();
297 ////////////////////////////////////////
298                 trigger();
299             }
300             break;
301         default:
302             break;
303         }
304     } else if (e->button()==Qt::RightButton) {
305         deleteSnapper();
306         if (targetEntity) {
307             targetEntity->setHighlighted(false);
308             graphicView->drawEntity(targetEntity);
309 ////////////////////////////////////////2006/06/15
310                 graphicView->redraw();
311 ////////////////////////////////////////
312         }
313         init(getStatus()-1);
314     }
315 /*    if (e->button())==Qt::LeftButton) {
316         RS_CoordinateEvent ce(snapPoint(e));
317         coordinateEvent(&ce);
318     } else if (RS2::qtToRsButtonState(e->button())==RS2::RightButton) {
319         deletePreview();
320         deleteSnapper
321         init(getStatus()-1);
322     }
323 */
324 }
325 
updateMouseButtonHints()326 void RS_ActionPolylineSegment::updateMouseButtonHints() {
327     switch (getStatus()) {
328     case ChooseEntity:
329         RS_DIALOGFACTORY->updateMouseWidget(tr("Choose one of the segments on the original polyline"),
330                                             tr("Cancel"));
331         break;
332     default:
333 		RS_DIALOGFACTORY->updateMouseWidget();
334         break;
335     }
336 }
337 
338 
updateMouseCursor()339 void RS_ActionPolylineSegment::updateMouseCursor() {
340     graphicView->setMouseCursor(RS2::SelectCursor);
341 }
342 
343 // EOF
344