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