From 7b2ecc0edc0c86bf0e5627b63b65ec0b9a861997 Mon Sep 17 00:00:00 2001 From: Matthias Kuehlewein Date: Sun, 1 Dec 2024 23:58:40 +0100 Subject: [PATCH] Refactor distribute points tool The core functionality to distribute points evenly along a path was partly contained in the MapEditorController class (which was marked as a TODO). Move the functionality to the (renamed) DistributePointsDialog class. Add three dots to the submenu item's name to indicate that selecting it will open another window. Remove the 'Settings' since changing them in the dialog was not made persistent. --- src/gui/map/map_editor.cpp | 37 +----- src/tools/distribute_points_tool.cpp | 185 +++++++++++++++------------ src/tools/distribute_points_tool.h | 107 ++++------------ 3 files changed, 128 insertions(+), 201 deletions(-) diff --git a/src/gui/map/map_editor.cpp b/src/gui/map/map_editor.cpp index dd85f41460..de9ca6d237 100644 --- a/src/gui/map/map_editor.cpp +++ b/src/gui/map/map_editor.cpp @@ -1077,7 +1077,7 @@ void MapEditorController::createActions() simplify_path_act = newAction("simplify", tr("Simplify path"), this, SLOT(simplifyPathClicked()), "tool-simplify-path.png", QString{}, "toolbars.html#simplify_path"); clip_area_act = newToolAction("cliparea", tr("Clip area"), this, SLOT(clipAreaClicked()), "tool-clip.png", QString{}, "toolbars.html#clip_area"); erase_area_act = newToolAction("erasearea", tr("Erase area"), this, SLOT(eraseAreaClicked()), "tool-erase.png", QString{}, "toolbars.html#erase_area"); - distribute_points_act = newAction("distributepoints", tr("Distribute points along path"), this, SLOT(distributePointsClicked()), "tool-distribute-points.png", QString{}, "toolbars.html#distribute_points"); // TODO: write documentation + distribute_points_act = newAction("distributepoints", tr("Distribute points along path..."), this, SLOT(distributePointsClicked()), "tool-distribute-points.png", QString{}, "toolbars.html#distribute_points"); // TODO: write documentation paint_feature = std::make_unique(*this); @@ -3475,38 +3475,11 @@ void MapEditorController::eraseAreaClicked() void MapEditorController::distributePointsClicked() { Q_ASSERT(activeSymbol()->getType() == Symbol::Point); - PointSymbol* point = activeSymbol()->asPoint(); + const auto point = activeSymbol()->asPoint(); - DistributePointsTool::Settings settings; - if (!DistributePointsTool::showSettingsDialog(window, point, settings)) - return; - - // Create points along paths - std::vector created_objects; - for (const auto* object : map->selectedObjects()) - { - if (object->getType() == Object::Path) - DistributePointsTool::execute(object->asPath(), point, settings, created_objects); - } - if (created_objects.empty()) - return; - - // Add points to map - for (auto* o : created_objects) - map->addObject(o); - - // Create undo step and select new objects - map->clearObjectSelection(false); - MapPart* part = map->getCurrentPart(); - auto* delete_step = new DeleteObjectsUndoStep(map); - for (std::size_t i = 0; i < created_objects.size(); ++i) - { - Object* object = created_objects[i]; - delete_step->addObject(part->findObjectIndex(object)); - map->addObjectToSelection(object, i == created_objects.size() - 1); - } - map->push(delete_step); - map->setObjectsDirty(); + DistributePointsDialog dialog(window, map, point); + dialog.setWindowModality(Qt::WindowModal); + dialog.exec(); } void MapEditorController::addFloatingDockWidget(QDockWidget* dock_widget) diff --git a/src/tools/distribute_points_tool.cpp b/src/tools/distribute_points_tool.cpp index f3e9b660cc..dd8816c2ae 100644 --- a/src/tools/distribute_points_tool.cpp +++ b/src/tools/distribute_points_tool.cpp @@ -1,6 +1,7 @@ /* * Copyright 2013 Thomas Schöps - * Copyright 2014, 2015 Kai Pastor + * Copyright 2014-2015, 2017-2019 Kai Pastor + * Copyright 2024 Matthias Kühlewein * * This file is part of OpenOrienteering. * @@ -21,6 +22,8 @@ #include "distribute_points_tool.h" +#include + #include #include #include @@ -32,35 +35,109 @@ #include #include +#include "core/map.h" #include "core/map_coord.h" #include "core/path_coord.h" #include "core/symbols/point_symbol.h" #include "core/objects/object.h" #include "gui/util_gui.h" +#include "undo/object_undo.h" namespace OpenOrienteering { -bool DistributePointsTool::showSettingsDialog( - QWidget* parent, - const PointSymbol* point, - DistributePointsTool::Settings& settings ) +DistributePointsDialog::DistributePointsDialog(QWidget* parent, Map* map, const PointSymbol* point) +: QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) +, map { map } +, point { point } +{ + setWindowTitle(tr("Distribute points evenly along path")); + + auto layout = new QFormLayout(); + + num_points_edit = Util::SpinBox::create(1, 9999); + num_points_edit->setValue(3); + layout->addRow(tr("Number of points per path:"), num_points_edit); + + points_at_ends_check = new QCheckBox(tr("Also place objects at line end points")); + points_at_ends_check->setChecked(true); + layout->addRow(points_at_ends_check); + + layout->addItem(Util::SpacerItem::create(this)); + + auto rotation_headline = Util::Headline::create(tr("Rotation settings")); + layout->addRow(rotation_headline); + + rotate_symbols_check = new QCheckBox(tr("Align points with direction of line")); + rotate_symbols_check->setChecked(true); + layout->addRow(rotate_symbols_check); + + additional_rotation_edit = Util::SpinBox::create(); + additional_rotation_edit->setDecimals(1); + additional_rotation_edit->setSingleStep(5.0); + additional_rotation_edit->setValue(qRadiansToDegrees(0.0)); + layout->addRow(tr("Additional rotation angle (counter-clockwise):"), additional_rotation_edit); + + if (!point->isRotatable()) + { + rotation_headline->setEnabled(false); + rotate_symbols_check->setEnabled(false); + additional_rotation_edit->setEnabled(false); + layout->labelForField(additional_rotation_edit)->setEnabled(false); + } + + layout->addItem(Util::SpacerItem::create(this)); + auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + layout->addRow(button_box); + + setLayout(layout); + + connect(button_box, &QDialogButtonBox::accepted, this, &DistributePointsDialog::okClicked); + connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +DistributePointsDialog::~DistributePointsDialog() = default; + +// slot +void DistributePointsDialog::okClicked() { - DistributePointsSettingsDialog dialog(parent, point, settings); - dialog.setWindowModality(Qt::WindowModal); - if (dialog.exec() == QDialog::Rejected) - return false; + // Create points along paths + created_objects.reserve(map->selectedObjects().size() * num_points_edit->value()); + for (const auto* object : map->selectedObjects()) + { + if (object->getType() == Object::Path) + distributePoints(object->asPath()); + } + if (created_objects.empty()) + return; + + // Add points to map + for (auto* o : created_objects) + map->addObject(o); + + // Create undo step and select new objects + map->clearObjectSelection(false); + MapPart* part = map->getCurrentPart(); + auto* delete_step = new DeleteObjectsUndoStep(map); + for (std::size_t i = 0; i < created_objects.size(); ++i) + { + Object* object = created_objects[i]; + delete_step->addObject(part->findObjectIndex(object)); + map->addObjectToSelection(object, i == created_objects.size() - 1); + } + map->push(delete_step); + map->setObjectsDirty(); - dialog.getValues(settings); - return true; + accept(); } -void DistributePointsTool::execute( - const PathObject* path, - PointSymbol* point, - const DistributePointsTool::Settings& settings, - std::vector& out_objects ) +void DistributePointsDialog::distributePoints(const PathObject* path) { + const auto num_points_per_line = num_points_edit->value(); + const auto points_at_ends = points_at_ends_check->isChecked(); + const auto rotate_symbols = rotate_symbols_check->isChecked(); + const auto additional_rotation = qDegreesToRadians(additional_rotation_edit->value()); + path->update(); // This places the points only on the first part. @@ -70,17 +147,17 @@ void DistributePointsTool::execute( int total, start, end; if (part.isClosed()) { - total = settings.num_points_per_line; + total = num_points_per_line; start = 0; end = total - 1; } - else if (!settings.points_at_ends) + else if (!points_at_ends) { - total = settings.num_points_per_line + 1; + total = num_points_per_line + 1; start = 1; end = total - 1; } - else if (settings.num_points_per_line == 1) + else if (num_points_per_line == 1) { total = 1; start = 1; @@ -88,7 +165,7 @@ void DistributePointsTool::execute( } else { - total = settings.num_points_per_line - 1; + total = num_points_per_line - 1; start = 0; end = total; } @@ -106,77 +183,17 @@ void DistributePointsTool::execute( object->setPosition(split.pos); if (point->isRotatable()) { - double rotation = settings.additional_rotation; - if (settings.rotate_symbols) + double rotation = additional_rotation; + if (rotate_symbols) { auto right = split.tangentVector().perpRight(); rotation -= right.angle(); } object->setRotation(rotation); } - out_objects.push_back(object); + created_objects.push_back(object); } } -DistributePointsSettingsDialog::DistributePointsSettingsDialog( - QWidget* parent, - const PointSymbol* point, - const DistributePointsTool::Settings& settings ) - : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint) -{ - setWindowTitle(tr("Distribute points evenly along path")); - - auto layout = new QFormLayout(); - - num_points_edit = Util::SpinBox::create(1, 9999); - num_points_edit->setValue(settings.num_points_per_line); - layout->addRow(tr("Number of points per path:"), num_points_edit); - - points_at_ends_check = new QCheckBox(tr("Also place objects at line end points")); - points_at_ends_check->setChecked(settings.points_at_ends); - layout->addRow(points_at_ends_check); - - layout->addItem(Util::SpacerItem::create(this)); - - auto rotation_headline = Util::Headline::create(tr("Rotation settings")); - layout->addRow(rotation_headline); - - rotate_symbols_check = new QCheckBox(tr("Align points with direction of line")); - rotate_symbols_check->setChecked(settings.rotate_symbols); - layout->addRow(rotate_symbols_check); - - additional_rotation_edit = Util::SpinBox::create(); - additional_rotation_edit->setDecimals(1); - additional_rotation_edit->setSingleStep(5.0); - additional_rotation_edit->setValue(qRadiansToDegrees(settings.additional_rotation)); - layout->addRow(tr("Additional rotation angle (counter-clockwise):"), additional_rotation_edit); - - if (!point->isRotatable()) - { - rotation_headline->setEnabled(false); - rotate_symbols_check->setEnabled(false); - additional_rotation_edit->setEnabled(false); - layout->labelForField(additional_rotation_edit)->setEnabled(false); - } - - layout->addItem(Util::SpacerItem::create(this)); - auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); - layout->addRow(button_box); - - setLayout(layout); - - connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); -} - -void DistributePointsSettingsDialog::getValues(DistributePointsTool::Settings& settings) -{ - settings.num_points_per_line = num_points_edit->value(); - settings.points_at_ends = points_at_ends_check->isChecked(); - settings.rotate_symbols = rotate_symbols_check->isChecked(); - settings.additional_rotation = qDegreesToRadians(additional_rotation_edit->value()); -} - - } // namespace OpenOrienteering diff --git a/src/tools/distribute_points_tool.h b/src/tools/distribute_points_tool.h index 2a8b6dc1e6..0462f91d80 100644 --- a/src/tools/distribute_points_tool.h +++ b/src/tools/distribute_points_tool.h @@ -1,6 +1,7 @@ /* * Copyright 2013 Thomas Schöps - * Copyright 2015 Kai Pastor + * Copyright 2015, 2017-2019 Kai Pastor + * Copyright 2024 Matthias Kühlewein * * This file is part of OpenOrienteering. * @@ -19,15 +20,13 @@ */ -#ifndef OPENORIENTEERING_TOOL_DISTRIBUTE_POINTS_H -#define OPENORIENTEERING_TOOL_DISTRIBUTE_POINTS_H - -#include +#ifndef OPENORIENTEERING_DISTRIBUTE_POINTS_TOOL_H +#define OPENORIENTEERING_DISTRIBUTE_POINTS_TOOL_H #include #include -#include +// IWYU pragma: no_include class QDoubleSpinBox; class QCheckBox; class QSpinBox; @@ -35,103 +34,41 @@ class QWidget; namespace OpenOrienteering { +class Map; class PathObject; class PointObject; class PointSymbol; - -/** - * Provides methods to create evenly spaced point objects along a line. - * - * \todo Integrate implementation of MapEditorController::distributePointsClicked(). - */ -class DistributePointsTool +class DistributePointsDialog : public QDialog { +Q_OBJECT public: - /** Required user input for applying the action. */ - struct Settings - { - /** Number of points to create */ - int num_points_per_line; - - /** If true, points will also be placed at open paths ends, - * otherwise only inside for open paths*/ - bool points_at_ends; - - /** If true, points will be aligned with the path direction if rotatable */ - bool rotate_symbols; - - /** Additional rotation for rotatable point symbols, - * in radians counter-clockwise */ - double additional_rotation; - - /** Constructor, sets default values. */ - constexpr Settings() - : num_points_per_line{ 3 } - , points_at_ends{ true } - , rotate_symbols{ true } - , additional_rotation{ 0.0 } - { - // Nothing else - } - }; - /** - * Shows a settings dialog for the tool. - * - * If the user presses Ok, returns true and saves the chosen values to - * settings, otherwise returns false. - * - * The settings parameter is also used as initial values for the dialog. - * The point symbol is used to determine the enabled state of some options. + * Creates a new DistributePointsDialog object. */ - static bool showSettingsDialog( - QWidget* parent, - const PointSymbol* point, - DistributePointsTool::Settings& settings - ); + DistributePointsDialog(QWidget* parent, Map* map, const PointSymbol* point); - /** - * Executes the tool on the path, creating points according to settings. - * - * Appends the created objects to the out_objects vector, but does not add - * them to the map. - */ - static void execute( - const PathObject* path, - PointSymbol* point, - const DistributePointsTool::Settings& settings, - std::vector& out_objects - ); -}; - - - -/** - * Settings dialog for DistributePointsTool - */ -class DistributePointsSettingsDialog : public QDialog -{ -Q_OBJECT -public: - /** Creates a new DistributePointsSettingsDialog. */ - DistributePointsSettingsDialog( - QWidget* parent, - const PointSymbol* point, - const DistributePointsTool::Settings& settings - ); + ~DistributePointsDialog() override; - /** After the dialog finished successfully, returns the entered values. */ - void getValues(DistributePointsTool::Settings& settings); +private slots: + void okClicked(); private: + void distributePoints(const PathObject* path); + QSpinBox* num_points_edit; QCheckBox* points_at_ends_check; QCheckBox* rotate_symbols_check; QDoubleSpinBox* additional_rotation_edit; + + std::vector created_objects; + + Map* map; + const PointSymbol* point; }; } // namespace OpenOrienteering -#endif + +#endif // OPENORIENTEERING_DISTRIBUTE_POINTS_TOOL_H