src/editmodes/abstractEditMode.cpp

changeset 1087
80e25f6b0bb0
parent 1077
952d6b3e7d11
child 1088
c6d242d2b619
equal deleted inserted replaced
1086:621c2e5853bb 1087:80e25f6b0bb0
32 #include "../grid.h" 32 #include "../grid.h"
33 33
34 ConfigOption (bool DrawLineLengths = true) 34 ConfigOption (bool DrawLineLengths = true)
35 ConfigOption (bool DrawAngles = false) 35 ConfigOption (bool DrawAngles = false)
36 36
37 /*
38 * Base class constructor of the abstract editing mode.
39 */
37 AbstractEditMode::AbstractEditMode(GLRenderer* renderer) : 40 AbstractEditMode::AbstractEditMode(GLRenderer* renderer) :
38 QObject(renderer), 41 QObject(renderer),
39 HierarchyElement(renderer), 42 HierarchyElement(renderer),
40 m_renderer(renderer) {} 43 m_renderer(renderer) {}
41 44
42 AbstractEditMode::~AbstractEditMode() {} 45 /*
43 46 * Constructs an edit mode by type.
47 */
44 AbstractEditMode* AbstractEditMode::createByType(GLRenderer* renderer, EditModeType type) 48 AbstractEditMode* AbstractEditMode::createByType(GLRenderer* renderer, EditModeType type)
45 { 49 {
46 switch (type) 50 switch (type)
47 { 51 {
48 case EditModeType::Select: return new SelectMode (renderer); 52 case EditModeType::Select: return new SelectMode (renderer);
55 } 59 }
56 60
57 throw std::logic_error("bad type given to AbstractEditMode::createByType"); 61 throw std::logic_error("bad type given to AbstractEditMode::createByType");
58 } 62 }
59 63
64 /*
65 * Returns the edit mode's corresponding renderer pointer.
66 */
60 GLRenderer* AbstractEditMode::renderer() const 67 GLRenderer* AbstractEditMode::renderer() const
61 { 68 {
62 return m_renderer; 69 return m_renderer;
63 } 70 }
64 71
72 /*
73 * Base class constructor of the abstract drwaing mode.
74 */
65 AbstractDrawMode::AbstractDrawMode(GLRenderer* renderer) : 75 AbstractDrawMode::AbstractDrawMode(GLRenderer* renderer) :
66 AbstractEditMode(renderer), 76 AbstractEditMode {renderer},
67 m_polybrush{QBrush{QColor{64, 192, 0, 128}}} 77 m_polybrush {QBrush {QColor {64, 192, 0, 128}}}
68 { 78 {
69 renderer->setContextMenuPolicy (Qt::NoContextMenu); // We need the right mouse button for removing vertices 79 renderer->setContextMenuPolicy(Qt::NoContextMenu); // We need the right mouse button for removing vertices
70 renderer->setCursor (Qt::CrossCursor); 80 renderer->setCursor(Qt::CrossCursor);
71 m_window->currentDocument()->clearSelection(); 81 m_window->currentDocument()->clearSelection();
72 m_window->updateSelection(); 82 m_window->updateSelection();
73 m_drawedVerts.clear(); 83 m_drawedVerts.clear();
74 } 84 }
75 85
76 AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) : 86 /*
77 AbstractEditMode (renderer) 87 * Base class constructor of the abstract selection mode.
88 */
89 AbstractSelectMode::AbstractSelectMode(GLRenderer* renderer) :
90 AbstractEditMode {renderer}
78 { 91 {
79 renderer->unsetCursor(); 92 renderer->unsetCursor();
80 renderer->setContextMenuPolicy (Qt::DefaultContextMenu); 93 renderer->setContextMenuPolicy (Qt::DefaultContextMenu);
81 } 94 }
82 95
83 // ============================================================================= 96 /*
84 // 97 * Possibly adds this vertex into the list of drawn vertices.
98 */
85 void AbstractDrawMode::addDrawnVertex(const Vertex& position) 99 void AbstractDrawMode::addDrawnVertex(const Vertex& position)
86 { 100 {
87 if (preAddVertex(position)) 101 if (preAddVertex(position))
88 return; 102 return;
89 103
90 m_drawedVerts << position; 104 m_drawedVerts << position;
91 } 105 }
92 106
107 /*
108 * Handles mouse relese events.
109 */
93 bool AbstractDrawMode::mouseReleased(MouseEventData const& data) 110 bool AbstractDrawMode::mouseReleased(MouseEventData const& data)
94 { 111 {
95 if (Super::mouseReleased(data)) 112 if (Super::mouseReleased(data))
96 return true; 113 return true;
97 114
115 // If the user presses the middle mouse button, seek the closest existing vertex to the cursor and clamp to that.
98 if ((data.releasedButtons & Qt::MidButton) and (countof(m_drawedVerts) < 4) and (not data.mouseMoved)) 116 if ((data.releasedButtons & Qt::MidButton) and (countof(m_drawedVerts) < 4) and (not data.mouseMoved))
99 { 117 {
100 // Find the closest vertex to our cursor 118 // Find the closest vertex to our cursor
101 double minimumDistance = 1024.0; 119 double minimumDistance = 1024.0;
102 const Vertex* closest = nullptr; 120 const Vertex* closest = nullptr;
103 Vertex cursorPosition = renderer()->convert2dTo3d (data.ev->pos(), false); 121 Vertex cursorPosition = renderer()->convert2dTo3d(data.ev->pos(), false);
104 QPoint cursorPosition2D (data.ev->pos()); 122 QPoint cursorPosition2D = data.ev->pos();
105 const Axis depthAxis = renderer()->getRelativeZ(); 123 const Axis depthAxis = renderer()->getRelativeZ();
106 QList<Vertex> vertices = renderer()->document()->inlineVertices().toList(); 124 QList<Vertex> vertices = renderer()->document()->inlineVertices().toList();
107 125
108 // Sort the vertices in order of distance to camera 126 // Sort the vertices in order of distance to camera
109 std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool 127 std::sort(vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool
110 { 128 {
111 if (renderer()->cameraInfo (renderer()->camera()).negatedDepth) 129 if (renderer()->cameraInfo(renderer()->camera()).negatedDepth)
112 return a[depthAxis] > b[depthAxis]; 130 return a[depthAxis] > b[depthAxis];
113 else 131 else
114 return a[depthAxis] < b[depthAxis]; 132 return a[depthAxis] < b[depthAxis];
115 }); 133 });
116 134
117 for (const Vertex& vrt : vertices) 135 for (const Vertex& vertex : vertices)
118 { 136 {
119 // If the vertex in 2d space is very close to the cursor then we use it regardless of depth. 137 // If the vertex in 2d space is very close to the cursor then we use it regardless of depth.
120 QPoint vect2d = renderer()->convert3dTo2d (vrt) - cursorPosition2D; 138 QPoint vect2d = renderer()->convert3dTo2d(vertex) - cursorPosition2D;
121 const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); 139 double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2);
140
122 if (distance2DSquared < 16.0 * 16.0) 141 if (distance2DSquared < 16.0 * 16.0)
123 { 142 {
124 closest = &vrt; 143 closest = &vertex;
125 break; 144 break;
126 } 145 }
127 146
128 // Check if too far away from the cursor. 147 // Check if too far away from the cursor.
129 if (distance2DSquared > 64.0 * 64.0) 148 if (distance2DSquared > 64.0 * 64.0)
130 continue; 149 continue;
131 150
132 // Not very close to the cursor. Compare using true distance, 151 // Not very close to the cursor. Compare using true distance,
133 // including depth. 152 // including depth.
134 const double distanceSquared = (vrt - cursorPosition).lengthSquared(); 153 double distanceSquared = (vertex - cursorPosition).lengthSquared();
135 154
136 if (distanceSquared < minimumDistance) 155 if (distanceSquared < minimumDistance)
137 { 156 {
138 minimumDistance = distanceSquared; 157 minimumDistance = distanceSquared;
139 closest = &vrt; 158 closest = &vertex;
140 } 159 }
141 } 160 }
142 161
143 if (closest) 162 if (closest)
144 addDrawnVertex (*closest); 163 addDrawnVertex(*closest);
145 164
146 return true; 165 return true;
147 } 166 }
148 167
168 // If the user presses the right mouse button, remove the previously drawn vertex.
149 if ((data.releasedButtons & Qt::RightButton) and not m_drawedVerts.isEmpty()) 169 if ((data.releasedButtons & Qt::RightButton) and not m_drawedVerts.isEmpty())
150 { 170 {
151 // Remove the last vertex
152 m_drawedVerts.removeLast(); 171 m_drawedVerts.removeLast();
153 return true; 172 return true;
154 } 173 }
155 174
175 // If the user presses the left mouse button, insert the vertex or stop drawing, whichever is appropriate.
156 if (data.releasedButtons & Qt::LeftButton) 176 if (data.releasedButtons & Qt::LeftButton)
157 { 177 {
158 if (maxVertices() and countof(m_drawedVerts) >= maxVertices()) 178 if (maxVertices() and countof(m_drawedVerts) >= maxVertices())
159 endDraw(); 179 endDraw();
160 else 180 else
161 addDrawnVertex (getCursorVertex()); 181 addDrawnVertex (getCursorVertex());
162 return true; 182
163 } 183 return true;
164 184 }
185
186 // Otherwise we did not handle this mouse event.
165 return false; 187 return false;
166 } 188 }
167 189
190 /*
191 * Finalises the draw operation. The provided model is merged into the main document.
192 */
168 void AbstractDrawMode::finishDraw(Model& model) 193 void AbstractDrawMode::finishDraw(Model& model)
169 { 194 {
170 int pos = m_window->suggestInsertPoint(); 195 int position = m_window->suggestInsertPoint();
171 196
172 if (countof(model.objects()) > 0) 197 if (countof(model) > 0)
173 { 198 {
174 for (LDObject* obj : model) 199 renderer()->document()->merge(model, position);
175 renderer()->document()->insertObject (pos++, obj);
176
177 m_window->refresh(); 200 m_window->refresh();
178 m_window->endAction(); 201 m_window->endAction();
179 } 202 }
180 203
181 m_drawedVerts.clear(); 204 m_drawedVerts.clear();
182 } 205 }
183 206
184 void AbstractDrawMode::drawLineLength(QPainter &painter, const Vertex &v0, const Vertex &v1, 207 /*
185 const QPointF& v0p, const QPointF& v1p) const 208 * Renders the length of the provided line.
209 * - v0 and v1 are the line vertices in 3D space.
210 * - v0p and v1p are the line vertices in 2D space (so that this function does not have to calculate them separately)
211 */
212 void AbstractDrawMode::drawLineLength(QPainter &painter, const Vertex &v0, const Vertex &v1, const QPointF& v0p, const QPointF& v1p) const
186 { 213 {
187 if (not m_config->drawLineLengths()) 214 if (not m_config->drawLineLengths())
188 return; 215 return;
189 216
190 const QString label = QString::number(abs(v1 - v0), 'f', 2); 217 const QString label = QString::number(abs(v1 - v0), 'f', 2);
191 QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint(); 218 QPoint origin = QLineF {v0p, v1p}.pointAt(0.5).toPoint();
192 painter.drawText (origin, label); 219 painter.drawText (origin, label);
193 } 220 }
194 221
195 void AbstractDrawMode::renderPolygon(QPainter& painter, const QVector<Vertex>& polygon3d, 222 /*
196 bool drawLineLengths, bool drawAngles ) const 223 * Renders a polygon preview.
197 { 224 *
198 QVector<QPoint> polygon2d (countof(polygon3d)); 225 * painter: QPainter instance that is currently being rendered to.
199 QFontMetrics metrics = QFontMetrics(QFont()); 226 * polygon3d: The polygon as a vector of 3D vertices.
227 * drawLineLengths: if true, lengths of polygon sides are also previewed, assuming the user has enabled the relevant option.
228 * drawAngles: if true, the angles between polygon sides are also previewed, assuming the user has enabled the relevant option.
229 */
230 void AbstractDrawMode::renderPolygon(QPainter& painter, const QVector<Vertex>& polygon3d, bool drawLineLengths, bool drawAngles) const
231 {
232 QVector<QPoint> polygon2d {countof(polygon3d)};
233 QFontMetrics metrics {QFont {}};
200 234
201 // Convert to 2D 235 // Convert to 2D
202 for (int i = 0; i < countof(polygon3d); ++i) 236 for (int i = 0; i < countof(polygon3d); ++i)
203 polygon2d[i] = renderer()->convert3dTo2d(polygon3d[i]); 237 polygon2d[i] = renderer()->convert3dTo2d(polygon3d[i]);
204 238
214 } 248 }
215 249
216 // Draw line lenghts and angle info if appropriate 250 // Draw line lenghts and angle info if appropriate
217 if (countof(polygon3d) >= 2 and (drawLineLengths or drawAngles)) 251 if (countof(polygon3d) >= 2 and (drawLineLengths or drawAngles))
218 { 252 {
219 painter.setPen (renderer()->textPen()); 253 painter.setPen(renderer()->textPen());
220 254
221 for (int i = 0; i < countof(polygon3d); ++i) 255 for (int i = 0; i < countof(polygon3d); ++i)
222 { 256 {
223 int j = (i + 1) % countof(polygon3d); 257 int j = (i + 1) % countof(polygon3d);
224 int prior = (i - 1 >= 0) ? (i - 1) : (countof(polygon3d) - 1); 258 int prior = (i - 1 >= 0) ? (i - 1) : (countof(polygon3d) - 1);
242 } 276 }
243 } 277 }
244 } 278 }
245 } 279 }
246 280
247 bool AbstractDrawMode::keyReleased (QKeyEvent *ev) 281 /*
248 { 282 * Key release event handler
249 if (Super::keyReleased (ev)) 283 */
250 return true; 284 bool AbstractDrawMode::keyReleased(QKeyEvent *event)
251 285 {
252 if (not m_drawedVerts.isEmpty() and ev->key() == Qt::Key_Backspace) 286 if (Super::keyReleased(event))
287 return true;
288
289 // Map backspace to removing the previously drawn vertex.
290 if (not m_drawedVerts.isEmpty() and event->key() == Qt::Key_Backspace)
253 { 291 {
254 m_drawedVerts.removeLast(); 292 m_drawedVerts.removeLast();
255 return true; 293 return true;
256 } 294 }
257 295
258 return false; 296 return false;
259 } 297 }
260 298
261 // 299 /*
262 // roundToInterval 300 * Rounds the input value to the nearest multiple of the provided interval.
263 // 301 */
264 // Rounds 'a' to the nearest multiple of 'interval'.
265 //
266 template<typename T> 302 template<typename T>
267 T roundToInterval (T a, T interval) 303 T roundToInterval(T value, T interval)
268 { 304 {
269 T remainder = a % interval; 305 T remainder = value % interval;
270 306
271 if (remainder >= interval / 2.0) 307 if (remainder >= interval / 2.0)
272 a += interval; 308 value += interval;
273 309
274 a -= remainder; 310 value -= remainder;
275 return a; 311 return value;
276 } 312 }
277 313
314 /*
315 * Computes the position for the vertex currently being drawn.
316 */
278 Vertex AbstractDrawMode::getCursorVertex() const 317 Vertex AbstractDrawMode::getCursorVertex() const
279 { 318 {
280 Vertex result = renderer()->position3D(); 319 Vertex result = renderer()->position3D();
281 320
321 // If the Ctrl key is pressed, then the vertex is locked to 45 degree angles relative to the previously drawn vertex.
282 if ((renderer()->keyboardModifiers() & Qt::ControlModifier) and not m_drawedVerts.isEmpty()) 322 if ((renderer()->keyboardModifiers() & Qt::ControlModifier) and not m_drawedVerts.isEmpty())
283 { 323 {
284 Vertex const& vertex0 = m_drawedVerts.last(); 324 const Vertex& vertex0 = m_drawedVerts.last();
285 Vertex const& vertex1 = result; 325 const Vertex& vertex1 = result;
286 Axis relativeX, relativeY; 326 Axis relativeX;
287 327 Axis relativeY;
288 renderer()->getRelativeAxes (relativeX, relativeY); 328 renderer()->getRelativeAxes(relativeX, relativeY);
289 QLineF line = {vertex0[relativeX], vertex0[relativeY], vertex1[relativeX], vertex1[relativeY]}; 329 QLineF line = {vertex0[relativeX], vertex0[relativeY], vertex1[relativeX], vertex1[relativeY]};
290 line.setAngle(roundToInterval<int>(line.angle(), 45)); 330 line.setAngle(roundToInterval<int>(line.angle(), 45));
291 result.setCoordinate(relativeX, grid()->snap(line.x2(), Grid::Coordinate)); 331 result.setCoordinate(relativeX, grid()->snap(line.x2(), Grid::Coordinate));
292 result.setCoordinate(relativeY, grid()->snap(line.y2(), Grid::Coordinate)); 332 result.setCoordinate(relativeY, grid()->snap(line.y2(), Grid::Coordinate));
293 } 333 }
294 334
295 return result; 335 return result;
296 } 336 }
337
338 /*
339 * No draw mode can operate on the free camera, since 3D ⟷ 2D point conversions are not possible with it.
340 */
341 bool AbstractDrawMode::allowFreeCamera() const
342 {
343 return false;
344 }
345
346 /*
347 * This virtual method allows drawing modes to specify how many vertices at most can be drawn. Returning 0 means unlimited.
348 */
349 int AbstractDrawMode::maxVertices() const
350 {
351 return 0;
352 }
353
354 /*
355 * This virtual method is a hook that allows drawing modes to veto vertex insertion. Returning true means that the vertex insertion
356 * was handled separately and the vertex will not be added.
357 */
358 bool AbstractDrawMode::preAddVertex (Vertex const&)
359 {
360 return false;
361 }
362
363 /*
364 * This virtual method is overridden by subclasses to implement the actions taken by the edit mode.
365 * The editing mode is to call finishDraw with the prepared model.
366 *
367 * TODO: the two method names are too similar and should be renamed.
368 */
369 void AbstractDrawMode::endDraw() {}

mercurial