11/*
22 * Copyright 2012, 2013 Thomas Schöps
3- * Copyright 2014, 2015 Kai Pastor
3+ * Copyright 2014-2020 Kai Pastor
44 *
55 * This file is part of OpenOrienteering.
66 *
2727#include < memory>
2828#include < stdexcept>
2929#include < type_traits>
30+ #include < utility>
3031
3132#include < QtGlobal>
3233#include < QDebug>
34+ #include < QFlags>
35+ #include < QHash>
36+ #include < QHashFunctions>
3337#include < QScopedPointer>
3438
39+ #include < clipper.hpp>
40+
3541#include " core/map.h"
3642#include " core/map_coord.h"
3743#include " core/map_part.h"
@@ -72,6 +78,128 @@ uint qHash(const IntPoint& point, uint seed)
7278
7379namespace OpenOrienteering {
7480
81+ namespace {
82+
83+ using PathObjects = BooleanTool::PathObjects;
84+
85+ using PathCoordInfo = std::pair<const PathPart*, const PathCoord*>;
86+ using PolyMap = QHash<ClipperLib::IntPoint, PathCoordInfo>;
87+
88+ /* *
89+ * Converts a ClipperLib::PolyTree to PathObjects.
90+ *
91+ * @see BooleanTool::outerPolyNodeToPathObjects()
92+ */
93+ static void polyTreeToPathObjects (
94+ const ClipperLib::PolyTree& tree,
95+ PathObjects& out_objects,
96+ const PathObject* proto,
97+ const PolyMap& polymap );
98+
99+ /* *
100+ * Converts a ClipperLib::PolyNode to PathObjects.
101+ *
102+ * The given ClipperLib::PolyNode must represent an outer polygon, not a hole.
103+ *
104+ * This method operates recursively on all outer children.
105+ */
106+ static void outerPolyNodeToPathObjects (
107+ const ClipperLib::PolyNode& node,
108+ PathObjects& out_objects,
109+ const PathObject* proto,
110+ const PolyMap& polymap );
111+
112+ /* *
113+ * Constructs ClipperLib::Paths from a PathObject.
114+ */
115+ static void pathObjectToPolygons (
116+ const PathObject* object,
117+ ClipperLib::Paths& polygons,
118+ PolyMap& polymap );
119+
120+ /* *
121+ * Reconstructs a PathObject from a polygon given as ClipperLib::Path.
122+ *
123+ * Curves are reconstructed with the help of the polymap, mapping locations
124+ * to path coords of the original objects.
125+ */
126+ static void polygonToPathPart (
127+ const ClipperLib::Path& polygon,
128+ const PolyMap& polymap,
129+ PathObject* object );
130+
131+ /* *
132+ * Tries to reconstruct a straight or curved segment with given start and
133+ * end indices from the polygon.
134+ * The first coordinate of the segment is assumed to be already added.
135+ */
136+ static void rebuildSegment (
137+ ClipperLib::Path::size_type start_index,
138+ ClipperLib::Path::size_type end_index,
139+ bool sequence_increasing,
140+ const ClipperLib::Path& polygon,
141+ const PolyMap& polymap,
142+ PathObject* object );
143+
144+ /* *
145+ * Approximates a curved segment from the result polygon alone.
146+ */
147+ static void rebuildSegmentFromPathOnly (
148+ const ClipperLib::IntPoint& start_point,
149+ const ClipperLib::IntPoint& second_point,
150+ const ClipperLib::IntPoint& second_last_point,
151+ const ClipperLib::IntPoint& end_point,
152+ PathObject* object );
153+
154+ /* *
155+ * Special case of rebuildSegment() for straight or very short lines.
156+ */
157+ static void rebuildTwoIndexSegment (
158+ ClipperLib::Path::size_type start_index,
159+ ClipperLib::Path::size_type end_index,
160+ bool sequence_increasing,
161+ const ClipperLib::Path& polygon,
162+ const PolyMap& polymap,
163+ PathObject* object );
164+
165+ /* *
166+ * Reconstructs one polygon coordinate and adds it to the object.
167+ *
168+ * Uses the polymap to check whether the coordinate should be a dash point.
169+ */
170+ static void rebuildCoordinate (
171+ ClipperLib::Path::size_type index,
172+ const ClipperLib::Path& polygon,
173+ const PolyMap& polymap,
174+ PathObject* object,
175+ bool start_new_part = false );
176+
177+ /* *
178+ * Compares a PathObject segment to a ClipperLib::Path polygon segment.
179+ *
180+ * Returns true if the segments match. In this case, the out_... parameters are set.
181+ *
182+ * @param original The original PathObject.
183+ * @param coord_index The index of the segment start at the original.
184+ * @param polygon The ClipperLib::Path polygon.
185+ * @param start_index The start of the segment at the polygon.
186+ * @param end_index The end of the segment at the polygon.
187+ * @param out_coords_increasing If the segments match, will be set to
188+ * either true if a matching segment's point at coord_index corresponds to the point at start_index,
189+ * or false otherwise.
190+ * @param out_is_curve If the segments match, will be set to
191+ * either true if the original segment is a curve,
192+ * or false otherwise.
193+ */
194+ static bool checkSegmentMatch (
195+ const PathObject* original,
196+ int coord_index,
197+ const ClipperLib::Path& polygon,
198+ ClipperLib::Path::size_type start_index,
199+ ClipperLib::Path::size_type end_index,
200+ bool & out_coords_increasing,
201+ bool & out_is_curve );
202+
75203/* *
76204 * Removes flags from the coordinate to be able to use it in the reconstruction.
77205 */
@@ -97,6 +225,8 @@ bool operator==(const ClipperLib::IntPoint& lhs, const MapCoord& rhs)
97225 return rhs == lhs;
98226}
99227
228+ } // namespace
229+
100230
101231
102232// ### BooleanTool ###
@@ -111,7 +241,7 @@ BooleanTool::BooleanTool(Operation op, Map* map)
111241bool BooleanTool::execute ()
112242{
113243 // Check basic prerequisite
114- Object* const primary_object = map->getFirstSelectedObject ();
244+ const Object* const primary_object = map->getFirstSelectedObject ();
115245 if (primary_object->getType () != Object::Path)
116246 {
117247 qWarning (" The first selected object must be a path." );
@@ -211,7 +341,7 @@ bool BooleanTool::executePerSymbol()
211341 return have_changes;
212342}
213343
214- bool BooleanTool::executeForObjects (PathObject* subject, PathObjects& in_objects, PathObjects& out_objects, CombinedUndoStep& undo_step)
344+ bool BooleanTool::executeForObjects (const PathObject* subject, const PathObjects& in_objects, PathObjects& out_objects, CombinedUndoStep& undo_step)
215345{
216346 if (!executeForObjects (subject, in_objects, out_objects))
217347 {
@@ -258,7 +388,7 @@ bool BooleanTool::executeForObjects(PathObject* subject, PathObjects& in_objects
258388 return true ;
259389}
260390
261- bool BooleanTool::executeForObjects (PathObject* subject, PathObjects& in_objects, PathObjects& out_objects)
391+ bool BooleanTool::executeForObjects (const PathObject* subject, const PathObjects& in_objects, PathObjects& out_objects) const
262392{
263393 // Convert the objects to Clipper polygons and
264394 // create a hash map, mapping point positions to the PathCoords.
@@ -312,40 +442,7 @@ bool BooleanTool::executeForObjects(PathObject* subject, PathObjects& in_objects
312442 return success;
313443}
314444
315- void BooleanTool::polyTreeToPathObjects (const ClipperLib::PolyTree& tree, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap)
316- {
317- for (int i = 0 , count = tree.ChildCount (); i < count; ++i)
318- outerPolyNodeToPathObjects (*tree.Childs [i], out_objects, proto, polymap);
319- }
320-
321- void BooleanTool::outerPolyNodeToPathObjects (const ClipperLib::PolyNode& node, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap)
322- {
323- auto object = std::unique_ptr<PathObject>{ proto->duplicate () };
324- object->clearCoordinates ();
325-
326- try
327- {
328- polygonToPathPart (node.Contour , polymap, object.get ());
329- for (int i = 0 , i_count = node.ChildCount (); i < i_count; ++i)
330- {
331- polygonToPathPart (node.Childs [i]->Contour , polymap, object.get ());
332-
333- // Add outer polygons contained by (nested within) holes ...
334- for (int j = 0 , j_count = node.Childs [i]->ChildCount (); j < j_count; ++j)
335- outerPolyNodeToPathObjects (*node.Childs [i]->Childs [j], out_objects, proto, polymap);
336- }
337-
338- out_objects.push_back (object.release ());
339- }
340- catch (std::range_error&)
341- {
342- // Do nothing
343- }
344- }
345-
346-
347-
348- void BooleanTool::executeForLine (const PathObject* area, const PathObject* line, BooleanTool::PathObjects& out_objects)
445+ void BooleanTool::executeForLine (const PathObject* area, const PathObject* line, BooleanTool::PathObjects& out_objects) const
349446{
350447 if (op != BooleanTool::Intersection && op != BooleanTool::Difference)
351448 {
@@ -428,7 +525,42 @@ void BooleanTool::executeForLine(const PathObject* area, const PathObject* line,
428525 }
429526}
430527
431- void BooleanTool::pathObjectToPolygons (
528+
529+
530+ namespace {
531+
532+ void polyTreeToPathObjects (const ClipperLib::PolyTree& tree, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap)
533+ {
534+ for (int i = 0 , count = tree.ChildCount (); i < count; ++i)
535+ outerPolyNodeToPathObjects (*tree.Childs [i], out_objects, proto, polymap);
536+ }
537+
538+ void outerPolyNodeToPathObjects (const ClipperLib::PolyNode& node, PathObjects& out_objects, const PathObject* proto, const PolyMap& polymap)
539+ {
540+ auto object = std::unique_ptr<PathObject>{ proto->duplicate () };
541+ object->clearCoordinates ();
542+
543+ try
544+ {
545+ polygonToPathPart (node.Contour , polymap, object.get ());
546+ for (int i = 0 , i_count = node.ChildCount (); i < i_count; ++i)
547+ {
548+ polygonToPathPart (node.Childs [i]->Contour , polymap, object.get ());
549+
550+ // Add outer polygons contained by (nested within) holes ...
551+ for (int j = 0 , j_count = node.Childs [i]->ChildCount (); j < j_count; ++j)
552+ outerPolyNodeToPathObjects (*node.Childs [i]->Childs [j], out_objects, proto, polymap);
553+ }
554+
555+ out_objects.push_back (object.release ());
556+ }
557+ catch (std::range_error&)
558+ {
559+ // Do nothing
560+ }
561+ }
562+
563+ void pathObjectToPolygons (
432564 const PathObject* object,
433565 ClipperLib::Paths& polygons,
434566 PolyMap& polymap)
@@ -475,7 +607,7 @@ void BooleanTool::pathObjectToPolygons(
475607 }
476608}
477609
478- void BooleanTool:: polygonToPathPart (const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object)
610+ void polygonToPathPart (const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object)
479611{
480612 auto num_points = polygon.size ();
481613 if (num_points < 3 )
@@ -538,7 +670,7 @@ void BooleanTool::polygonToPathPart(const ClipperLib::Path& polygon, const PolyM
538670 if (cur_info.first && cur_info.first == new_info.first )
539671 {
540672 // Same original part
541- auto cur_coord_index = cur_info.second ->index ;
673+ auto cur_coord_index = cur_info.second ->index ; // NOLINT
542674 const auto cur_coord = cur_info.first ->path ->getCoordinate (cur_coord_index);
543675
544676 auto new_coord_index = new_info.second ->index ;
@@ -644,7 +776,7 @@ void BooleanTool::polygonToPathPart(const ClipperLib::Path& polygon, const PolyM
644776 object->parts ().back ().connectEnds ();
645777}
646778
647- void BooleanTool:: rebuildSegment (
779+ void rebuildSegment (
648780 ClipperLib::Path::size_type start_index,
649781 ClipperLib::Path::size_type end_index,
650782 bool sequence_increasing,
@@ -938,7 +1070,7 @@ void BooleanTool::rebuildSegment(
9381070 }
9391071}
9401072
941- void BooleanTool:: rebuildSegmentFromPathOnly (
1073+ void rebuildSegmentFromPathOnly (
9421074 const ClipperLib::IntPoint& start_point,
9431075 const ClipperLib::IntPoint& second_point,
9441076 const ClipperLib::IntPoint& second_last_point,
@@ -961,7 +1093,7 @@ void BooleanTool::rebuildSegmentFromPathOnly(
9611093 object->addCoordinate (end_point_c);
9621094}
9631095
964- void BooleanTool:: rebuildTwoIndexSegment (
1096+ void rebuildTwoIndexSegment (
9651097 ClipperLib::Path::size_type start_index,
9661098 ClipperLib::Path::size_type end_index,
9671099 bool sequence_increasing,
@@ -1030,7 +1162,7 @@ void BooleanTool::rebuildTwoIndexSegment(
10301162 }
10311163}
10321164
1033- void BooleanTool:: rebuildCoordinate (
1165+ void rebuildCoordinate (
10341166 ClipperLib::Path::size_type index,
10351167 const ClipperLib::Path& polygon,
10361168 const PolyMap& polymap,
@@ -1049,7 +1181,7 @@ void BooleanTool::rebuildCoordinate(
10491181 object->addCoordinate (coord, start_new_part);
10501182}
10511183
1052- bool BooleanTool:: checkSegmentMatch (
1184+ bool checkSegmentMatch (
10531185 const PathObject* original,
10541186 int coord_index,
10551187 const ClipperLib::Path& polygon,
@@ -1082,5 +1214,7 @@ bool BooleanTool::checkSegmentMatch(
10821214 return found;
10831215}
10841216
1217+ } // namespace
1218+
10851219
10861220} // namespace OpenOrienteering
0 commit comments