1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2015-2017 Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifndef OPENORIENTEERING_OBJECT_MOVER_H
22 #define OPENORIENTEERING_OBJECT_MOVER_H
23 
24 #include <unordered_map>
25 #include <unordered_set>
26 #include <utility>
27 #include <vector>
28 
29 #include <QtGlobal>
30 
31 #include "core/map_coord.h"
32 
33 namespace OpenOrienteering {
34 
35 class Map;
36 class Object;
37 class PathObject;
38 class TextObject;
39 
40 using SelectionInfoVector = std::vector<std::pair<int, Object*>>;
41 
42 
43 /**
44  * Implements the logic to move sets of objects and / or object points for edit tools.
45  */
46 class ObjectMover
47 {
48 public:
49 	enum HandleOpMode {
50 		Never, ///< Never move opposite curve handles
51 		Click, ///< Move opposite handles once they get aligned in line, move them
52 		       ///< together from that point on
53 	};
54 
55 	/** Creates a mover for the map with the given cursor start position. */
56 	ObjectMover(Map* map, const MapCoordF& start_pos);
57 
58 	/** Sets the start position. */
59 	void setStartPos(const MapCoordF& start_pos);
60 
61 	/** Sets corner point tolerance. A curve anchor point is considered a corner
62 	 * point during editing when the moving curve handle is more than
63 	 * corner_tolerance away from the direction line set by the opposite handle.
64 	 *
65 	 * @param corner_tolerance Maximum difference in vector directions
66 	 *                         in millimeters.
67 	 */
68 	void setCornerTolerance(qreal corner_tolerance);
69 
70 	/** Adds an object to the set of elements to move. */
71 	void addObject(Object* object);
72 
73 	/** Adds a point to the set of elements to move. */
74 	void addPoint(PathObject* object, MapCoordVector::size_type point_index);
75 
76 	/** Adds a line to the set of elements to move. */
77 	void addLine(PathObject* object, MapCoordVector::size_type start_point_index);
78 
79 	/** Adds a text handle to the set of elements to move. */
80 	void addTextHandle(TextObject* text, MapCoordVector::size_type handle);
81 
82 	/**
83 	 * Moves the elements.
84 	 * @param move_opposite_handles Opposite curve handles either operate
85 	 *        in "click in" mode or move independently.
86 	 * @param out_dx returns the move along the x coordinate in map units
87 	 * @param out_dy returns the move along the y coordinate in map units
88 	 */
89 	void move(const MapCoordF& cursor_pos, HandleOpMode move_opposite_handles,
90 	          qint32* out_dx = nullptr, qint32* out_dy = nullptr);
91 
92 	/** Overload of move() taking delta values. */
93 	void move(qint32 dx, qint32 dy, HandleOpMode move_opposite_handles);
94 
95 private:
96 	using ObjectSet = std::unordered_set<Object*>;
97 	using CoordIndexSet = std::unordered_set<MapCoordVector::size_type>;
98 
99 	CoordIndexSet* insertPointObject(PathObject* object);
100 	void calculateConstraints();
101 
102 	// Basic information
103 	MapCoordF start_position;
104 	qreal corner_tolerance {};
105 	qint32 prev_drag_x {};
106 	qint32 prev_drag_y {};
107 	ObjectSet objects;
108 	std::unordered_map<PathObject*, CoordIndexSet> points;
109 	std::unordered_map<TextObject*, MapCoordVector::size_type> text_handles;
110 
111 	/** Constraints calculated from the basic information */
112 	struct OppositeHandleConstraint
113 	{
114 		/** Object to which the constraint applies */
115 		PathObject* object;
116 		/** Index of moved handle */
117 		MapCoordVector::size_type moved_handle_index;
118 		/** Index of opposite handle */
119 		MapCoordVector::size_type opposite_handle_index;
120 		/** Index of center point in the middle of the handles */
121 		MapCoordVector::size_type curve_anchor_index;
122 		/** Middle point is a corner point */
123 		bool anchor_is_corner;
124 		/** Distance of opposite handle to center point */
125 		qreal opposite_handle_dist;
126 		/** Original position of the opposite handle */
127 		MapCoord opposite_handle_original_position;
128 	};
129 	std::vector<OppositeHandleConstraint> handle_constraints;
130 	bool constraints_calculated {true};
131 };
132 
133 
134 }  // namespace OpenOrienteering
135 
136 #endif
137