1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2013-2019 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 
22 #ifndef OPENORIENTEERING_OBJECT_H
23 #define OPENORIENTEERING_OBJECT_H
24 
25 #include <limits>
26 #include <vector>
27 #include <utility>
28 
29 #include <QtGlobal>
30 #include <QHash>
31 #include <QRectF>
32 #include <QString>
33 // IWYU pragma: no_include <QTransform>
34 
35 #include "core/map_coord.h"
36 #include "core/path_coord.h"
37 #include "core/virtual_path.h"
38 #include "core/renderables/renderable.h"
39 #include "core/symbols/symbol.h"
40 
41 class QTransform;
42 class QXmlStreamReader;
43 class QXmlStreamWriter;
44 // IWYU pragma: no_forward_declare QRectF
45 
46 namespace OpenOrienteering {
47 
48 class Map;
49 class PointObject;
50 class PathObject;
51 class TextObject;
52 class VirtualCoordVector;
53 
54 
55 /**
56  * Abstract base class which combines coordinates and a symbol to form an object
57  * (in a map, or inside a point symbol as one of its elements).
58  *
59  * Every object must have a symbol. If the symbol is not known, one of the
60  * "undefined" symbols from the Map class can be used.
61  *
62  * From the object's data, a call to update() will generate the object's "output",
63  * that is a set of renderables and the calculation of the object's extent (bounding box).
64  * The renderables can then be inserted into a map where they are used to display the object.
65  */
66 class Object  // clazy:exclude=copyable-polymorphic
67 {
68 friend class ObjectRenderables;
69 friend class OCAD8FileImport;
70 friend class XMLImportExport;
71 public:
72 	/** Enumeration of possible object types. */
73 	enum Type
74 	{
75 		/**
76 		 * A single coordinate, no further coordinates can be added.
77 		 * For point symbols only.
78 		 */
79 		Point = 0,
80 
81 		/**
82 		 * A dynamic list of coordinates.
83 		 * For line, area and combined symbols.
84 		 */
85 		Path = 1,
86 
87 		/**
88 		 * Either one or two coordinates, for single-anchor or box text.
89 		 * For text symbols only.
90 		 */
91 		Text = 4
92 	};
93 
94 	/** Creates an empty object with the given type and (optional) symbol. */
95 	explicit Object(Type type, const Symbol* symbol = nullptr);
96 
97 	/** Creates an empty object with the given type, symbol, coords and (optional) map. */
98 	explicit Object(Type type, const Symbol* symbol, MapCoordVector coords, Map* map = nullptr);
99 
100 protected:
101 	/**
102 	 * Constructs a Object, initialized from the given prototype.
103 	 *
104 	 * Note that the object is NOT added to a map, and consequently,
105 	 * the map pointer is initialized to nullptr.
106 	 */
107 	explicit Object(const Object& proto);
108 
109 public:
110 	/** Destructs the object. */
111 	virtual ~Object();
112 
113 	Object& operator=(const Object& other) = delete;
114 
115 	virtual void copyFrom(const Object& other);
116 
117 	/** Creates an identical copy of the object.
118 	 *
119 	 * This needs to be implemented in non-abstract subclasses.
120 	 * Implementation should use the copy constructor to ensure proper initialization.
121 	 */
122 	virtual Object* duplicate() const = 0;
123 
124 	/**
125 	 * Checks for equality with another object.
126 	 *
127 	 * If compare_symbol is set, also the symbols are compared for having the same properties.
128 	 * Note that the map property is not compared.
129 	 */
130 	bool equals(const Object* other, bool compare_symbol) const;
131 
132 	virtual bool validate() const;
133 
134 	/** Returns the object type determined by the subclass */
135 	inline Type getType() const;
136 
137 	/** Convenience cast to PointObject with type checking */
138 	PointObject* asPoint();
139 	/** Convenience cast to PointObject with type checking */
140 	const PointObject* asPoint() const;
141 	/** Convenience cast to PathObject with type checking */
142 	PathObject* asPath();
143 	/** Convenience cast to PathObject with type checking */
144 	const PathObject* asPath() const;
145 	/** Convenience cast to TextObject with type checking */
146 	TextObject* asText();
147 	/** Convenience cast to TextObject with type checking */
148 	const TextObject* asText() const;
149 
150 	/** Saves the object in xml format to the given stream. */
151 	void save(QXmlStreamWriter& xml) const;
152 	/**
153 	 * Loads the object in xml format from the given stream.
154 	 * @param xml The stream to load the object from, must be at the correct tag.
155 	 * @param map The map in which the object will be inserted.
156 	 *            This value will be assigned to the object's map pointer
157 	 *            It may be nullptr.
158 	 * @param symbol_dict A dictionary mapping symbol IDs to symbol pointers.
159 	 * @param symbol If set, this symbol will be assigned to the object, rather
160 	 *               than reading the symbol from the stream.
161 	 */
162 	static Object* load(QXmlStreamReader& xml, Map* map, const SymbolDictionary& symbol_dict, const Symbol* symbol = nullptr);
163 
164 
165 	/**
166 	 * Returns the rotation for this object (in radians).
167 	 *
168 	 * The interpretation of this value depends the object's symbol.
169 	 */
getRotation()170 	qreal getRotation() const { return rotation; }
171 
172 	/**
173 	 * Sets the object's rotation (in radians).
174 	 *
175 	 * The interpretation of this value depends the object's symbol.
176 	 * It may have no effect at all.
177 	 * The value must not be NaN.
178 	 */
179 	void setRotation(qreal new_rotation);
180 
181 
182 	/**
183 	 * If the output_dirty flag is set, regenerates output and extent, and updates the object's map (if set).
184 	 *
185 	 * Returns true if output was dirty.
186 	 */
187 	bool update() const;
188 
189 	/**
190 	 * Always regenerates output and extent, and updates the object's map (if set).
191 	 */
192 	void forceUpdate() const;
193 
194 
195 	/** Moves the whole object
196 	 * @param dx X offset in native map coordinates.
197 	 * @param dy Y offset in native map coordinates.
198 	 */
199 	void move(qint32 dx, qint32 dy);
200 
201 	/** Moves the whole object by the given offset. */
202 	void move(const MapCoord& offset);
203 
204 	/** Scales all coordinates, with the given scaling center */
205 	virtual void scale(const MapCoordF& center, double factor);
206 
207 	/** Scales all coordinates, with the center (0, 0).
208 	 * @param factor_x horizontal scaling factor
209 	 * @param factor_y vertical scaling factor
210 	 */
211 	virtual void scale(double factor_x, double factor_y);
212 
213 	/** Rotates the whole object around the center point.
214 	 *  The angle must be given in radians. */
215 	void rotateAround(const MapCoordF& center, qreal angle);
216 
217 	/** Rotates the whole object around the center (0, 0).
218 	 *  The angle must be given in radians. */
219 	void rotate(qreal angle);
220 
221 	/**
222 	 * Apply a transformation to all coordinates.
223 	 *
224 	 * \todo Handle rotation of patterns or text (?)
225 	 */
226 	virtual void transform(const QTransform& t) = 0;
227 
228 	/**
229 	 * Checks if the given coord, with the given tolerance, is on this object.
230 	 *
231 	 * With extended_selection, the coord is on point objects always
232 	 * if it is within their extent, otherwise it has to be close to
233 	 * their midpoint. Returns a Symbol::Type which specifies on which
234 	 * symbol type the coord is
235 	 * (important for combined symbols which can have areas and lines).
236 	 */
237 	int isPointOnObject(const MapCoordF& coord, qreal tolerance, bool treat_areas_as_paths, bool extended_selection) const;
238 
239 	/**
240 	 * Checks if a path point (excluding curve control points) is included in the given box.
241 	 */
242 	virtual bool intersectsBox(const QRectF& box) const = 0;
243 
244 	/** Takes ownership of the renderables */
245 	void takeRenderables();
246 
247 	/** Deletes the renderables (and extent), undoing update() */
248 	void clearRenderables();
249 
250 	/** Returns the renderables, read-only */
251 	const ObjectRenderables& renderables() const;
252 
253 	// Getters / Setters
254 
255 	/**
256 	 * Returns the raw MapCoordVector of the object.
257 	 * It's layout and interpretation depends on the object type.
258 	 */
259 	const MapCoordVector& getRawCoordinateVector() const;
260 
261 	/** Sets the object output's dirty state. */
262 	void setOutputDirty(bool dirty = true);
263 	/** Returns if the object's output must be regenerated. */
264 	bool isOutputDirty() const;
265 
266 	/**
267 	 * Changes the object's symbol, returns if successful.
268 	 *
269 	 * Some conversions are impossible, for example point to line. Normally,
270 	 * this method checks if the types of the old and the new symbol are
271 	 * compatible. If the old symbol pointer is no longer valid, you can
272 	 * use no_checks to disable this.
273 	 */
274 	bool setSymbol(const Symbol* new_symbol, bool no_checks);
275 	/** Returns the object's symbol. */
276 	const Symbol* getSymbol() const;
277 
278 	/** NOTE: The extent is only valid after update() has been called! */
279 	const QRectF& getExtent() const;
280 
281 	/**
282 	 * Sets the object's map pointer.
283 	 *
284 	 * May be nullptr if the object is not in a map.
285 	 */
286 	void setMap(Map* map);
287 	/** Returns the object's map pointer. */
288 	Map* getMap() const;
289 
290 	/** Constructs an object of the given type with the given symbol. */
291 	static Object* getObjectForType(Type type, const Symbol* symbol = nullptr);
292 
293 
294 	/** Defines a type which maps keys to values, to be used for tagging objects. */
295 	typedef QHash<QString, QString> Tags;
296 
297 	/** Returns a const reference to the object's tags. */
298 	const Tags& tags() const;
299 
300 	/** Replaces the object's tags. */
301 	void setTags(const Tags& tags);
302 
303 	/** Returns the value of the given tag key. */
304 	QString getTag(const QString& key) const;
305 
306 	/** Sets the given tag key to the value. */
307 	void setTag(const QString& key, const QString& value);
308 
309 	/** Removes the given tag key and its value. */
310 	void removeTag(const QString& key);
311 
312 
313 	/**
314 	 * @brief Extends a rectangle to enclose all of the object's control points.
315 	 */
316 	void includeControlPointsRect(QRectF& rect) const;
317 
318 protected:
319 	virtual void updateEvent() const;
320 
321 	virtual void createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const;
322 
323 	Type type;
324 	const Symbol* symbol = nullptr;
325 	MapCoordVector coords;
326 	Map* map = nullptr;
327 	Tags object_tags;
328 
329 private:
330 	qreal rotation = 0;               ///< The object's rotation (in radians).
331 	mutable bool output_dirty = true; // does the output have to be re-generated because of changes?
332 	mutable QRectF extent;            // only valid after calling update()
333 	mutable ObjectRenderables output; // only valid after calling update()
334 };
335 
336 
337 
338 class PathPartVector;
339 
340 
341 /**
342  * Helper class with information about parts of paths.
343  *
344  * A part is a path segment which is separated from other parts by
345  * a hole point at its end.
346  */
347 class PathPart : public VirtualPath
348 {
349 public:
350 	/** Pointer to path object containing this part */
351 	PathObject* path = nullptr;
352 
353 	PathPart(
354 	        PathObject& path,
355 	        MapCoordVector::size_type start_index,
356 	        MapCoordVector::size_type end_index
357 	);
358 
359 	PathPart(
360 	        const VirtualCoordVector& coords,
361 	        MapCoordVector::size_type start_index,
362 	        MapCoordVector::size_type end_index
363 	);
364 
365 	PathPart(
366 	        PathObject& path,
367 	        const VirtualPath& virtual_path
368 	);
369 
370 	~PathPart() = default;
371 
372 	PathPart& operator=(const PathPart& rhs);
373 
374 	/**
375 	 * Closes or opens the sub-path.
376 	 *
377 	 * If closed == true and may_use_existing_close_point == false,
378 	 * a new point is added as closing point even if its coordinates
379 	 * are identical to the existing last point. Else, the last point
380 	 * may be reused.
381 	 */
382 	void setClosed(bool closed, bool may_use_existing_close_point = false);
383 
384 	/**
385 	 * Closes the subpath, merging the start and end point at their center.
386 	 *
387 	 * \see PathPart::setClosed()
388 	 */
389 	void connectEnds();
390 
391 	/**
392 	 * Reverses the part's coordinates.
393 	 *
394 	 * Reversing the coordinates results in switching the start/end/mid/dash
395 	 * symbol direction for line symbols.
396 	 *
397 	 * \see PathObject::reverse()
398 	 */
399 	void reverse();
400 
401 	static PathPartVector calculatePathParts(const VirtualCoordVector& coords);
402 };
403 
404 
405 
406 class PathPartVector : public std::vector<PathPart>
407 {
408 public:
409 	/**
410 	 * This is dangerous when copying objects (which own a PathPartVector).
411 	 *
412 	 * Objects need to deal with the PathParts explicitly, at least as long as
413 	 * the PathPart contains distinct references to the object and to the
414 	 * coordinates.
415 	 *
416 	 * Other use cases may consider using std::vector<PathPart>.
417 	 */
418 	PathPartVector& operator=(const PathPartVector&) = delete;
419 
420 	/**
421 	 * Returns true if the part's end_index is lower than index.
422 	 *
423 	 * This function can be used for doing a binary search on a sorted PathPartVector.
424 	 *
425 	 * @see std::lower_bound()
426 	 */
427 	static bool compareEndIndex(const PathPart& part, VirtualPath::size_type index);
428 };
429 
430 
431 
432 /**
433  * Object type which can be used for line, area and combined symbols.
434  * Has a dynamic number of coordinates.
435  *
436  * The coordinates are divided into one or multiple PathParts. A PathPart
437  * is ended by a coordinate with the "hole point" flag. For all types of
438  * flags which can be set, see the MapCoord documentation.
439  */
440 class PathObject : public Object  // clazy:exclude=copyable-polymorphic
441 {
442 	friend class PathPart;
443 
444 public:
445 
446 	/** Returned by calcAllIntersectionsWith(). */
447 	struct Intersection
448 	{
449 		/** Coordinate of the intersection */
450 		MapCoordF coord;
451 		/** Part index of intersection */
452 		PathPartVector::size_type part_index;
453 		/** Length of path until this intersection point */
454 		PathCoord::length_type length;
455 		/** Part index of intersection in other path */
456 		PathPartVector::size_type other_part_index;
457 		/** Length of other path until this intersection point */
458 		PathCoord::length_type other_length;
459 
460 		/**
461 		 * Creates an Intersection at the position specified by factors a and b
462 		 * between the a0/a1 and b0/b1 PathCoords in the given parts.
463 		 */
464 		static Intersection makeIntersectionAt(
465 		        double a,
466 		        double b,
467 		        const PathCoord& a0,
468 		        const PathCoord& a1,
469 		        const PathCoord& b0,
470 		        const PathCoord& b1,
471 		        PathPartVector::size_type part_index,
472 		        PathPartVector::size_type other_part_index );
473 	};
474 
475 	/** std::vector of Intersection with the ability to sort them and remove duplicates. */
476 	class Intersections : public std::vector<Intersection>
477 	{
478 	public:
479 		/** Sorts the intersections and removes duplicates. */
480 		void normalize();
481 	};
482 
483 
484 	/** Constructs a PathObject, optionally assigning a symbol. */
485 	explicit PathObject(const Symbol* symbol = nullptr);
486 
487 	/** Constructs a PathObject, assigning initial coords and optionally the map pointer. */
488 	explicit PathObject(const Symbol* symbol, MapCoordVector coords, Map* map = nullptr);
489 
490 	/**
491 	 * Constructs a PathObject, assigning initial coords from a single piece of a line.
492 	 *
493 	 * "Piece" refers to a single straight or curved arc from the point
494 	 * identified by parameter piece to the immediate next point on the path.
495 	 *
496 	 * If the path is not closed, and piece refers to the last element in the
497 	 * path (part), then the arc ending in the point referred to by piece is
498 	 * returned instead.
499 	 */
500 	explicit PathObject(const Symbol* symbol, const PathObject& proto, MapCoordVector::size_type piece);
501 
502 protected:
503 	/** Constructs a PathObject, initialized from the given prototype. */
504 	explicit PathObject(const PathObject& proto);
505 
506 public:
507 	/**
508 	 * Constructs a PathObject, initialized from the given part of another object.
509 	 */
510 	explicit PathObject(const PathPart& proto_part);
511 
512 	/**
513 	 * Creates a duplicate of the path object.
514 	 *
515 	 * Use asPath() on the result to obtain an object of type PathObject.
516 	 */
517 	PathObject* duplicate() const override;
518 
519 	PathObject& operator=(const PathObject& other) = delete;
520 
521 	/** Replaces this object's contents by those of the other. */
522 	void copyFrom(const Object& other) override;
523 
524 
525 	bool validate() const override;
526 
527 
528 	/** Checks the path for valid flags, and makes corrections as necessary. */
529 	void normalize();
530 
531 
532 	bool intersectsBox(const QRectF& box) const override;
533 
534 
535 	// Coordinate access methods
536 
537 	/** Returns the number of coordinates, including curve handles and close points. */
getCoordinateCount()538 	MapCoordVector::size_type getCoordinateCount() const { return coords.size(); }
539 
540 	/** Returns the i-th coordinate. */
getCoordinate(MapCoordVector::size_type pos)541 	MapCoord getCoordinate(MapCoordVector::size_type pos) const
542 	{
543 		Q_ASSERT(pos < coords.size());
544 		return coords[pos];
545 	}
546 
547 	/** Returns a non-const reference to the i-th coordinate.
548 	 *
549 	 * Normally you should modify coordinates via PathObject::setCoordinate.
550 	 * Unlike that function, modifying a coordinate directly via the reference
551 	 * will not keep the first and last point of a closed path in sync.
552 	 */
getCoordinateRef(MapCoordVector::size_type pos)553 	MapCoord& getCoordinateRef(MapCoordVector::size_type pos)
554 	{
555 		Q_ASSERT(pos < coords.size());
556 		setOutputDirty();
557 		return coords[pos];
558 	}
559 
560 	/** Replaces the i-th coordinate with c. */
561 	void setCoordinate(MapCoordVector::size_type pos, const MapCoord& c);
562 
563 	/** Adds the coordinate at the given index. */
564 	void addCoordinate(MapCoordVector::size_type pos, const MapCoord& c);
565 
566 	/** Adds the coordinate at the end, optionally starting a new part.
567 	 *  If starting a new part, make sure that the last coord of the old part
568 	 *  has the hole point flag! */
569 	void addCoordinate(const MapCoord& c, bool start_new_part = false);
570 
571 	/**
572 	 * Deletes a coordinate from the path.
573 	 *
574 	 * When requesting a control point of a bezier arc to be deleted, the other
575 	 * control point is deleted, too.
576 	 *
577 	 * If the number of regular points in the coordinate's part is not more
578 	 * than two, the whole part is delete from the object.
579 	 *
580 	 * @param pos Index of the coordinate to delete.
581 	 * @param adjust_other_coords If set and the deleted coordinate was joining
582 	 *     two bezier curves, adapts the adjacent curves with a strategy defined
583 	 *     by delete_bezier_point_action. adjust_other_coords does not work
584 	 *     when deleting bezier curve handles!
585 	 * @param delete_bezier_point_action Must be an enum value from
586 	 *     Settings::DeleteBezierPointAction if adjust_other_coords is set.
587 	 */
588 	void deleteCoordinate(MapCoordVector::size_type pos, bool adjust_other_coords, int delete_bezier_point_action = -1);
589 
590 	/** Deletes all coordinates of the object. */
591 	void clearCoordinates();
592 
593 	/**
594 	 * Assigns the given prototype's coordinates subset to this object's coordinates.
595 	 *
596 	 * The range must be within one part. Last may be smaller than first iff
597 	 * the path is closed.
598 	 */
599 	void assignCoordinates(const PathObject& proto, MapCoordVector::size_type first, MapCoordVector::size_type last);
600 
601 
602 	/** Finds the path part containing the given coord index. */
603 	PathPartVector::const_iterator findPartForIndex(MapCoordVector::size_type coords_index) const;
604 
605 	/** Finds the path part containing the given coord index. */
606 	PathPartVector::iterator findPartForIndex(MapCoordVector::size_type coords_index);
607 
608 	/**
609 	 * Finds the path part containing the given coord index.
610 	 *
611 	 * \todo Review where this signature can be replace by the one returning an iterator.
612 	 */
613 	PathPartVector::size_type findPartIndexForIndex(MapCoordVector::size_type coords_index) const;
614 
615 
616 	/**
617 	 * Returns the path coordinate for the map coordinate with given index.
618 	 *
619 	 * @param index Index of normal MapCoord for which to create the PathCoord.
620 	 */
621 	PathCoord findPathCoordForIndex(MapCoordVector::size_type index) const;
622 
623 
624 	/**
625 	 * Returns true if the given index is a curve handle.
626 	 */
627 	bool isCurveHandle(MapCoordVector::size_type index) const;
628 
629 
630 	/**
631 	 * Returns the vector of path parts.
632 	 */
633 	const PathPartVector& parts() const;
634 
635 	/**
636 	 * Returns the vector of path parts.
637 	 *
638 	 * Marks the output as dirty.
639 	 */
640 	PathPartVector& parts();
641 
642 	/**
643 	 * Deletes the i-th path part.
644 	 */
645 	void deletePart(PathPartVector::size_type part_index);
646 
647 	/**
648 	 * Transforms the coordinates and the pattern origin.
649 	 */
650 	void transform(const QTransform& t) override;
651 
652 	// Pattern methods
653 
654 	/**
655 	 * Returns the rotation of the object pattern. Only has an effect in
656 	 * combination with a symbol interpreting this value.
657 	 */
658 	qreal getPatternRotation() const;
659 
660 	/**
661 	 * Sets the rotation of the object pattern. Only has an effect in
662 	 * combination with a symbol interpreting this value.
663 	 */
664 	void setPatternRotation(qreal rotation);
665 
666 	/**
667 	 * Returns the origin of the object pattern. Only has an effect in
668 	 * combination with a symbol interpreting this value.
669 	 */
670 	MapCoord getPatternOrigin() const;
671 
672 	/**
673 	 * Sets the origin of the object pattern. Only has an effect in
674 	 * combination with a symbol interpreting this value.
675 	 */
676 	void setPatternOrigin(const MapCoord& origin);
677 
678 
679 	// Operations
680 
681 	/**
682 	 * Calculates the closest point on the path to the given coordinate,
683 	 * returns the squared distance of these points and PathCoord information
684 	 * for the point on the path.
685 	 *
686 	 * This does not need to be an existing path coordinate. This method is
687 	 * usually called to find the position on the path the user clicked on.
688 	 * part_index can be set to a valid part index to constrain searching
689 	 * to this specific path part.
690 	 *
691 	 * \todo Convert out_distance_sq to double (so avoiding conversions).
692 	 * \todo Return PathCoord rather than writing to the provided reference.
693 	 */
694 	void calcClosestPointOnPath(
695 	        MapCoordF coord,
696 	        float& out_distance_sq,
697 	        PathCoord& out_path_coord,
698 	        MapCoordVector::size_type start_index = 0,
699 	        MapCoordVector::size_type end_index = std::numeric_limits<PathPartVector::size_type>::max()
700 	) const;
701 
702 	/**
703 	 * Calculates the closest control point coordinate to the given coordinate,
704 	 * returns the squared distance of these points and the index of the control point.
705 	 *
706 	 * \todo Convert out_distance_sq to double (so avoiding conversions).
707 	 * \todo Return index rather than writing to the provided reference.
708 	 */
709 	void calcClosestCoordinate(
710 	        MapCoordF coord,
711 	        float& out_distance_sq,
712 	        MapCoordVector::size_type& out_index) const;
713 
714 	/**
715 	 * Splits the path at the position given by path_coord.
716 	 *
717 	 * Must not be called while isOutputDirty() returns true.
718 	 *
719 	 * Returns the index of the added point.
720 	 */
721 	MapCoordVector::size_type subdivide(const PathCoord& path_coord);
722 
723 	/**
724 	 * Splits the path in the curve which starts at the given index.
725 	 *
726 	 * The second parameter determines the split position between begin and end
727 	 * of the curve (0.0 ... 1.0).
728 	 *
729 	 * Must not be called while isOutputDirty() returns true.
730 	 *
731 	 * @return The index of the added point.
732 	 */
733 	MapCoordVector::size_type subdivide(MapCoordVector::size_type index, float param);
734 
735 	/**
736 	 * Returns if connectIfClose() would change something with the given parameters
737 	 */
738 	bool canBeConnected(const PathObject* other, double connect_threshold_sq) const;
739 
740 	/**
741 	 * Returns if the objects were connected (if so, you can delete the other object).
742 	 * If one of the paths has to be reversed, it is done for the "other" path.
743 	 * Otherwise, the "other" path is not changed.
744 	 *
745 	 * \todo Review documentation, container usage,
746 	 */
747 	bool connectIfClose(PathObject* other, double connect_threshold_sq);
748 
749 	/**
750 	 * Connects the given parts, optionally merging the end coordinates at the
751 	 * center position, and copying over the coordinates from other.
752 	 */
753 	void connectPathParts(
754 	        PathPartVector::size_type part_index,
755 	        const PathObject* other,
756 	        PathPartVector::size_type other_part_index,
757 	        bool prepend,
758 	        bool merge_ends = true
759 	);
760 
761 	/**
762 	 * Returns the result of removing the section between begin and end from the path.
763 	 *
764 	 * begin and end must belong to the path part with the given part_index.
765 	 * However, objects with holes, and part_index values greater than 0, are
766 	 * not supported at the moment.
767 	 *
768 	 * Returns an empty vector when nothing remains after removal.
769 	 */
770 	std::vector<PathObject*> removeFromLine(
771 	        PathPartVector::size_type part_index,
772 	        PathCoord::length_type clen_begin,
773 	        PathCoord::length_type clen_end
774 	) const;
775 
776 	/**
777 	 * Returns the result of splitting the path at the given inner position.
778 	 *
779 	 * Returns an empty vector when the object is not changed by the split.
780 	 * This happens when the path is not closed and split_pos is the begin or
781 	 * end of the path, or when the object has got more than a single PathPart.
782 	 */
783 	std::vector<PathObject*> splitLineAt(const PathCoord& split_pos) const;
784 
785 	/**
786 	 * Replaces the path with a non-empty range of it starting and ending at the given lengths.
787 	 *
788 	 * For open paths, the end length must be greater than the start length.
789 	 * For closed paths, an end length smaller than or equal to the start length
790 	 * will cause the resulting path to span the original start/end point.
791 	 */
792 	void changePathBounds(
793 	        PathPartVector::size_type part_index,
794 	        PathCoord::length_type start_len,
795 	        PathCoord::length_type end_len
796 	);
797 
798 	/**
799 	 * Appends (copies) the coordinates of other to this path.
800 	 */
801 	void appendPath(const PathObject* other);
802 
803 	/**
804 	 * Appends (copies) the coordinates of a specific part to this path.
805 	 *
806 	 * The other object is determined from the part's path property.
807 	 */
808 	void appendPathPart(const PathPart& part);
809 
810 	/**
811 	 * Reverses the object's coordinates, resulting in switching
812 	 * the start / end / mid / dash symbol direction for line symbols.
813 	 */
814 	void reverse();
815 
816 	/** Ensures that all parts are closed. Useful for objects with area-only symbols. */
817 	void closeAllParts();
818 
819 	/**
820 	 * Converts all polygonal sections in this path to splines.
821 	 * If at least one section is converted, returns true and
822 	 * returns an undo duplicate if the corresponding pointer is set.
823 	 */
824 	bool convertToCurves(PathObject** undo_duplicate = nullptr);
825 
826 	/**
827 	 * Converts the given range of coordinates to a spline by inserting handles.
828 	 * The range must consist of only polygonal segments before.
829 	 *
830 	 * @return The new index of the end of the range.
831 	 */
832 	PathPart::size_type convertRangeToCurves(const PathPart& part, PathPart::size_type start_index, PathPart::size_type end_index);
833 
834 	/**
835 	 * Tries to remove points while retaining the path shape as much as possible.
836 	 * If at least one point is changed, returns true and
837 	 * returns an undo duplicate if the corresponding pointer is set.
838 	 */
839 	bool simplify(PathObject** undo_duplicate, double threshold);
840 
841 	/** See Object::isPointOnObject() */
842 	int isPointOnPath(
843 	        MapCoordF coord,
844 	        qreal tolerance,
845 	        bool treat_areas_as_paths,
846 	        bool extended_selection
847 	) const;
848 
849 	/**
850 	 * Returns true if the given coordinate is inside the area
851 	 * defined by this object, which must be closed.
852 	 */
853 	bool isPointInsideArea(const MapCoordF& coord) const;
854 
855 	/**
856 	 * Calculates the maximum distance of the given coord ranges of two objects.
857 	 */
858 	double calcMaximumDistanceTo(
859 	        MapCoordVector::size_type start_index,
860 	        MapCoordVector::size_type end_index,
861 	        const PathObject* other,
862 	        MapCoordVector::size_type other_start_index,
863 	        MapCoordVector::size_type other_end_index
864 	) const;
865 
866 	/**
867 	 * Calculates and adds all intersections with the other path to out.
868 	 * Note: intersections are not sorted and may contain duplicates!
869 	 * To clean them up, call clean() on the Intersections object after adding
870 	 * all intersections with objects you are interested in.
871 	 */
872 	void calcAllIntersectionsWith(const PathObject* other, Intersections& out) const;
873 
874 	/** Called by Object::update() */
875 	void updatePathCoords() const;
876 
877 	/** Called by Object::load() */
878 	void recalculateParts();
879 
880 protected:
881 	/**
882 	 * Adjusts the end index of the given part and the start/end indexes of the following parts.
883 	 *
884 	 * output_dirty must be set before calling this function.
885 	 */
886 	void partSizeChanged(PathPartVector::iterator part, MapCoordVector::difference_type change);
887 
888 
889 	void prepareDeleteBezierPoint(MapCoordVector::size_type pos, int delete_bezier_point_action);
890 
891 	/**
892 	 * Calculates the factors which should be applied to the length of the
893 	 * remaining bezier curve handle vectors when deleting a point joining
894 	 * two bezier curves to try to retain the original curves' shape.
895 	 *
896 	 * This is a simple version, the result should be optimized with
897 	 * calcBezierPointDeletionRetainingShapeOptimization().
898 	 *
899 	 * p0, p1, p2, q0 make up the first original curve,
900 	 * q0, q1, q2, q3 make up the second original curve.
901 	 * out_pfactor is set to the factor to apply to the vector (p1 - p0),
902 	 * out_qfactor is set to the factor to apply to the vector (q2 - q3),
903 	 */
904 	static void calcBezierPointDeletionRetainingShapeFactors(
905 		MapCoord p0,
906 		MapCoord p1,
907 		MapCoord p2,
908 		MapCoord q0,
909 		MapCoord q1,
910 		MapCoord q2,
911 		MapCoord q3,
912 		double& out_pfactor,
913 		double& out_qfactor
914 	);
915 
916 	/**
917 	 * Uses nonlinear optimization to improve the first result obtained by
918 	 * calcBezierPointDeletionRetainingShapeFactors().
919 	 */
920 	static void calcBezierPointDeletionRetainingShapeOptimization(
921 		MapCoord p0,
922 		MapCoord p1,
923 		MapCoord p2,
924 		MapCoord q0,
925 		MapCoord q1,
926 		MapCoord q2,
927 		MapCoord q3,
928 		double& out_pfactor,
929 		double& out_qfactor
930 	);
931 
932 	/**
933 	 * Is used internally by calcBezierPointDeletionRetainingShapeOptimization()
934 	 * to calculate the current cost. Evaluates the distance between p0 ... p3
935 	 * and the reference path.
936 	 */
937 	static float calcBezierPointDeletionRetainingShapeCost(
938 		MapCoord p0,
939 		MapCoordF p1,
940 		MapCoordF p2,
941 		MapCoord p3,
942 		PathObject* reference
943 	);
944 
945 	/**
946 	 * Sets coord as the point which closes a part: sets the correct flags
947 	 * on it and replaces the coord at the given index by it.
948 	 * TODO: make separate methods? Setting coords exists already.
949 	 */
950 	void setClosingPoint(MapCoordVector::size_type index, const MapCoord& coord);
951 
952 	void updateEvent() const override;
953 
954 	void createRenderables(ObjectRenderables& output, Symbol::RenderableOptions options) const override;
955 
956 private:
957 	/**
958 	 * Origin shift of the object pattern. Only used if the object
959 	 * has a symbol which interprets this value.
960 	 */
961 	MapCoord pattern_origin = {};
962 
963 	/** Path parts list */
964 	mutable PathPartVector path_parts;
965 };
966 
967 
968 
969 /** Compares the length of the intersections. */
970 inline
971 bool operator< (const PathObject::Intersection& lhs, const PathObject::Intersection& rhs)
972 {
973 	return lhs.length < rhs.length;
974 }
975 
976 /** Fuzzy equality check. */
977 inline
978 bool operator== (const PathObject::Intersection& lhs, const PathObject::Intersection& rhs)
979 {
980 	// NOTE: coord is not compared, as the intersection is defined by the other params already.
981 	const double epsilon = 1e-10;
982 	return lhs.part_index == rhs.part_index &&
983 	       lhs.other_part_index == rhs.other_part_index &&
984 	       qAbs(lhs.length - rhs.length) <= epsilon &&
985 	       qAbs(lhs.other_length - rhs.other_length) <= epsilon;
986 }
987 
988 
989 
990 /**
991  * Object type which can only be used for point symbols,
992  * and is also the only object which can be used with them.
993  *
994  * Has exactly one coordinate, and additionally a rotation parameter.
995  */
996 class PointObject : public Object  // clazy:exclude=copyable-polymorphic
997 {
998 public:
999 	/** Constructs a PointObject, optionally assigning the symbol. */
1000 	explicit PointObject(const Symbol* symbol = nullptr);
1001 
1002 protected:
1003 	/** Constructs a PointObject, initialized from the given prototype. */
1004 	explicit PointObject(const PointObject& proto);
1005 
1006 public:
1007 	/**
1008 	 * Creates a duplicate of the point.
1009 	 *
1010 	 * Use asPoint() on the result to obtain an object of type PointObject.
1011 	 */
1012 	PointObject* duplicate() const override;
1013 
1014 	PointObject& operator=(const PointObject& other) = delete;
1015 
1016 	/** Replaces the content of this object by that of another. */
1017 	void copyFrom(const Object& other) override;
1018 
1019 
1020 	/** Sets the point's position to a new position given in native map coordinates. */
1021 	void setPosition(qint32 x, qint32 y);
1022 
1023 	/** Changes the point's position. */
1024 	void setPosition(const MapCoord& coord);
1025 
1026 	/** Changes the point's position. */
1027 	void setPosition(const MapCoordF& coord);
1028 
1029 	/** Returns the point's position as MapCoordF. */
1030 	MapCoordF getCoordF() const;
1031 
1032 	/** Returns the point's coordinate. */
1033 	MapCoord getCoord() const;
1034 
1035 
1036 	/**
1037 	 * Transforms the position.
1038 	 */
1039 	void transform(const QTransform& t) override;
1040 
1041 
1042 	bool intersectsBox(const QRectF& box) const override;
1043 };
1044 
1045 
1046 
1047 /**
1048  * A single PathCoord together with the object it belongs to.
1049  *
1050  * This is a convenient structure for passing around as parameter and return value.
1051  */
1052 struct ObjectPathCoord : public PathCoord
1053 {
1054 	PathObject* object;
1055 
1056 	constexpr ObjectPathCoord() noexcept;
1057 	constexpr ObjectPathCoord(PathObject* object) noexcept;
1058 	constexpr ObjectPathCoord(PathObject* object, const PathCoord& coord) noexcept;
1059 	constexpr ObjectPathCoord(PathObject* object, const PathCoord&& coord) noexcept;
1060 	constexpr ObjectPathCoord(const ObjectPathCoord&) noexcept = default;
1061 	constexpr ObjectPathCoord(ObjectPathCoord&&) noexcept = default;
1062 	ObjectPathCoord& operator=(const ObjectPathCoord&) noexcept = default;
1063 	ObjectPathCoord& operator=(ObjectPathCoord&&) noexcept = default;
1064 
1065 	/**
1066 	 * This constructor sets the PathCoord members according to the given coordinate index.
1067 	 */
1068 	ObjectPathCoord(PathObject* object, MapCoordVector::size_type index);
1069 
1070 	/**
1071 	 * Returns true iff the object is not null.
1072 	 */
1073 	constexpr operator bool() const;
1074 
1075 	/**
1076 	 * Sets this PathCoord to the point on this path which is the closest to the
1077 	 * given coordinate.
1078 	 *
1079 	 * \return The squared distance of these points.
1080 	 *
1081 	 * \see PathObject::calcClosestPointOnPath
1082 	 */
1083 	float findClosestPointTo(const MapCoordF& map_coord);
1084 };
1085 
1086 
1087 
1088 //### Object inline code ###
1089 
1090 inline
getType()1091 Object::Type Object::getType() const
1092 {
1093 	return type;
1094 }
1095 
1096 inline
renderables()1097 const ObjectRenderables& Object::renderables() const
1098 {
1099 	return output;
1100 }
1101 
1102 inline
getRawCoordinateVector()1103 const MapCoordVector& Object::getRawCoordinateVector() const
1104 {
1105 	return coords;
1106 }
1107 
1108 inline
setOutputDirty(bool dirty)1109 void Object::setOutputDirty(bool dirty)
1110 {
1111 	output_dirty = dirty;
1112 }
1113 
1114 inline
isOutputDirty()1115 bool Object::isOutputDirty() const
1116 {
1117 	return output_dirty;
1118 }
1119 
1120 inline
getSymbol()1121 const Symbol* Object::getSymbol() const
1122 {
1123 	return symbol;
1124 }
1125 
1126 inline
getExtent()1127 const QRectF& Object::getExtent() const
1128 {
1129 	return extent;
1130 }
1131 
1132 inline
setMap(Map * map)1133 void Object::setMap(Map* map)
1134 {
1135 	this->map = map;
1136 	setOutputDirty();
1137 }
1138 
1139 inline
getMap()1140 Map* Object::getMap() const
1141 {
1142 	return map;
1143 }
1144 
1145 inline
tags()1146 const Object::Tags& Object::tags() const
1147 {
1148 	return object_tags;
1149 }
1150 
1151 inline
getTag(const QString & key)1152 QString Object::getTag(const QString& key) const
1153 {
1154 	return object_tags.value(key);
1155 }
1156 
1157 
1158 
1159 //### PathPart inline code ###
1160 
1161 inline
PathPart(const VirtualCoordVector & coords,MapCoordVector::size_type start_index,MapCoordVector::size_type end_index)1162 PathPart::PathPart(
1163         const VirtualCoordVector& coords,
1164         MapCoordVector::size_type start_index,
1165         MapCoordVector::size_type end_index)
1166  : VirtualPath(coords, start_index, end_index)
1167 {
1168 	// nothing else
1169 }
1170 
1171 inline
PathPart(PathObject & path,MapCoordVector::size_type start_index,MapCoordVector::size_type end_index)1172 PathPart::PathPart(
1173         PathObject& path,
1174         MapCoordVector::size_type start_index,
1175         MapCoordVector::size_type end_index )
1176  : VirtualPath(path.getRawCoordinateVector(), start_index, end_index)
1177  , path(&path)
1178 {
1179 	// nothing else
1180 }
1181 
1182 inline
1183 PathPart& PathPart::operator=(const PathPart& rhs)
1184 {
1185 	Q_ASSERT(path == rhs.path);
1186 	VirtualPath::operator=(rhs);
1187 	return *this;
1188 }
1189 
1190 
1191 
1192 //### PathObject inline code ###
1193 
1194 inline
parts()1195 const PathPartVector& PathObject::parts() const
1196 {
1197 	return path_parts;
1198 }
1199 
1200 inline
parts()1201 PathPartVector& PathObject::parts()
1202 {
1203 	setOutputDirty();
1204 	return path_parts;
1205 }
1206 
1207 inline
getPatternRotation()1208 qreal PathObject::getPatternRotation() const
1209 {
1210 	return getRotation();
1211 }
1212 
1213 inline
getPatternOrigin()1214 MapCoord PathObject::getPatternOrigin() const
1215 {
1216 	return pattern_origin;
1217 }
1218 
1219 
1220 
1221 //### ObjectPathCoord inline code ###
1222 
1223 inline
ObjectPathCoord()1224 constexpr ObjectPathCoord::ObjectPathCoord() noexcept
1225 : ObjectPathCoord { nullptr }
1226 {
1227 	// nothing else
1228 }
1229 
1230 
1231 inline
ObjectPathCoord(PathObject * object)1232 constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object) noexcept
1233 : object { object }
1234 {
1235 	// nothing else
1236 }
1237 
1238 
1239 inline
ObjectPathCoord(PathObject * object,const PathCoord & coord)1240 constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object, const PathCoord& coord) noexcept
1241 : PathCoord { coord }
1242 , object    { object }
1243 {
1244 	// nothing else
1245 }
1246 
1247 
1248 inline
ObjectPathCoord(PathObject * object,const PathCoord && coord)1249 constexpr ObjectPathCoord::ObjectPathCoord(PathObject* object, const PathCoord&& coord) noexcept
1250 : PathCoord { std::move(coord) }
1251 , object    { object }
1252 {
1253 	// nothing else
1254 }
1255 
1256 
1257 inline
ObjectPathCoord(PathObject * object,MapCoordVector::size_type index)1258 ObjectPathCoord::ObjectPathCoord(PathObject* object, MapCoordVector::size_type index)
1259 : PathCoord { object->findPathCoordForIndex(index) }
1260 , object    { object }
1261 {
1262 	// nothing else
1263 }
1264 
1265 
1266 inline
findClosestPointTo(const MapCoordF & map_coord)1267 float ObjectPathCoord::findClosestPointTo(const MapCoordF& map_coord)
1268 {
1269 	float distance_sq;
1270 	object->calcClosestPointOnPath(map_coord, distance_sq, *this);
1271 	return distance_sq;
1272 
1273 }
1274 
1275 
1276 inline
1277 constexpr ObjectPathCoord::operator bool() const
1278 {
1279 	return static_cast<bool>(object);
1280 }
1281 
1282 }  // namespace OpenOrienteering
1283 
1284 
1285 #endif
1286