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