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