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 AbstractEditMode::AbstractEditMode (GLRenderer* renderer) : |
37 AbstractEditMode::AbstractEditMode(GLRenderer* renderer) : |
38 QObject (renderer), |
38 QObject(renderer), |
39 HierarchyElement (renderer), |
39 HierarchyElement(renderer), |
40 m_renderer (renderer) {} |
40 m_renderer(renderer) {} |
41 |
41 |
42 AbstractEditMode::~AbstractEditMode() {} |
42 AbstractEditMode::~AbstractEditMode() {} |
43 |
43 |
44 AbstractEditMode* AbstractEditMode::createByType (GLRenderer* renderer, EditModeType type) |
44 AbstractEditMode* AbstractEditMode::createByType(GLRenderer* renderer, EditModeType type) |
45 { |
45 { |
46 switch (type) |
46 switch (type) |
47 { |
47 { |
48 case EditModeType::Select: return new SelectMode (renderer); |
48 case EditModeType::Select: return new SelectMode (renderer); |
49 case EditModeType::Draw: return new DrawMode (renderer); |
49 case EditModeType::Draw: return new DrawMode (renderer); |
52 case EditModeType::MagicWand: return new MagicWandMode (renderer); |
52 case EditModeType::MagicWand: return new MagicWandMode (renderer); |
53 case EditModeType::LinePath: return new LinePathMode (renderer); |
53 case EditModeType::LinePath: return new LinePathMode (renderer); |
54 case EditModeType::Curve: return new CurveMode (renderer); |
54 case EditModeType::Curve: return new CurveMode (renderer); |
55 } |
55 } |
56 |
56 |
57 throw std::logic_error ("bad type given to AbstractEditMode::createByType"); |
57 throw std::logic_error("bad type given to AbstractEditMode::createByType"); |
58 } |
58 } |
59 |
59 |
60 GLRenderer* AbstractEditMode::renderer() const |
60 GLRenderer* AbstractEditMode::renderer() const |
61 { |
61 { |
62 return m_renderer; |
62 return m_renderer; |
63 } |
63 } |
64 |
64 |
65 AbstractDrawMode::AbstractDrawMode (GLRenderer* renderer) : |
65 AbstractDrawMode::AbstractDrawMode(GLRenderer* renderer) : |
66 AbstractEditMode (renderer), |
66 AbstractEditMode(renderer), |
67 m_polybrush (QBrush (QColor (64, 192, 0, 128))) |
67 m_polybrush{QBrush{QColor{64, 192, 0, 128}}} |
68 { |
68 { |
69 renderer->setContextMenuPolicy (Qt::NoContextMenu); // We need the right mouse button for removing vertices |
69 renderer->setContextMenuPolicy (Qt::NoContextMenu); // We need the right mouse button for removing vertices |
70 renderer->setCursor (Qt::CrossCursor); |
70 renderer->setCursor (Qt::CrossCursor); |
71 m_window->currentDocument()->clearSelection(); |
71 m_window->currentDocument()->clearSelection(); |
72 m_window->updateSelection(); |
72 m_window->updateSelection(); |
80 renderer->setContextMenuPolicy (Qt::DefaultContextMenu); |
80 renderer->setContextMenuPolicy (Qt::DefaultContextMenu); |
81 } |
81 } |
82 |
82 |
83 // ============================================================================= |
83 // ============================================================================= |
84 // |
84 // |
85 void AbstractDrawMode::addDrawnVertex (Vertex const& pos) |
85 void AbstractDrawMode::addDrawnVertex(const Vertex& position) |
86 { |
86 { |
87 if (preAddVertex (pos)) |
87 if (preAddVertex(position)) |
88 return; |
88 return; |
89 |
89 |
90 m_drawedVerts << pos; |
90 m_drawedVerts << position; |
91 } |
91 } |
92 |
92 |
93 bool AbstractDrawMode::mouseReleased (MouseEventData const& data) |
93 bool AbstractDrawMode::mouseReleased(MouseEventData const& data) |
94 { |
94 { |
95 if (Super::mouseReleased (data)) |
95 if (Super::mouseReleased(data)) |
96 return true; |
96 return true; |
97 |
97 |
98 if ((data.releasedButtons & Qt::MidButton) and (m_drawedVerts.size() < 4) and (not data.mouseMoved)) |
98 if ((data.releasedButtons & Qt::MidButton) and (m_drawedVerts.size() < 4) and (not data.mouseMoved)) |
99 { |
99 { |
100 // Find the closest vertex to our cursor |
100 // Find the closest vertex to our cursor |
101 double minimumDistance = 1024.0; |
101 double minimumDistance = 1024.0; |
102 const Vertex* closest = nullptr; |
102 const Vertex* closest = nullptr; |
103 Vertex cursorPosition = renderer()->convert2dTo3d (data.ev->pos(), false); |
103 Vertex cursorPosition = renderer()->convert2dTo3d (data.ev->pos(), false); |
104 QPoint cursorPosition2D (data.ev->pos()); |
104 QPoint cursorPosition2D (data.ev->pos()); |
105 const Axis relZ = renderer()->getRelativeZ(); |
105 const Axis depthAxis = renderer()->getRelativeZ(); |
106 QList<Vertex> vertices = renderer()->document()->inlineVertices().toList(); |
106 QList<Vertex> vertices = renderer()->document()->inlineVertices().toList(); |
107 |
107 |
108 // Sort the vertices in order of distance to camera |
108 // Sort the vertices in order of distance to camera |
109 std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool |
109 std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool |
110 { |
110 { |
111 if (renderer()->cameraInfo (renderer()->camera()).negatedDepth) |
111 if (renderer()->cameraInfo (renderer()->camera()).negatedDepth) |
112 return a[relZ] > b[relZ]; |
112 return a[depthAxis] > b[depthAxis]; |
113 |
113 else |
114 return a[relZ] < b[relZ]; |
114 return a[depthAxis] < b[depthAxis]; |
115 }); |
115 }); |
116 |
116 |
117 for (const Vertex& vrt : vertices) |
117 for (const Vertex& vrt : vertices) |
118 { |
118 { |
119 // If the vertex in 2d space is very close to the cursor then we use it regardless of depth. |
119 // If the vertex in 2d space is very close to the cursor then we use it regardless of depth. |
185 } |
182 } |
186 |
183 |
187 m_drawedVerts.clear(); |
184 m_drawedVerts.clear(); |
188 } |
185 } |
189 |
186 |
190 void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1, |
187 void AbstractDrawMode::drawLineLength(QPainter &painter, const Vertex &v0, const Vertex &v1, |
191 const QPointF& v0p, const QPointF& v1p) const |
188 const QPointF& v0p, const QPointF& v1p) const |
192 { |
189 { |
193 if (not m_config->drawLineLengths()) |
190 if (not m_config->drawLineLengths()) |
194 return; |
191 return; |
195 |
192 |
196 const QString label = QString::number ((v1 - v0).length(), 'f', 2); |
193 const QString label = QString::number ((v1 - v0).length(), 'f', 2); |
197 QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint(); |
194 QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint(); |
198 painter.drawText (origin, label); |
195 painter.drawText (origin, label); |
199 } |
196 } |
200 |
197 |
201 void AbstractDrawMode::renderPolygon (QPainter& painter, const QVector<Vertex>& poly3d, |
198 void AbstractDrawMode::renderPolygon(QPainter& painter, const QVector<Vertex>& polygon3d, |
202 bool withlengths, bool withangles) const |
199 bool drawLineLengths, bool drawAngles ) const |
203 { |
200 { |
204 QVector<QPoint> poly (poly3d.size()); |
201 QVector<QPoint> polygon2d (polygon3d.size()); |
205 QFontMetrics metrics = QFontMetrics (QFont()); |
202 QFontMetrics metrics = QFontMetrics(QFont()); |
206 |
203 |
207 // Convert to 2D |
204 // Convert to 2D |
208 for (int i = 0; i < poly3d.size(); ++i) |
205 for (int i = 0; i < polygon3d.size(); ++i) |
209 poly[i] = renderer()->convert3dTo2d (poly3d[i]); |
206 polygon2d[i] = renderer()->convert3dTo2d(polygon3d[i]); |
210 |
207 |
211 // Draw the polygon-to-be |
208 // Draw the polygon-to-be |
212 painter.setBrush (m_polybrush); |
209 painter.setBrush(m_polybrush); |
213 painter.drawPolygon (QPolygonF (poly)); |
210 painter.drawPolygon(QPolygonF{polygon2d}); |
214 |
211 |
215 // Draw vertex blips |
212 // Draw vertex blips |
216 for (int i = 0; i < poly3d.size(); ++i) |
213 for (int i = 0; i < polygon3d.size(); ++i) |
217 { |
214 { |
218 renderer()->drawBlip (painter, poly[i]); |
215 renderer()->drawBlip(painter, polygon2d[i]); |
219 renderer()->drawBlipCoordinates (painter, poly3d[i], poly[i]); |
216 renderer()->drawBlipCoordinates(painter, polygon3d[i], polygon2d[i]); |
220 } |
217 } |
221 |
218 |
222 // Draw line lenghts and angle info if appropriate |
219 // Draw line lenghts and angle info if appropriate |
223 if (poly3d.size() >= 2 and (withlengths or withangles)) |
220 if (polygon3d.size() >= 2 and (drawLineLengths or drawAngles)) |
224 { |
221 { |
225 painter.setPen (renderer()->textPen()); |
222 painter.setPen (renderer()->textPen()); |
226 |
223 |
227 for (int i = 0; i < poly3d.size(); ++i) |
224 for (int i = 0; i < polygon3d.size(); ++i) |
228 { |
225 { |
229 const int j = (i + 1) % poly3d.size(); |
226 int j = (i + 1) % polygon3d.size(); |
230 const int h = (i - 1 >= 0) ? (i - 1) : (poly3d.size() - 1); |
227 int prior = (i - 1 >= 0) ? (i - 1) : (polygon3d.size() - 1); |
231 |
228 |
232 if (withlengths) |
229 if (drawLineLengths) |
233 drawLength (painter, poly3d[i], poly3d[j], poly[i], poly[j]); |
230 drawLineLength(painter, polygon3d[i], polygon3d[j], polygon2d[i], polygon2d[j]); |
234 |
231 |
235 if (withangles and m_config->drawAngles()) |
232 if (drawAngles and m_config->drawAngles()) |
236 { |
233 { |
237 QLineF l0 (poly[h], poly[i]), |
234 QLineF line0 = {polygon2d[prior], polygon2d[i]}; |
238 l1 (poly[i], poly[j]); |
235 QLineF line1 = {polygon2d[i], polygon2d[j]}; |
239 |
236 double angle = 180 - line0.angleTo(line1); |
240 double angle = 180 - l0.angleTo (l1); |
|
241 |
237 |
242 if (angle < 0) |
238 if (angle < 0) |
243 angle = 180 - l1.angleTo (l0); |
239 angle = 180 - line1.angleTo(line0); |
244 |
240 |
245 QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260")); |
241 QString label = QString::number(angle) + "°"; |
246 QPoint pos = poly[i]; |
242 QPoint textPosition = polygon2d[i]; |
247 pos.setY (pos.y() + metrics.height()); |
243 textPosition.setY(textPosition.y() + metrics.height()); |
248 |
244 painter.drawText(textPosition, label); |
249 painter.drawText (pos, label); |
|
250 } |
245 } |
251 } |
246 } |
252 } |
247 } |
253 } |
248 } |
254 |
249 |
264 } |
259 } |
265 |
260 |
266 return false; |
261 return false; |
267 } |
262 } |
268 |
263 |
|
264 // |
|
265 // roundToInterval |
|
266 // |
|
267 // Rounds 'a' to the nearest multiple of 'interval'. |
|
268 // |
269 template<typename T> |
269 template<typename T> |
270 T intervalClamp (T a, T interval) |
270 T roundToInterval (T a, T interval) |
271 { |
271 { |
272 T remainder = a % interval; |
272 T remainder = a % interval; |
273 |
273 |
274 if (remainder >= float (interval / 2)) |
274 if (remainder >= interval / 2.0) |
275 a += interval; |
275 a += interval; |
276 |
276 |
277 a -= remainder; |
277 a -= remainder; |
278 return a; |
278 return a; |
279 } |
279 } |
280 |
280 |
281 Vertex AbstractDrawMode::getCursorVertex() const |
281 Vertex AbstractDrawMode::getCursorVertex() const |
282 { |
282 { |
283 Vertex result = renderer()->position3D(); |
283 Vertex result = renderer()->position3D(); |
284 |
284 |
285 if (renderer()->keyboardModifiers() & Qt::ControlModifier |
285 if ((renderer()->keyboardModifiers() & Qt::ControlModifier) and not m_drawedVerts.isEmpty()) |
286 and not m_drawedVerts.isEmpty()) |
286 { |
287 { |
287 Vertex const& vertex0 = m_drawedVerts.last(); |
288 Vertex const& v0 = m_drawedVerts.last(); |
288 Vertex const& vertex1 = result; |
289 Vertex const& v1 = result; |
289 Axis relativeX, relativeY; |
290 Axis relX, relY; |
290 |
291 |
291 renderer()->getRelativeAxes (relativeX, relativeY); |
292 renderer()->getRelativeAxes (relX, relY); |
292 QLineF line = {vertex0[relativeX], vertex0[relativeY], vertex1[relativeX], vertex1[relativeY]}; |
293 QLineF ln (v0[relX], v0[relY], v1[relX], v1[relY]); |
293 line.setAngle(roundToInterval<int>(line.angle(), 45)); |
294 ln.setAngle (intervalClamp<int> (ln.angle(), 45)); |
294 result.setCoordinate(relativeX, grid()->snap(line.x2(), Grid::Coordinate)); |
295 result.setCoordinate (relX, grid()->snap(ln.x2(), Grid::Coordinate)); |
295 result.setCoordinate(relativeY, grid()->snap(line.y2(), Grid::Coordinate)); |
296 result.setCoordinate (relY, grid()->snap(ln.y2(), Grid::Coordinate)); |
|
297 } |
296 } |
298 |
297 |
299 return result; |
298 return result; |
300 } |
299 } |