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_TEMPLATE_H 23 #define OPENORIENTEERING_TEMPLATE_H 24 25 #include <functional> 26 #include <memory> 27 #include <vector> 28 29 #include <QtGlobal> 30 #include <QObject> 31 #include <QPointF> 32 #include <QString> 33 34 #include "core/map_coord.h" 35 #include "util/matrix.h" 36 #include "util/transformation.h" 37 38 class QByteArray; 39 class QColor; 40 class QDir; 41 class QFileInfo; 42 class QPainter; 43 class QPointF; 44 class QRectF; 45 class QTransform; 46 class QWidget; 47 class QXmlStreamReader; 48 class QXmlStreamWriter; 49 50 namespace OpenOrienteering { 51 52 class Map; 53 class MapView; 54 class Object; 55 56 57 /** 58 * Transformation parameters for non-georeferenced templates, 59 * transforming template coordinates to map coordinates. 60 * 61 * The parameters are applied to painter in the order 62 * 1. translate 63 * 2. rotate 64 * 3. scale. 65 * 66 * So this order is also chosen for the member variables, and 67 * thus used in list initialization. 68 * 69 * \see Template::applyTemplateTransform() 70 * 71 * Coordinate transformations use the opposite order for the 72 * same effect. 73 */ 74 struct TemplateTransform 75 { 76 /// x position in 1/1000 mm 77 qint32 template_x = 0; 78 /// x position in 1/1000 mm 79 qint32 template_y = 0; 80 81 /// Rotation in radians, a positive rotation is counter-clockwise. 82 double template_rotation = 0.0; 83 84 /// Scaling in x direction, smaller than 1 shrinks. 85 double template_scale_x = 1.0; 86 /// Scaling in y direction, smaller than 1 shrinks. 87 double template_scale_y = 1.0; 88 /// Adjustment to scaling if anisotropy is askew. 89 double template_shear = 0.0; 90 91 static TemplateTransform fromQTransform(const QTransform& qt) noexcept; 92 93 std::function<void (Object*)> makeObjectTransform() const; 94 95 void save(QXmlStreamWriter& xml, const QString& role) const; 96 void load(QXmlStreamReader& xml); 97 }; 98 99 bool operator==(const TemplateTransform& lhs, const TemplateTransform& rhs) noexcept; 100 bool operator!=(const TemplateTransform& lhs, const TemplateTransform& rhs) noexcept; 101 102 103 104 /** 105 * Abstract base class for templates. 106 */ 107 class Template : public QObject 108 { 109 Q_OBJECT 110 public: 111 /** 112 * States in the lifetime of a template. 113 */ 114 enum State 115 { 116 /// The template data is loaded and ready to be displayed. 117 Loaded = 0, 118 /// The template data is not yet loaded or has been unloaded. 119 /// It is assumed to be available, so it can be (re-)loaded if needed. 120 Unloaded, 121 /// A required resource cannot be found (e.g. missing image or font), 122 /// so the template data cannot be loaded. 123 Invalid 124 }; 125 126 /** 127 * The result of template file lookup attempts. 128 */ 129 enum LookupResult 130 { 131 NotFound = 0, ///< File not found at all. 132 FoundInMapDir = 1, ///< File name found in the map's directory. 133 FoundByRelPath = 2, ///< File found by relative path from the map's directory. 134 FoundByAbsPath = 3, ///< File found by absolute path. 135 }; 136 137 /** 138 * Indicates arguments which must not be nullptr. 139 * \todo Use the Guideline Support Library 140 */ 141 template <typename T> 142 using not_null = T; 143 144 145 protected: 146 /** 147 * Initializes the template as "Unloaded". 148 */ 149 Template(const QString& path, not_null<Map*> map); 150 151 /** 152 * Copy-construction as duplication helper. 153 */ 154 Template(const Template& proto); 155 156 public: 157 ~Template() override; 158 159 /** 160 * Creates a duplicate of the template 161 */ 162 virtual Template* duplicate() const = 0; 163 164 /** 165 * Returns a string which should identify the type of the template uniquely: 166 * the class name. Very simple RTTI feature. 167 */ 168 virtual const char* getTemplateType() const = 0; 169 170 171 /** 172 * Returns a description of the last error that occurred. 173 */ 174 QString errorString() const; 175 176 /** 177 * Returns true if the template is raster graphics. 178 * 179 * Raster graphics cannot be printed in the foreground in vector mode. 180 */ 181 virtual bool isRasterGraphics() const = 0; 182 183 184 /** 185 * Saves template parameters. 186 * 187 * This method saves common properties such as filename, transformation, 188 * adjustment, etc., and it calls saveTypeSpecificTemplateConfiguration for 189 * saving type-specific parameters (e.g. filtering mode for images). 190 * 191 * If a target directory is given via `map_dir`, the relative template 192 * path is determined for this directory. 193 */ 194 void saveTemplateConfiguration(QXmlStreamWriter& xml, bool open, const QDir* map_dir = nullptr) const; 195 196 /** 197 * Creates and returns a template from the configuration in the XML stream. 198 * 199 * Returns a null pointer in the case of error. 200 */ 201 static std::unique_ptr<Template> loadTemplateConfiguration(QXmlStreamReader& xml, Map& map, bool& open); 202 203 204 /** 205 * Saves the template itself, returns true if successful. 206 * 207 * This is called when saving the map if the template's hasUnsavedChanges() 208 * is true. 209 */ 210 virtual bool saveTemplateFile() const; 211 212 /** 213 * Changes a template's file without changing the parameters. 214 * 215 * Useful when a template file has been moved. 216 * If load_file is true, tries to load the given file. 217 */ 218 void switchTemplateFile(const QString& new_path, bool load_file); 219 220 /** 221 * Shows the dialog to find a moved template. 222 * 223 * If the user selects a new file, tries to switch to the selected template 224 * file using switchTemplateFile() and by trying to load the new file. 225 * Returns true if this succeeds; if not, reverts the switch and returns 226 * false. Also returns false if the dialog is aborted. 227 */ 228 bool execSwitchTemplateFileDialog(QWidget* dialog_parent); 229 230 231 /** 232 * Does everything needed to load a template. 233 * 234 * Calls preLoadConfiguration(), loadTemplateFile() and 235 * postLoadConfiguration(). Returns if the process was successful. 236 * 237 * The passed-in view is used to center the template if needed. 238 */ 239 bool configureAndLoad(QWidget* dialog_parent, MapView* view); 240 241 /** 242 * Tries to find the template file non-interactively. 243 * 244 * Thus function updates the path and name variables, and the template state. 245 * If successful, changes the state from Invalid to Unloaded if necessary, 246 * and returns true. Otherwise, changes the state from Unloaded to Invalid 247 * if necessary, and returns false. (If the state is Loaded, it is left 248 * unchanged.) It returns an indication of its success. 249 * 250 * This function searches for the template in the following locations: 251 * 252 * 1. The relative path with regard to the map directory, if both are valid. 253 * This alternative has precedence because it should always work, 254 * especially after coyping or moving a whole working directory on the 255 * same computer or to another one. 256 * 257 * 2. The absolute path of the template. 258 * This is the most explicit alternative. It works on the same computer 259 * when the map file is copied or moved to another location. 260 * 261 * 3. The map directory, if valid, for the filename of the template. 262 * This is a fallback for use cases where a map and selected templates 263 * are moved to the same flat folder, e.g. when receiving them via 264 * individual e-mail attachments. 265 * 266 * \param map_path Either the full filepath of the map, or an arbitrary 267 * directory which shall be regarded as the map directory. 268 */ 269 LookupResult tryToFindTemplateFile(const QString& map_path); 270 271 /** 272 * Tries to find and load the template file non-interactively. 273 * 274 * Returns true if the template was loaded successful. 275 * 276 * If out_loaded_from_map_dir is given, it is set to true if the template file 277 * is found using the template filename in the map's directory. 278 * 279 * \see tryToFindTemplateFile 280 */ 281 bool tryToFindAndReloadTemplateFile(const QString& map_path); 282 283 284 /** 285 * Does configuration before the actual template is loaded. 286 * 287 * This function is called after the user chooses the template file, but 288 * before it is loaded. Derived classes can show dialogs here to get user 289 * input which is needed to interpret the template file. 290 * 291 * If the implementation returns false, loading the template is aborted. 292 * 293 * \note Derived classes should set is_georeferenced either here or in 294 * postLoadConfiguration(). 295 * By default templates are loaded as non-georeferenced. 296 */ 297 virtual bool preLoadConfiguration(QWidget* dialog_parent); 298 299 /** 300 * Loads the template file. 301 * 302 * This function can be called if the template state is Invalid or Unloaded. 303 * It must not be called if the template file is already loaded. 304 * It returns true if the template is loaded successfully. 305 * 306 * Set the configuring parameter to true if the template is currently being 307 * configured by the user (in contrast to the case where it is reloaded, e.g. 308 * when loaded while reopening an existing map file). 309 */ 310 bool loadTemplateFile(bool configuring); 311 312 /** 313 * Does configuration after the actual template is loaded. 314 * 315 * This function is called after the user chose the template file and after 316 * the chosen file was successfully loaded. Derived classes can show dialogs 317 * here to get user input which is needed to interpret the template file. 318 * 319 * If the implementation returns false, loading the template is aborted. 320 * 321 * By setting out_center_in_view, the implementation can decide if the 322 * template should be centered in the active view (only if it is not 323 * georeferenced.) 324 */ 325 virtual bool postLoadConfiguration(QWidget* dialog_parent, bool& out_center_in_view); 326 327 /** 328 * Unloads the template file. 329 * 330 * Can be called if the template state is Loaded. 331 * Must not be called if the template file is already unloaded, or invalid. 332 */ 333 void unloadTemplateFile(); 334 335 /** 336 * Draws the template using the given painter with the given opacity. 337 * 338 * The painter transformation is set to use template coordinates. 339 * The clip rect is in template coordinates. 340 * The scale is the combined view & template scale. It can be used to give 341 * a minimum size to elements. 342 */ 343 virtual void drawTemplate(QPainter* painter, const QRectF& clip_rect, double scale, bool on_screen, qreal opacity) const = 0; 344 345 346 /** 347 * Calculates the template's bounding box in map coordinates. 348 */ 349 virtual QRectF calculateTemplateBoundingBox() const; 350 351 /** 352 * Returns the extra extent of the template out of the bounding box. 353 * 354 * While the bounding box is defined in map coordinates, this border is 355 * given in pixels. This is useful for elements which stay the same size 356 * regardless of the zoom level so that a bounding box in map coords 357 * cannot be calculated. 358 */ getTemplateBoundingBoxPixelBorder()359 virtual int getTemplateBoundingBoxPixelBorder() {return 0;} 360 361 /** 362 * Marks the whole area of the template as needing a redraw. 363 * 364 * Use this before and after modifications to the template transformation. 365 * 366 * The default implementation marks everything as "to be redrawn" for 367 * georeferenced templates and uses the reported extent otherwise. 368 */ 369 virtual void setTemplateAreaDirty(); 370 371 372 /** 373 * Must return if freehand drawing onto the template is possible. 374 */ 375 virtual bool canBeDrawnOnto() const; 376 377 /** 378 * Draws onto the template. 379 * 380 * This only works for templates for which canBeDrawnOnto() returns true. 381 * 382 * coords is an array of points with which the drawn line is defined and 383 * must contain at least 2 points. 384 * 385 * If map_bbox is an invalid rect, then the method will calculate it itself. 386 * 387 * \todo Rewrite using a range of MapCoordF. 388 */ 389 void drawOntoTemplate(not_null<MapCoordF*> coords, int num_coords, const QColor& color, qreal width, QRectF map_bbox); 390 391 /** 392 * Triggers an undo or redo action for template freehand drawing. 393 * 394 * This only works for templates for which canBeDrawnOnto() returns true. 395 */ 396 virtual void drawOntoTemplateUndo(bool redo); 397 398 399 // Transformation related methods for non-georeferenced templates only 400 401 /** 402 * Changes the painter's transformation so it can be used to draw in template coordinates. 403 * 404 * The previous transformation of the painter must be the map transformation. 405 * 406 * \note For non-georeferenced templates only, or if the template 407 * transformation has been set by the template nevertheless. 408 */ 409 void applyTemplateTransform(QPainter* painter) const; 410 411 /** 412 * Returns the extent of the template in template coordinates. 413 * 414 * The default implementation returns a "very big" rectangle. 415 * 416 * \note For non-georeferenced templates only! 417 */ 418 virtual QRectF getTemplateExtent() const; 419 420 /** 421 * Scales the template with the given scaling center. 422 * 423 * \note For non-georeferenced templates only! 424 */ 425 void scale(double factor, const MapCoord& center); 426 427 /** 428 * Rotates the template around the given point. 429 * 430 * \note For non-georeferenced templates only! 431 */ 432 void rotate(double rotation, const MapCoord& center); 433 434 435 // Coordinate transformations between template coordinates and map coordinates 436 mapToTemplate(const MapCoordF & coords)437 inline MapCoordF mapToTemplate(const MapCoordF& coords) const 438 { 439 return MapCoordF(map_to_template.get(0, 0) * coords.x() + map_to_template.get(0, 1) * coords.y() + map_to_template.get(0, 2), 440 map_to_template.get(1, 0) * coords.x() + map_to_template.get(1, 1) * coords.y() + map_to_template.get(1, 2)); 441 } mapToTemplateOther(const MapCoordF & coords)442 inline MapCoordF mapToTemplateOther(const MapCoordF& coords) const // normally not needed - this uses the other transformation parameters 443 { 444 Q_ASSERT(!is_georeferenced); 445 // SLOW - cache this matrix if needed often 446 Matrix map_to_template_other; 447 template_to_map_other.invert(map_to_template_other); 448 return MapCoordF(map_to_template_other.get(0, 0) * coords.x() + map_to_template_other.get(0, 1) * coords.y() + map_to_template_other.get(0, 2), 449 map_to_template_other.get(1, 0) * coords.x() + map_to_template_other.get(1, 1) * coords.y() + map_to_template_other.get(1, 2)); 450 } templateToMap(const QPointF & coords)451 inline MapCoordF templateToMap(const QPointF& coords) const 452 { 453 return MapCoordF(template_to_map.get(0, 0) * coords.x() + template_to_map.get(0, 1) * coords.y() + template_to_map.get(0, 2), 454 template_to_map.get(1, 0) * coords.x() + template_to_map.get(1, 1) * coords.y() + template_to_map.get(1, 2)); 455 } templateToMapOther(const QPointF & coords)456 inline MapCoordF templateToMapOther(const QPointF& coords) const // normally not needed - this uses the other transformation parameters 457 { 458 Q_ASSERT(!is_georeferenced); 459 return MapCoordF(template_to_map_other.get(0, 0) * coords.x() + template_to_map_other.get(0, 1) * coords.y() + template_to_map_other.get(0, 2), 460 template_to_map_other.get(1, 0) * coords.x() + template_to_map_other.get(1, 1) * coords.y() + template_to_map_other.get(1, 2)); 461 } 462 463 464 // Pass points & adjustment 465 getPassPointList()466 inline const PassPointList& getPassPointList() const {return passpoints;} getPassPointList()467 inline PassPointList& getPassPointList() {return passpoints;} getNumPassPoints()468 inline int getNumPassPoints() const {return passpoints.size();} getPassPoint(int i)469 inline PassPoint* getPassPoint(int i) {return &passpoints[i];} 470 void addPassPoint(const PassPoint& point, int pos); 471 void deletePassPoint(int pos); 472 void clearPassPoints(); 473 474 /// Change from adjusted into original state or the other way round 475 void switchTransforms(); 476 void getTransform(TemplateTransform& out) const; 477 void setTransform(const TemplateTransform& transform); 478 void getOtherTransform(TemplateTransform& out) const; 479 void setOtherTransform(const TemplateTransform& transform); 480 481 482 // Getters / Setters 483 // General 484 getMap()485 inline Map* getMap() const {return map;} 486 getTemplateFilename()487 inline const QString& getTemplateFilename() const {return template_file;} 488 489 /// Changes the path and filename only. Does not do any reloading etc. 490 void setTemplateFileInfo(const QFileInfo& file_info); 491 getTemplatePath()492 inline const QString& getTemplatePath() const {return template_path;} 493 /// Changes the path and filename only. Does not do any reloading etc. 494 void setTemplatePath(const QString& value); 495 getTemplateRelativePath()496 inline const QString& getTemplateRelativePath() const {return template_relative_path;} setTemplateRelativePath(const QString & value)497 inline void setTemplateRelativePath(const QString& value) {template_relative_path = value;} 498 getTemplateState()499 inline State getTemplateState() const {return template_state;} setTemplateState(State state)500 inline void setTemplateState(State state) {template_state = state;} 501 getTemplateGroup()502 inline int getTemplateGroup() const {return template_group;} setTemplateGroup(int value)503 inline void setTemplateGroup(int value) {template_group = value;} 504 hasUnsavedChanges()505 inline bool hasUnsavedChanges() const {return has_unsaved_changes;} 506 void setHasUnsavedChanges(bool value); 507 isTemplateGeoreferenced()508 inline bool isTemplateGeoreferenced() const {return is_georeferenced;} 509 510 /** 511 * Returns if the template allows the georefencing state to be changed at all. 512 * 513 * The default implementation returns false. 514 */ 515 virtual bool canChangeTemplateGeoreferenced(); 516 517 /** 518 * Tries to change the usage of georeferencing data. 519 * 520 * If supported by the actual template, this function tries to switch the 521 * state between non-georeferenced and georeferenced. It returns false when 522 * an error occurred, and true if the change was successful or if it was 523 * explicitly cancelled by the user. 524 * 525 * The default implementation returns true iff the state matches the value. 526 */ 527 virtual bool trySetTemplateGeoreferenced(bool value, QWidget* dialog_parent); 528 529 530 // Transformation of non-georeferenced templates 531 532 MapCoord templatePosition() const; 533 void setTemplatePosition(const MapCoord& coord); 534 535 MapCoord templatePositionOffset() const; 536 void setTemplatePositionOffset(const MapCoord& offset); 537 void applyTemplatePositionOffset(); 538 void resetTemplatePositionOffset(); 539 getTemplateX()540 inline qint64 getTemplateX() const {return transform.template_x;} setTemplateX(qint64 x)541 inline void setTemplateX(qint64 x) {transform.template_x = x; updateTransformationMatrices();} 542 getTemplateY()543 inline qint64 getTemplateY() const {return transform.template_y;} setTemplateY(qint64 y)544 inline void setTemplateY(qint64 y) {transform.template_y = y; updateTransformationMatrices();} 545 getTemplateScaleX()546 inline double getTemplateScaleX() const {return transform.template_scale_x;} setTemplateScaleX(double scale_x)547 inline void setTemplateScaleX(double scale_x) {transform.template_scale_x = scale_x; updateTransformationMatrices();} getTemplateScaleY()548 inline double getTemplateScaleY() const {return transform.template_scale_y;} setTemplateScaleY(double scale_y)549 inline void setTemplateScaleY(double scale_y) {transform.template_scale_y = scale_y; updateTransformationMatrices();} getTemplateShear()550 inline double getTemplateShear() const {return transform.template_shear;} setTemplateShear(double shear)551 inline void setTemplateShear(double shear) {transform.template_shear = shear; updateTransformationMatrices();} 552 getTemplateRotation()553 inline double getTemplateRotation() const {return transform.template_rotation;} setTemplateRotation(double rotation)554 inline void setTemplateRotation(double rotation) {transform.template_rotation = rotation; updateTransformationMatrices();} 555 isAdjustmentApplied()556 inline bool isAdjustmentApplied() const {return adjusted;} isAdjustmentDirty()557 inline bool isAdjustmentDirty() const {return adjustment_dirty;} 558 void setAdjustmentDirty(bool value); 559 560 561 /** 562 * Returns true if the template has elements which are not opaque. 563 * 564 * The default implementation returns true when the template is loaded. 565 */ 566 virtual bool hasAlpha() const; 567 568 569 // Static 570 /** 571 * Returns the filename extensions supported by known subclasses. 572 */ 573 static const std::vector<QByteArray>& supportedExtensions(); 574 575 /** 576 * Creates a Template instance for the given path. 577 * 578 * This function tries to find a matching template subclass by looking at 579 * the file extension. It may return nullptr if no subclass supports the 580 * extension. 581 */ 582 static std::unique_ptr<Template> templateForFile(const QString& path, Map* map); 583 584 /** 585 * A flag which disables the writing of absolute paths for template files. 586 * 587 * By default, class Template saves absolute paths. This behavior can be 588 * changed by setting this flag to true. This allows to hide local (or 589 * private) directory names. 590 * 591 * This flag defaults to false. 592 */ 593 static bool suppressAbsolutePaths; 594 595 596 signals: 597 /** 598 * Emitted whenever template_state was changed. 599 */ 600 void templateStateChanged(); 601 602 603 protected: 604 /** 605 * Sets the error description which will be returned by errorString(). 606 */ 607 void setErrorString(const QString &text); 608 609 610 /** 611 * Hook for saving parameters needed by the actual template type. 612 * 613 * The default implementation does nothing. 614 */ 615 virtual void saveTypeSpecificTemplateConfiguration(QXmlStreamWriter& xml) const; 616 617 /** 618 * Hook for loading parameters needed by the actual template type. 619 * 620 * \note The default implementation calls xml.skipCurrentElement(). 621 * Implementations must do the same if they do not parse it. 622 * 623 * Returns true on success. 624 */ 625 virtual bool loadTypeSpecificTemplateConfiguration(QXmlStreamReader& xml); 626 627 628 /** 629 * Hook for loading the actual template file non-interactively. 630 * 631 * Returns true if successful. 632 * 633 * If configuring is true, a call to postLoadConfiguration() will follow 634 * if this returns true. 635 */ 636 virtual bool loadTemplateFileImpl(bool configuring) = 0; 637 638 /** 639 * Hook for unloading the template file. 640 */ 641 virtual void unloadTemplateFileImpl() = 0; 642 643 644 /** 645 * Hook for drawing on the template. 646 * 647 * Draws the polyline given by the points onto the template. 648 * Required if canBeDrawnOnto() returns true. 649 */ 650 virtual void drawOntoTemplateImpl(MapCoordF* coords, int num_coords, const QColor& color, qreal width); 651 652 653 /** 654 * Must be called after direct changes to transform or other_transform. 655 */ 656 void updateTransformationMatrices(); 657 658 659 // General properties 660 661 /// Map containing this template 662 Map* map; 663 664 /// The filename of the template file (e.g. "map.bmp") 665 QString template_file; 666 667 /// The complete path to the template file including the filename (e.g. "/home/me/map.bmp") 668 QString template_path; 669 670 /// The template path relative to the map file (e.g. "../me/map.bmp"). 671 /// Can be empty as long as the map file has not been saved yet. 672 QString template_relative_path; 673 674 /// The template lifetime state 675 State template_state = Unloaded; 676 677 /// The description of the last error 678 QString error_string; 679 680 /// Does the template itself (not its transformation) have unsaved changes (e.g. GPS track has changed, image has been painted on) 681 bool has_unsaved_changes = false; 682 683 /// Is the template in georeferenced mode? 684 bool is_georeferenced = false; 685 686 private: 687 // Properties for non-georeferenced templates (invalid if is_georeferenced is true) 688 689 /// Bounds correction offset for map templates. Must be masked out when saving. 690 MapCoord accounted_offset; 691 692 /** 693 * This class reverts the template's accounted offset for its lifetime. 694 * 695 * \todo Copy/restore the transformation matrices when this no longer needs 696 * allocations. 697 */ 698 class ScopedOffsetReversal; 699 700 protected: 701 /// Currently active transformation. NOTE: after direct changes here call updateTransformationMatrices() 702 TemplateTransform transform; 703 704 /// Currently inactive transformation (adjusted or original). NOTE: after direct changes here call updateTransformationMatrices() 705 TemplateTransform other_transform; 706 707 /// If true, transform is the adjusted transformation, otherwise it is the original one 708 bool adjusted = false; 709 710 /// If true, the adjusted transformation has to be recalculated 711 bool adjustment_dirty = true; 712 713 /// List of pass points for position adjustment 714 PassPointList passpoints; 715 716 /// Number of the template group. If the template is not grouped, this is set to -1. 717 /// \todo Switch to initialization with -1. ATM 0 is kept for compatibility. 718 int template_group = 0; 719 720 // Transformation matrices calculated from cur_trans 721 Matrix map_to_template; 722 Matrix template_to_map; 723 Matrix template_to_map_other; 724 }; 725 726 727 } // namespace OpenOrienteering 728 729 #endif 730