1 /*
2 * Copyright 2013 Thomas Schöps
3 * Copyright 2014, 2015 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 #include "distribute_points_tool.h"
23
24 #include <Qt>
25 #include <QtMath>
26 #include <QCheckBox>
27 #include <QDialogButtonBox>
28 #include <QDoubleSpinBox>
29 #include <QFormLayout>
30 #include <QLabel>
31 #include <QSpacerItem>
32 #include <QSpinBox>
33 #include <QWidget>
34
35 #include "core/map_coord.h"
36 #include "core/path_coord.h"
37 #include "core/symbols/point_symbol.h"
38 #include "core/objects/object.h"
39 #include "gui/util_gui.h"
40
41
42 namespace OpenOrienteering {
43
showSettingsDialog(QWidget * parent,const PointSymbol * point,DistributePointsTool::Settings & settings)44 bool DistributePointsTool::showSettingsDialog(
45 QWidget* parent,
46 const PointSymbol* point,
47 DistributePointsTool::Settings& settings )
48 {
49 DistributePointsSettingsDialog dialog(parent, point, settings);
50 dialog.setWindowModality(Qt::WindowModal);
51 if (dialog.exec() == QDialog::Rejected)
52 return false;
53
54 dialog.getValues(settings);
55 return true;
56 }
57
execute(const PathObject * path,PointSymbol * point,const DistributePointsTool::Settings & settings,std::vector<PointObject * > & out_objects)58 void DistributePointsTool::execute(
59 const PathObject* path,
60 PointSymbol* point,
61 const DistributePointsTool::Settings& settings,
62 std::vector<PointObject*>& out_objects )
63 {
64 path->update();
65
66 // This places the points only on the first part.
67 const auto& part = path->parts().front();
68
69 // Check how to distribute the points over the part length
70 int total, start, end;
71 if (part.isClosed())
72 {
73 total = settings.num_points_per_line;
74 start = 0;
75 end = total - 1;
76 }
77 else if (!settings.points_at_ends)
78 {
79 total = settings.num_points_per_line + 1;
80 start = 1;
81 end = total - 1;
82 }
83 else if (settings.num_points_per_line == 1)
84 {
85 total = 1;
86 start = 1;
87 end = 1;
88 }
89 else
90 {
91 total = settings.num_points_per_line - 1;
92 start = 0;
93 end = total;
94 }
95
96 auto distance = part.length() / total;
97 auto split = SplitPathCoord::begin(part.path_coords);
98
99 // Create the objects
100 for (int i = start; i <= end; ++i)
101 {
102 auto clen = distance * i;
103 split = SplitPathCoord::at(clen, split);
104
105 auto object = new PointObject(point);
106 object->setPosition(split.pos);
107 if (point->isRotatable())
108 {
109 double rotation = settings.additional_rotation;
110 if (settings.rotate_symbols)
111 {
112 auto right = split.tangentVector().perpRight();
113 rotation -= right.angle();
114 }
115 object->setRotation(rotation);
116 }
117 out_objects.push_back(object);
118 }
119 }
120
121
DistributePointsSettingsDialog(QWidget * parent,const PointSymbol * point,const DistributePointsTool::Settings & settings)122 DistributePointsSettingsDialog::DistributePointsSettingsDialog(
123 QWidget* parent,
124 const PointSymbol* point,
125 const DistributePointsTool::Settings& settings )
126 : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
127 {
128 setWindowTitle(tr("Distribute points evenly along path"));
129
130 auto layout = new QFormLayout();
131
132 num_points_edit = Util::SpinBox::create(1, 9999);
133 num_points_edit->setValue(settings.num_points_per_line);
134 layout->addRow(tr("Number of points per path:"), num_points_edit);
135
136 points_at_ends_check = new QCheckBox(tr("Also place objects at line end points"));
137 points_at_ends_check->setChecked(settings.points_at_ends);
138 layout->addRow(points_at_ends_check);
139
140 layout->addItem(Util::SpacerItem::create(this));
141
142 auto rotation_headline = Util::Headline::create(tr("Rotation settings"));
143 layout->addRow(rotation_headline);
144
145 rotate_symbols_check = new QCheckBox(tr("Align points with direction of line"));
146 rotate_symbols_check->setChecked(settings.rotate_symbols);
147 layout->addRow(rotate_symbols_check);
148
149 additional_rotation_edit = Util::SpinBox::create<Util::RotationalDegrees>();
150 additional_rotation_edit->setDecimals(1);
151 additional_rotation_edit->setSingleStep(5.0);
152 additional_rotation_edit->setValue(qRadiansToDegrees(settings.additional_rotation));
153 layout->addRow(tr("Additional rotation angle (counter-clockwise):"), additional_rotation_edit);
154
155 if (!point->isRotatable())
156 {
157 rotation_headline->setEnabled(false);
158 rotate_symbols_check->setEnabled(false);
159 additional_rotation_edit->setEnabled(false);
160 layout->labelForField(additional_rotation_edit)->setEnabled(false);
161 }
162
163 layout->addItem(Util::SpacerItem::create(this));
164 auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
165 layout->addRow(button_box);
166
167 setLayout(layout);
168
169 connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
170 connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
171 }
172
getValues(DistributePointsTool::Settings & settings)173 void DistributePointsSettingsDialog::getValues(DistributePointsTool::Settings& settings)
174 {
175 settings.num_points_per_line = num_points_edit->value();
176 settings.points_at_ends = points_at_ends_check->isChecked();
177 settings.rotate_symbols = rotate_symbols_check->isChecked();
178 settings.additional_rotation = qDegreesToRadians(additional_rotation_edit->value());
179 }
180
181
182 } // namespace OpenOrienteering
183