1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2012-2020 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_LINE_SYMBOL_H
23 #define OPENORIENTEERING_LINE_SYMBOL_H
24 
25 #include <vector>  // IWYU pragma: keep
26 
27 #include <Qt>
28 #include <QtGlobal>
29 #include <QString>
30 
31 #include "core/map_coord.h"  // IWYU pragma: keep
32 #include "core/symbols/symbol.h"
33 
34 class QXmlStreamReader;
35 class QXmlStreamWriter;
36 
37 namespace OpenOrienteering {
38 
39 class LineSymbol;
40 class Map;
41 class MapColor;
42 class MapColorMap;
43 class Object;
44 class ObjectRenderables;
45 class PathObject;
46 class PathPartVector;
47 class PointSymbol;
48 class SplitPathCoord;
49 class SymbolPropertiesWidget;
50 class SymbolSettingDialog;
51 class VirtualCoordVector;
52 class VirtualPath;
53 
54 using MapCoordVector = std::vector<MapCoord>;
55 using MapCoordVectorF = std::vector<MapCoordF>;
56 
57 
58 /**
59  * Settings for a line symbol's border.
60  */
61 struct LineSymbolBorder
62 {
63 	const MapColor* color = nullptr;
64 	int width             = 0;
65 	int shift             = 0;
66 	int dash_length       = 2000;
67 	int break_length      = 1000;
68 	bool dashed           = false;
69 
70 	// Default special member functions are fine.
71 
72 	void save(QXmlStreamWriter& xml, const Map& map) const;
73 	bool load(QXmlStreamReader& xml, const Map& map);
74 
75 	bool isVisible() const;
76 	void setupSymbol(LineSymbol& out) const;
77 	void scale(double factor);
78 
79 	/// Compares without attention for color priority.
80 	bool equals(const LineSymbolBorder& other) const;
81 };
82 
83 
84 /// Compares including color priority.
85 bool operator==(const LineSymbolBorder &lhs, const LineSymbolBorder &rhs) noexcept;
86 
87 inline bool operator!=(const LineSymbolBorder &lhs, const LineSymbolBorder &rhs) noexcept
88 {
89 	return !(lhs == rhs);
90 }
91 
92 
93 
94 /** Symbol for PathObjects which displays a line along the path. */
95 class LineSymbol : public Symbol
96 {
97 friend class LineSymbolSettings;
98 friend class PointSymbolEditorWidget;
99 friend class OCAD8FileImport;
100 public:
101 	enum CapStyle
102 	{
103 		FlatCap = 0,
104 		RoundCap = 1,
105 		SquareCap = 2,
106 		PointedCap = 3
107 	};
108 
109 	enum JoinStyle
110 	{
111 		BevelJoin = 0,
112 		MiterJoin = 1,
113 		RoundJoin = 2
114 	};
115 
116 	/**
117 	 * Mid symbol placement on dashed lines.
118 	 */
119 	enum MidSymbolPlacement
120 	{
121 		CenterOfDash      = 0,  ///< Mid symbols on every dash
122 		CenterOfDashGroup = 1,  ///< Mid symbols on the center of a dash group
123 		CenterOfGap       = 2,  ///< Mid symbols on the main gap (i.e. not between dashes in a group)
124 		NoMidSymbols      = 99
125 	};
126 
127 	/** Constructs an empty line symbol. */
128 	LineSymbol() noexcept;
129 	~LineSymbol() override;
130 
131 protected:
132 	explicit LineSymbol(const LineSymbol& proto);
133 	LineSymbol* duplicate() const override;
134 
135 public:
136 	bool validate() const override;
137 
138 	void createRenderables(
139 	        const Object *object,
140 	        const VirtualCoordVector &coords,
141 	        ObjectRenderables &output,
142 	        Symbol::RenderableOptions options ) const override;
143 
144 	void createRenderables(
145 	        const PathObject* object,
146 	        const PathPartVector& path_parts,
147 	        ObjectRenderables &output,
148 	        Symbol::RenderableOptions options ) const override;
149 
150 	/**
151 	 * Creates the renderables for a single path (i.e. a single part).
152 	 *
153 	 * For the single path part represented by VirtualPath, this function
154 	 * takes care of all renderables for the main line, borders, mid symbol
155 	 * and dash symbol being added to the output. It does not deal with
156 	 * start symbols and end symbols.
157 	 */
158 	void createSinglePathRenderables(const VirtualPath& path, bool path_closed, ObjectRenderables& output) const;
159 
160 
161 	void colorDeletedEvent(const MapColor* color) override;
162 	bool containsColor(const MapColor* color) const override;
163 	const MapColor* guessDominantColor() const override;
164 	void replaceColors(const MapColorMap& color_map) override;
165 	void scale(double factor) override;
166 
167 	/**
168 	 * Creates empty point symbols with the given names for undefined subsymbols.
169 	 *
170 	 * After calling this method, all subsymbols are defined, i.e. not nullptr.
171 	 * Call cleanupPointSymbols() later to remove the empty symbols.
172 	 */
173     void ensurePointSymbols(
174 		const QString& start_name,
175 		const QString& mid_name,
176 		const QString& end_name,
177 		const QString& dash_name
178 	);
179 
180 	/**
181 	 * Deletes unused point symbols and sets them to nullptr again.
182 	 *
183 	 * See ensurePointSymbols().
184 	 */
185 	void cleanupPointSymbols();
186 
187 
188 	/**
189 	 * Returns the dimension which shall considered when scaling the icon.
190 	 */
191 	qreal dimensionForIcon() const override;
192 
193 	/**
194 	 * Returns the largest extent (half width) of the components of this line.
195 	 */
196 	qreal calculateLargestLineExtent() const override;
197 
198 
199 
200 	/**
201 	 * Returns the limit for miter joins in units of the line width.
202 	 * See the Qt docs for QPainter::setMiterJoin().
203 	 * TODO: Should that better be a line property?
204 	 * FIXME: shall be 0 for border lines.
205 	 */
miterLimit()206 	static constexpr qreal miterLimit() { return 1; }
207 
208 	// Getters / Setters
getLineWidth()209 	inline int getLineWidth() const {return line_width;}
setLineWidth(double width)210 	inline void setLineWidth(double width) {line_width = qRound(1000 * width);}
getColor()211 	inline const MapColor* getColor() const {return color;}
setColor(const MapColor * color)212 	inline void setColor(const MapColor* color) {this->color = color;}
getMinimumLength()213 	inline int getMinimumLength() const {return minimum_length;}
setMinimumLength(int length)214 	inline void setMinimumLength(int length) {this->minimum_length = length;}
getCapStyle()215 	inline CapStyle getCapStyle() const {return cap_style;}
setCapStyle(CapStyle style)216 	inline void setCapStyle(CapStyle style) {cap_style = style;}
getJoinStyle()217 	inline JoinStyle getJoinStyle() const {return join_style;}
setJoinStyle(JoinStyle style)218 	inline void setJoinStyle(JoinStyle style) {join_style = style;}
219 
startOffset()220 	int startOffset() const { return start_offset; }
setStartOffset(int value)221 	void setStartOffset(int value) { start_offset = value; }
222 
endOffset()223 	int endOffset() const { return end_offset; }
setEndOffset(int value)224 	void setEndOffset(int value) { end_offset = value; }
225 
isDashed()226 	inline bool isDashed() const {return dashed;}
setDashed(bool value)227 	inline void setDashed(bool value) {dashed = value;}
228 
getStartSymbol()229 	inline PointSymbol* getStartSymbol() const {return start_symbol;}
230 	void setStartSymbol(PointSymbol* symbol);
getMidSymbol()231 	inline PointSymbol* getMidSymbol() const {return mid_symbol;}
232 	void setMidSymbol(PointSymbol* symbol);
getEndSymbol()233 	inline PointSymbol* getEndSymbol() const {return end_symbol;}
234 	void setEndSymbol(PointSymbol* symbol);
getDashSymbol()235 	inline PointSymbol* getDashSymbol() const {return dash_symbol;}
236 	void setDashSymbol(PointSymbol* symbol);
237 
getMidSymbolsPerSpot()238 	inline int getMidSymbolsPerSpot() const {return mid_symbols_per_spot;}
setMidSymbolsPerSpot(int value)239 	inline void setMidSymbolsPerSpot(int value) {mid_symbols_per_spot = value;}
getMidSymbolDistance()240 	inline int getMidSymbolDistance() const {return mid_symbol_distance;}
setMidSymbolDistance(int value)241 	inline void setMidSymbolDistance(int value) {mid_symbol_distance = value;}
getMidSymbolPlacement()242 	inline MidSymbolPlacement getMidSymbolPlacement() const { return mid_symbol_placement; }
243 	void setMidSymbolPlacement(MidSymbolPlacement placement);
244 
getSuppressDashSymbolAtLineEnds()245 	inline bool getSuppressDashSymbolAtLineEnds() const {return suppress_dash_symbol_at_ends;}
setSuppressDashSymbolAtLineEnds(bool value)246 	inline void setSuppressDashSymbolAtLineEnds(bool value) {suppress_dash_symbol_at_ends = value;}
getScaleDashSymbol()247 	bool getScaleDashSymbol() const { return scale_dash_symbol; }
setScaleDashSymbol(bool value)248 	void setScaleDashSymbol(bool value) { scale_dash_symbol = value; }
249 
getSegmentLength()250 	inline int getSegmentLength() const {return segment_length;}
setSegmentLength(int value)251 	inline void setSegmentLength(int value) {segment_length = value;}
getEndLength()252 	inline int getEndLength() const {return end_length;}
setEndLength(int value)253 	inline void setEndLength(int value) {end_length = value;}
getShowAtLeastOneSymbol()254 	inline bool getShowAtLeastOneSymbol() const {return show_at_least_one_symbol;}
setShowAtLeastOneSymbol(bool value)255 	inline void setShowAtLeastOneSymbol(bool value) {show_at_least_one_symbol = value;}
getMinimumMidSymbolCount()256 	inline int getMinimumMidSymbolCount() const {return minimum_mid_symbol_count;}
setMinimumMidSymbolCount(int value)257 	inline void setMinimumMidSymbolCount(int value) {minimum_mid_symbol_count = value;}
getMinimumMidSymbolCountWhenClosed()258 	inline int getMinimumMidSymbolCountWhenClosed() const {return minimum_mid_symbol_count_when_closed;}
setMinimumMidSymbolCountWhenClosed(int value)259 	inline void setMinimumMidSymbolCountWhenClosed(int value) {minimum_mid_symbol_count_when_closed = value;}
260 
getDashLength()261 	inline int getDashLength() const {return dash_length;}
setDashLength(int value)262 	inline void setDashLength(int value) {dash_length = value;}
getBreakLength()263 	inline int getBreakLength() const {return break_length;}
setBreakLength(int value)264 	inline void setBreakLength(int value) {break_length = value;}
getDashesInGroup()265 	inline int getDashesInGroup() const {return dashes_in_group;}
setDashesInGroup(int value)266 	inline void setDashesInGroup(int value) {dashes_in_group = value;}
getInGroupBreakLength()267 	inline int getInGroupBreakLength() const {return in_group_break_length;}
setInGroupBreakLength(int value)268 	inline void setInGroupBreakLength(int value) {in_group_break_length = value;}
getHalfOuterDashes()269 	inline bool getHalfOuterDashes() const {return half_outer_dashes;}
setHalfOuterDashes(bool value)270 	inline void setHalfOuterDashes(bool value) {half_outer_dashes = value;}
271 
hasBorder()272 	inline bool hasBorder() const {return have_border_lines;}
setHasBorder(bool value)273 	inline void setHasBorder(bool value) {have_border_lines = value;}
areBordersDifferent()274 	inline bool areBordersDifferent() const {return border != right_border;}
275 
getBorder()276 	inline LineSymbolBorder& getBorder() {return border;}
getBorder()277 	inline const LineSymbolBorder& getBorder() const {return border;}
getRightBorder()278 	inline LineSymbolBorder& getRightBorder() {return right_border;}
getRightBorder()279 	inline const LineSymbolBorder& getRightBorder() const {return right_border;}
280 
281 	SymbolPropertiesWidget* createPropertiesWidget(SymbolSettingDialog* dialog) override;
282 
283 protected:
284 	void saveImpl(QXmlStreamWriter& xml, const Map& map) const override;
285 	bool loadImpl(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict, int version) override;
286 	PointSymbol* loadPointSymbol(QXmlStreamReader& xml, const Map& map, SymbolDictionary& symbol_dict, int version);
287 	bool equalsImpl(const Symbol* other, Qt::CaseSensitivity case_sensitivity) const override;
288 
289 	/**
290 	 * Creates the border lines' renderables for a single path (i.e. a single part).
291 	 *
292 	 * For the single path part represented by VirtualPath, this function takes
293 	 * care of all renderables for the border lines being added to the output.
294 	 */
295 	void createBorderLines(
296 	        const VirtualPath& path,
297 	        const SplitPathCoord& start,
298 	        const SplitPathCoord& end,
299 	        ObjectRenderables& output
300 	) const;
301 
302 	void shiftCoordinates(
303 	        const VirtualPath& path,
304 	        double main_shift,
305 	        MapCoordVector& out_flags,
306 	        MapCoordVectorF& out_coords
307 	) const;
308 
309 	/**
310 	 * Creates flags and coords for a continuous line segment, and
311 	 * adds pointed line caps and mid symbol renderables to the output.
312 	 *
313 	 * Note that this function does not create LineRenderables.
314 	 */
315 	void processContinuousLine(
316 	        const VirtualPath& path,
317 	        const SplitPathCoord& start,
318 	        const SplitPathCoord& end,
319 	        bool set_mid_symbols,
320 	        MapCoordVector& processed_flags,
321 	        MapCoordVectorF& processed_coords,
322 	        ObjectRenderables& output
323 	) const;
324 
325 	void createPointedLineCap(
326 	        const VirtualPath& path,
327 	        const SplitPathCoord& start,
328 	        const SplitPathCoord& end,
329 	        bool is_end,
330 	        ObjectRenderables& output
331 	) const;
332 
333 	/**
334 	 * Creates flags and coords for a single dashed path (i.e. a single part)
335 	 * which may consist of multiple sections separated by dash points,
336 	 * and adds pointed line caps and mid symbol renderables to the output.
337 	 *
338 	 * Note that this function does not create LineRenderables.
339 	 */
340 	void processDashedLine(
341 	        const VirtualPath& path,
342 	        const SplitPathCoord& start,
343 	        const SplitPathCoord& end,
344 	        bool path_closed,
345 	        MapCoordVector& out_flags,
346 	        MapCoordVectorF& out_coords,
347 	        ObjectRenderables& output
348 	) const;
349 
350 	/**
351 	 * Creates flags and coords for a single dashed path section, and adds
352 	 * pointed line caps and mid symbol renderables to the output.
353 	 *
354 	 * This is the main function determining the layout of dash patterns.
355 	 * A path section is delimited by the part start, the part end, and/or
356 	 * by dash points.
357 	 *
358 	 * Note that this function does not create LineRenderables.
359 	 *
360 	 * \see LineSymbol::createMidSymbolRenderables
361 	 */
362 	SplitPathCoord createDashGroups(
363 	        const VirtualPath& path,
364 	        bool path_closed,
365 	        const SplitPathCoord& line_start,
366 	        const SplitPathCoord& start,
367 	        const SplitPathCoord& end,
368 	        bool is_part_start,
369 	        bool is_part_end,
370 	        MapCoordVector& out_flags,
371 	        MapCoordVectorF& out_coords,
372 	        ObjectRenderables& output
373 	) const;
374 
375 	void createDashSymbolRenderables(
376 	        const VirtualPath& path,
377 	        bool path_closed,
378 	        ObjectRenderables& output
379 	) const;
380 
381 	/**
382 	 * Adds just the mid symbol renderables for a single solid path
383 	 * (i.e. a single part) to the output.
384 	 *
385 	 * This is the main function determining the layout of mid symbols
386 	 * when the path is solid, not dashed.
387 	 *
388 	 * Note that this function does not create LineRenderables.
389 	 *
390 	 * \see LineSymbol::createDashGroups
391 	 */
392 	void createMidSymbolRenderables(
393 	        const VirtualPath& path,
394 	        const SplitPathCoord& start,
395 	        const SplitPathCoord& end,
396 	        bool path_closed,
397 	        ObjectRenderables& output
398 	) const;
399 
400 	/**
401 	 * Adds just the start and end symbol renderables to the output.
402 	 *
403 	 * The start symbol is placed at the start of the first part,
404 	 * and the end symbols is placed at the end of the last part.
405 	 */
406 	void createStartEndSymbolRenderables(
407 	        const PathPartVector& path_parts,
408 	        ObjectRenderables& output
409 	) const;
410 
411 
412 	void replaceSymbol(PointSymbol*& old_symbol, PointSymbol* replace_with, const QString& name);
413 
414 	// Members ordered for minimizing padding
415 
416 	// Border line details
417 	LineSymbolBorder border;
418 	LineSymbolBorder right_border;
419 
420 	// Point symbols
421 	PointSymbol* start_symbol;
422 	PointSymbol* mid_symbol;
423 	PointSymbol* end_symbol;
424 	PointSymbol* dash_symbol;
425 
426 	// Base line
427 	const MapColor* color;
428 	int line_width;		// in 1/1000 mm
429 	int minimum_length;
430 	int start_offset;
431 	int end_offset;
432 
433 	int mid_symbols_per_spot;
434 	int mid_symbol_distance;
435 	int minimum_mid_symbol_count;
436 	int minimum_mid_symbol_count_when_closed;
437 
438 	// Not dashed
439 	int segment_length;
440 	int end_length;
441 
442 	// Dashed
443 	int dash_length;
444 	int break_length;
445 	int dashes_in_group;
446 	int in_group_break_length;
447 
448 	CapStyle cap_style;
449 	JoinStyle join_style;
450 	MidSymbolPlacement mid_symbol_placement;
451 
452 	// Various flags
453 	bool dashed;
454 	bool half_outer_dashes;
455 	bool show_at_least_one_symbol;
456 	bool suppress_dash_symbol_at_ends;
457 	bool scale_dash_symbol;
458 	bool have_border_lines;
459 };
460 
461 
462 }  // namespace OpenOrienteering
463 
464 #endif
465