Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor distribute points tool #2305

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 5 additions & 32 deletions src/gui/map/map_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<PaintOnTemplateFeature>(*this);

Expand Down Expand Up @@ -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<PointObject*> 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)
Expand Down
185 changes: 101 additions & 84 deletions src/tools/distribute_points_tool.cpp
Original file line number Diff line number Diff line change
@@ -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.
*
Expand All @@ -21,6 +22,8 @@

#include "distribute_points_tool.h"

#include <vector>

#include <Qt>
#include <QtMath>
#include <QCheckBox>
Expand All @@ -32,35 +35,109 @@
#include <QSpinBox>
#include <QWidget>

#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<Util::RotationalDegrees>();
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<PointObject*>& 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.
Expand All @@ -70,25 +147,25 @@ 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;
end = 1;
}
else
{
total = settings.num_points_per_line - 1;
total = num_points_per_line - 1;
start = 0;
end = total;
}
Expand All @@ -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<Util::RotationalDegrees>();
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
Loading