1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2012-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_SYMBOL_H
23 #define OPENORIENTEERING_SYMBOL_H
24 
25 #include <array>
26 #include <cstddef>
27 #include <memory>
28 #include <type_traits>
29 #include <vector>
30 
31 #include <Qt>
32 #include <QtGlobal>
33 #include <QFlags>
34 #include <QHash>
35 #include <QImage>
36 #include <QMetaType>
37 #include <QRgb>
38 #include <QString>
39 
40 class QXmlStreamReader;
41 class QXmlStreamWriter;
42 
43 namespace OpenOrienteering {
44 
45 class AreaSymbol;
46 class CombinedSymbol;
47 class LineSymbol;
48 class Map;
49 class MapColor;
50 class MapColorMap;
51 class Object;
52 class ObjectRenderables;
53 class PathObject;
54 class PathPartVector;
55 class PointSymbol;
56 class Symbol;
57 class SymbolPropertiesWidget;
58 class SymbolSettingDialog;
59 class TextSymbol;
60 class VirtualCoordVector;
61 
62 typedef QHash<qint32, Symbol*> SymbolDictionary;
63 
64 
65 /**
66  * Abstract base class for map symbols.
67  *
68  * Provides among other things a symbol number consisting of multiple parts,
69  * e.g. "2.4.12". Parts which are not set are assigned the value -1.
70  */
71 class Symbol
72 {
73 public:
74 	/**
75 	 * Enumeration of all possible symbol types.
76 	 *
77 	 * Values are chosen to be able to be used as bits in a bitmask.
78 	 */
79 	enum Type
80 	{
81 		Point    = 1,
82 		Line     = 2,
83 		Area     = 4,
84 		Text     = 8,
85 		Combined = 16,
86 
87 		NoSymbol = 0,
88 		AllSymbols = Point | Line | Area | Text | Combined
89 	};
90 	Q_DECLARE_FLAGS(TypeCombination, Type)
91 
92 	/**
93 	 * RenderableOptions denominate variations in painting symbols.
94 	 */
95 	enum RenderableOption
96 	{
97 		RenderBaselines    = 1 << 0,   ///< Paint cosmetique contours and baselines
98 		RenderAreasHatched = 1 << 1,   ///< Paint hatching instead of opaque fill
99 		RenderNormal       = 0         ///< Paint normally
100 	};
101 	Q_DECLARE_FLAGS(RenderableOptions, RenderableOption)
102 
103 
104 	explicit Symbol(Type type) noexcept;
105 	virtual ~Symbol();
106 
107 protected:
108 	explicit Symbol(const Symbol& proto);
109 	virtual Symbol* duplicate() const = 0;
110 
111 public:
112 	/**
113 	 * Duplicates a symbol.
114 	 *
115 	 * This template function mitigates the incompatibility of std::unique_ptr
116 	 * with covariant return types when duplicating instances of the
117 	 * polymorphic type Symbol.
118 	 *
119 	 * For convenient use outside of (child class) method implementatations,
120 	 * there is a free template function duplicate(const Derived& d).
121 	 */
122 	template<class S>
duplicate(const S & s)123 	static std::unique_ptr<S> duplicate(const S& s)
124 	{
125 		return std::unique_ptr<S>(static_cast<S*>(static_cast<const Symbol&>(s).duplicate()));
126 	}
127 
128 	Symbol& operator=(const Symbol&) = delete;
129 	Symbol& operator=(Symbol&&) = delete;
130 
131 
132 	virtual bool validate() const;
133 
134 
135 	/**
136 	 * Checks for equality to the other symbol.
137 	 *
138 	 * This function does not check the equality of the state
139 	 * (protected/hidden). Use stateEquals(Symbol*) for this comparison.
140 	 *
141 	 * It also does not compare color priorities.
142 	 *
143 	 * @param other The symbol to compare with.
144 	 * @param case_sensitivity Comparison mode for strings, e.g. symbol names.
145 	 */
146 	bool equals(const Symbol* other, Qt::CaseSensitivity case_sensitivity = Qt::CaseSensitive) const;
147 
148 	/**
149 	 * Checks protected/hidden state for equality to the other symbol.
150 	 */
151 	bool stateEquals(const Symbol* other) const;
152 
153 
154 	/**
155 	 * Returns the type of the symbol.
156 	 */
getType()157 	Type getType() const { return type; }
158 
159 	// Convenience casts with type checking
160 	/** Case to PointSymbol with type checking */
161 	const PointSymbol* asPoint() const;
162 	/** Case to PointSymbol with type checking */
163 	PointSymbol* asPoint();
164 	/** Case to LineSymbol with type checking */
165 	const LineSymbol* asLine() const;
166 	/** Case to LineSymbol with type checking */
167 	LineSymbol* asLine();
168 	/** Case to AreaSymbol with type checking */
169 	const AreaSymbol* asArea() const;
170 	/** Case to AreaSymbol with type checking */
171 	AreaSymbol* asArea();
172 	/** Case to TextSymbol with type checking */
173 	const TextSymbol* asText() const;
174 	/** Case to TextSymbol with type checking */
175 	TextSymbol* asText();
176 	/** Case to CombinedSymbol with type checking */
177 	const CombinedSymbol* asCombined() const;
178 	/** Case to CombinedSymbol with type checking */
179 	CombinedSymbol* asCombined();
180 
181 	/**
182 	 * Returns the combined bitmask of all symbol types this symbol contains.
183 	 */
184 	virtual TypeCombination getContainedTypes() const;
185 
186 	/**
187 	 * Checks if the symbol can be applied to the given object.
188 	 */
189 	bool isTypeCompatibleTo(const Object* object) const;
190 
191 
192 	/**
193 	 * Returns if the symbol numbers are exactly equal.
194 	 */
195 	bool numberEquals(const Symbol* other) const;
196 
197 	/**
198 	 * Returns if the symbol numbers are equal, ignoring trailing zeros.
199 	 */
200 	bool numberEqualsRelaxed(const Symbol* other) const;
201 
202 
203 	/**
204 	 * Saves the symbol in xml format.
205 	 *
206 	 * This function invokes saveImpl(...) which may be overridden by child classes.
207 	 *
208 	 * @param xml Stream to save to.
209 	 * @param map Reference to the map containing the symbol.
210 	 */
211 	void save(QXmlStreamWriter& xml, const Map& map) const;
212 
213 	/**
214 	 * Load the symbol in xml format.
215 	 *
216 	 * This function invokes loadImpl(...) which may be overridden by child classes.
217 	 * It does not add the symbol to the map.
218 	 *
219 	 * @param xml Stream to load from.
220 	 * @param map Reference to the map which will eventually contain the symbol.
221 	 * @param symbol_dict Dictionary mapping symbol IDs to symbols.
222 	 */
223 	static std::unique_ptr<Symbol> load(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict, int version);
224 
225 	/**
226 	 * Called when loading the map is finished.
227 	 *
228 	 * This event handler can be overridden in order to do tasks that need to
229 	 * access other symbols or map objects.
230 	 */
231 	virtual bool loadingFinishedEvent(Map* map);
232 
233 
234 	/**
235 	 * Creates renderables for a generic object.
236 	 *
237 	 * This will create the renderables according to the object's properties
238 	 * and the given coordinates.
239 	 *
240 	 * Implementations must use the coordinates (coords) instead of the object's
241 	 * coordinates.
242 	 */
243 	virtual void createRenderables(
244 	        const Object *object,
245 	        const VirtualCoordVector &coords,
246 	        ObjectRenderables &output,
247 	        Symbol::RenderableOptions options) const = 0;
248 
249 	/**
250 	 * Creates renderables for a path object.
251 	 *
252 	 * This will create the renderables according to the object's properties
253 	 * and the coordinates given by the path_parts. This allows the immediate
254 	 * use of precalculated meta-information on paths.
255 	 *
256 	 * \see createRenderables()
257 	 */
258 	virtual void createRenderables(
259 	        const PathObject* object,
260 	        const PathPartVector& path_parts,
261 	        ObjectRenderables &output,
262 	        Symbol::RenderableOptions options) const;
263 
264 	/**
265 	 * Creates baseline renderables for a path object.
266 	 *
267 	 * Baseline renderables show the coordinate paths with minimum line width.
268 	 *
269 	 * \see createRenderables()
270 	 */
271 	virtual void createBaselineRenderables(
272 	        const PathObject* object,
273 	        const PathPartVector& path_parts,
274 	        ObjectRenderables &output,
275 	        const MapColor* color) const;
276 
277 
278 	/**
279 	 * Called when a color is removed from the map.
280 	 *
281 	 * Symbols need to remove all references to the given color when this event
282 	 * occurs.
283 	 */
284 	virtual void colorDeletedEvent(const MapColor* color) = 0;
285 
286 	/**
287 	 * Returns if the given color is used by this symbol.
288 	 */
289 	virtual bool containsColor(const MapColor* color) const = 0;
290 
291 	/**
292 	 * Returns the dominant color of this symbol.
293 	 *
294 	 * If it is not possible to efficiently determine this color exactly,
295 	 * an appropriate heuristic should be used.
296 	 */
297 	virtual const MapColor* guessDominantColor() const = 0;
298 
299 	/**
300 	 * Replaces colors used by this symbol.
301 	 */
302 	virtual void replaceColors(const MapColorMap& color_map) = 0;
303 
304 
305 	/**
306 	 * Called when a symbol was changed, replaced, or removed.
307 	 *
308 	 * Symbol need top update or remove references to the given old_symbol.
309 	 * If new_symbol is nullptr, the symbol is about to be deleted.
310 	 *
311 	 * Returns true if this symbol contained the deleted symbol.
312 	 */
313 	virtual bool symbolChangedEvent(const Symbol* old_symbol, const Symbol* new_symbol);
314 
315 	/**
316 	 * Returns true if the given symbol is referenced by this symbol.
317 	 *
318 	 * A symbol does not contain itself, so it must return true when the given
319 	 * symbol is identical to the symbol this function is being called for.
320 	 */
321 	virtual bool containsSymbol(const Symbol* symbol) const;
322 
323 
324 	/**
325 	 * Scales the symbol.
326 	 */
327 	virtual void scale(double factor) = 0;
328 
329 
330 	/**
331 	 * Returns the custom symbol icon.
332 	 */
getCustomIcon()333 	QImage getCustomIcon() const { return custom_icon; }
334 
335 	/**
336 	 * Sets a custom symbol icon.
337 	 *
338 	 * The custom icon takes precedence over the generated one when custom icon
339 	 * display is enabled.
340 	 * Like the generated icon, it is not part of the symbol state which is
341 	 * compared by the `equals` functions.
342 	 * However, it is copied when duplicating an icon.
343 	 *
344 	 * To clear the custom icon, this function can be called with a null QImage.
345 	 */
346 	void setCustomIcon(const QImage& image);
347 
348 	/**
349 	 * Returns the symbol's icon.
350 	 *
351 	 * This function returns (a scaled version of) the custom symbol icon if
352 	 * it is set and custom icon display is enabled, or a generated one.
353 	 * The icon is cached, making repeated calls cheap.
354 	 */
355 	QImage getIcon(const Map* map) const;
356 
357 	/**
358 	 * Creates a symbol icon with the given side length (pixels).
359 	 *
360 	 * If the zoom parameter is zero, the map's symbolIconZoom() is used.
361 	 * At a zoom of 1.0 (100%), a square symbol of 1 mm side length would fill
362 	 * 50% of the icon width and height. Larger symbols may be scaled down.
363 	 */
364 	QImage createIcon(const Map& map, int side_length, bool antialiasing = true, qreal zoom = 0) const;
365 
366 	/**
367 	 * Clear the symbol's cached icon.
368 	 *
369 	 * Call this function after changes to the symbol definition, custom icon,
370 	 * or general size/zoom/visibility settings.
371 	 * The cached icon will be recreated the next time getIcon() gets called.
372 	 */
373 	void resetIcon();
374 
375 	/**
376 	 * Returns the dimension which shall considered when scaling the icon.
377 	 */
378 	virtual qreal dimensionForIcon() const;
379 
380 	/**
381 	 * Returns the largest extent of all primitive lines which are part of the symbol.
382 	 *
383 	 * Effectively, this is the half line width.
384 	 */
385 	virtual qreal calculateLargestLineExtent() const;
386 
387 
388 	// Getters / Setters
389 
390 	/**
391 	 * Returns the symbol name.
392 	 */
getName()393 	const QString& getName() const { return name; }
394 
395 	/**
396 	 * Returns the symbol name with all HTML markup stripped.
397 	 */
398 	QString getPlainTextName() const;
399 
400 	/**
401 	 * Sets the symbol name.
402 	 */
setName(const QString & new_name)403 	void setName(const QString& new_name) { name = new_name; }
404 
405 
406 	/**
407 	 * Returns the symbol number as string
408 	 */
409 	QString getNumberAsString() const;
410 
411 	/**
412 	 * Returns the i-th component of the symbol number as int.
413 	 */
getNumberComponent(int i)414 	int getNumberComponent(int i) const { Q_ASSERT(i >= 0 && i < int(number_components)); return number[std::size_t(i)]; }
415 
416 	/**
417 	 * Sets the i-th component of the symbol number.
418 	 */
setNumberComponent(int i,int new_number)419 	void setNumberComponent(int i, int new_number) { Q_ASSERT(i >= 0 && i < int(number_components)); number[std::size_t(i)] = new_number; }
420 
421 
422 	/**
423 	 * Returns the symbol description.
424 	 */
getDescription()425 	const QString& getDescription() const { return description; }
426 
427 	/**
428 	 * Sets the symbol description.
429 	 */
setDescription(const QString & new_description)430 	void setDescription(const QString& new_description) { description = new_description; }
431 
432 
433 	/**
434 	 * Returns if this is a helper symbol (which is not printed in the final map).
435 	 */
isHelperSymbol()436 	bool isHelperSymbol() const { return is_helper_symbol; }
437 
438 	/**
439 	 * Sets if this is a helper symbol, see isHelperSymbol().
440 	 */
setIsHelperSymbol(bool value)441 	void setIsHelperSymbol(bool value) { is_helper_symbol = value; }
442 
443 
444 	/**
445 	 * Returns if this symbol is hidden.
446 	 */
isHidden()447 	bool isHidden() const { return is_hidden; }
448 
449 	/**
450 	 * Sets the hidden state of this symbol.
451 	 */
setHidden(bool value)452 	void setHidden(bool value) { is_hidden = value; }
453 
454 
455 	/**
456 	 * Returns if this symbol is protected.
457 	 *
458 	 * Objects with a protected symbol cannot be selected or edited.
459 	 */
isProtected()460 	bool isProtected() const { return is_protected; }
461 
462 	/**
463 	 * Sets the protected state of this symbol.
464 	 */
setProtected(bool value)465 	void setProtected(bool value) { is_protected = value; }
466 
467 
468 	/**
469 	 * Returns if objects with this symbol can be rotated in arbitrary directions.
470 	 */
isRotatable()471 	bool isRotatable() const { return is_rotatable; }
472 
473 protected:
474 	/**
475 	 * Sets the rotatability state of the symbol.
476 	 *
477 	 * Symbol implementations which are to support user-defined object
478 	 * rotatability shall import this function into public visibility.
479 	 * They also need to handle saving and loading.
480 	 */
481 	void setRotatable(bool value);
482 
483 
484 public:
485 	/**
486 	 * Creates a properties widget for the symbol.
487 	 */
488 	virtual SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) = 0;
489 
490 
491 	// Static
492 
493 	/** Returns a newly created symbol of the given type */
494 	static std::unique_ptr<Symbol> makeSymbolForType(Type type);
495 
496 	/**
497 	 * Returns if the symbol types can be applied to the same object types
498 	 */
499 	static bool areTypesCompatible(Type a, Type b);
500 
501 	/**
502 	 * Returns a bitmask of all types which can be applied to
503 	 * the same objects as the given type.
504 	 */
505 	static TypeCombination getCompatibleTypes(Type type);
506 
507 
508 	/**
509 	 * Compares two symbols by number.
510 	 *
511 	 * @return True if the number of s1 is less than the number of s2.
512 	 */
513 	static bool lessByNumber(const Symbol* s1, const Symbol* s2);
514 
515 	/**
516 	 * Compares two symbols by the dominant colors' priorities.
517 	 *
518 	 * @return True if s1's dominant color's priority is lower than s2's dominant color's priority.
519 	 */
520 	static bool lessByColorPriority(const Symbol* s1, const Symbol* s2);
521 
522 	/**
523 	 * A functor for comparing symbols by dominant colors.
524 	 *
525 	 * Other than lessByColorPriority(), this comparison uses the lowest
526 	 * priority which exists for a particular RGB color in the map, i.e.
527 	 * it groups colors by RGB values (and orders the result by priority).
528 	 *
529 	 * All map colors are preprocessed in the constructor. Thus the functor
530 	 * becomes invalid as soon as colors are changed.
531 	 */
532 	struct lessByColor
533 	{
534 		/**
535 		 * Constructs the functor for the given map.
536 		 */
537 		lessByColor(const Map* map);
538 
539 		/**
540 		 * Operator which compares two symbols by dominant colors.
541 		 *
542 		 * \return True if s1's dominant color exists with lower priority then s2's dominant color.
543 		 */
544 		bool operator() (const Symbol* s1, const Symbol* s2) const;
545 
546 	private:
547 		/**
548 		 * RGB values ordered by color priority.
549 		 */
550 		std::vector<QRgb> colors;
551 	};
552 
553 
554 	/**
555 	 * Number of components of symbol numbers.
556 	 */
557 	constexpr static auto number_components = 3u;
558 
559 
560 protected:
561 	/**
562 	 * Must be overridden to save type-specific symbol properties.
563 	 *
564 	 * The map pointer can be used to get persistent indices to any pointers on map data.
565 	 */
566 	virtual void saveImpl(QXmlStreamWriter& xml, const Map& map) const = 0;
567 
568 	/**
569 	 * Must be overridden to load type-specific symbol properties.
570 	 *
571 	 * \see saveImpl()
572 	 *
573 	 * Returns false if the current xml tag does not belong to the symbol and
574 	 * should be skipped, true if the element has been read completely.
575 	 */
576 	virtual bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict, int version) = 0;
577 
578 	/**
579 	 * Must be overridden to compare specific attributes.
580 	 */
581 	virtual bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const = 0;
582 
583 
584 private:
585 	mutable QImage icon;  ///< Cached symbol icon
586 	QImage custom_icon;   ///< Custom symbol icon
587 	QString name;
588 	QString description;
589 	std::array<int, number_components> number;
590 	Type type;
591 	bool is_helper_symbol;    /// \see isHelperSymbol()
592 	bool is_hidden;           /// \see isHidden()
593 	bool is_protected;        /// \see isProtected()
594 	bool is_rotatable = false;
595 };
596 
597 
598 
599 /**
600  * Duplicates a symbol.
601  *
602  * This free template function mitigates the incompatibility of std::unique_ptr
603  * with covariant return types when duplicating instances of the
604  * polymorphic type Symbol.
605  *
606  * Synopsis:
607  *
608  *    AreaSymbol s;
609  *    auto s1 = duplicate(s);          // unique_ptr<AreaSymbol>
610  *    auto s2 = duplicate<Symbol>(s);  // unique_ptr<Symbol>
611  *    std::unique_ptr<Symbol> s3 = std::move(s1);
612  */
613 template<class S>
614 typename std::enable_if<std::is_base_of<Symbol, S>::value, std::unique_ptr<S>>::type
duplicate(const S & s)615 /*std::unique_ptr<S>*/ duplicate(const S& s)
616 {
617 	return Symbol::duplicate<S>(s);
618 }
619 
620 
621 }  // namespace OpenOrienteering
622 
623 
624 Q_DECLARE_METATYPE(const OpenOrienteering::Symbol*)
625 
626 Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::Symbol::TypeCombination)
627 Q_DECLARE_OPERATORS_FOR_FLAGS(OpenOrienteering::Symbol::RenderableOptions)
628 
629 
630 #endif
631