1 /* |
|
2 * LDForge: LDraw parts authoring CAD |
|
3 * Copyright (C) 2013 - 2015 Teemu Piippo |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, either version 3 of the License, or |
|
8 * (at your option) any later version. |
|
9 * |
|
10 * This program is distributed in the hope that it will be useful, |
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 * GNU General Public License for more details. |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License |
|
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17 */ |
|
18 |
|
19 #include <QMouseEvent> |
|
20 #include <stdexcept> |
|
21 #include "abstractEditMode.h" |
|
22 #include "selectMode.h" |
|
23 #include "drawMode.h" |
|
24 #include "rectangleMode.h" |
|
25 #include "circleMode.h" |
|
26 #include "magicWandMode.h" |
|
27 #include "linePathMode.h" |
|
28 #include "../mainWindow.h" |
|
29 #include "../glRenderer.h" |
|
30 |
|
31 CFGENTRY (Bool, DrawLineLengths, true) |
|
32 CFGENTRY (Bool, DrawAngles, false) |
|
33 |
|
34 AbstractEditMode::AbstractEditMode (GLRenderer* renderer) : |
|
35 m_renderer (renderer) {} |
|
36 |
|
37 AbstractEditMode::~AbstractEditMode() {} |
|
38 |
|
39 AbstractEditMode* AbstractEditMode::createByType (GLRenderer* renderer, EditModeType type) |
|
40 { |
|
41 switch (type) |
|
42 { |
|
43 case EditModeType::Select: return new SelectMode (renderer); |
|
44 case EditModeType::Draw: return new DrawMode (renderer); |
|
45 case EditModeType::Rectangle: return new RectangleMode (renderer); |
|
46 case EditModeType::Circle: return new CircleMode (renderer); |
|
47 case EditModeType::MagicWand: return new MagicWandMode (renderer); |
|
48 case EditModeType::LinePath: return new LinePathMode (renderer); |
|
49 } |
|
50 |
|
51 throw std::logic_error ("bad type given to AbstractEditMode::createByType"); |
|
52 } |
|
53 |
|
54 GLRenderer* AbstractEditMode::renderer() const |
|
55 { |
|
56 return m_renderer; |
|
57 } |
|
58 |
|
59 AbstractDrawMode::AbstractDrawMode (GLRenderer* renderer) : |
|
60 AbstractEditMode (renderer), |
|
61 m_polybrush (QBrush (QColor (64, 192, 0, 128))) |
|
62 { |
|
63 // Disable the context menu - we need the right mouse button |
|
64 // for removing vertices. |
|
65 renderer->setContextMenuPolicy (Qt::NoContextMenu); |
|
66 |
|
67 // Use the crosshair cursor when drawing. |
|
68 renderer->setCursor (Qt::CrossCursor); |
|
69 |
|
70 // Clear the selection when beginning to draw. |
|
71 CurrentDocument()->clearSelection(); |
|
72 |
|
73 g_win->updateSelection(); |
|
74 m_drawedVerts.clear(); |
|
75 } |
|
76 |
|
77 AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) : |
|
78 AbstractEditMode (renderer) |
|
79 { |
|
80 renderer->unsetCursor(); |
|
81 renderer->setContextMenuPolicy (Qt::DefaultContextMenu); |
|
82 } |
|
83 |
|
84 // ============================================================================= |
|
85 // |
|
86 void AbstractDrawMode::addDrawnVertex (Vertex const& pos) |
|
87 { |
|
88 if (preAddVertex (pos)) |
|
89 return; |
|
90 |
|
91 m_drawedVerts << pos; |
|
92 } |
|
93 |
|
94 bool AbstractDrawMode::mouseReleased (MouseEventData const& data) |
|
95 { |
|
96 if (Super::mouseReleased (data)) |
|
97 return true; |
|
98 |
|
99 if ((data.releasedButtons & Qt::MidButton) and (m_drawedVerts.size() < 4) and (not data.mouseMoved)) |
|
100 { |
|
101 // Find the closest vertex to our cursor |
|
102 double minimumDistance = 1024.0; |
|
103 const Vertex* closest = null; |
|
104 Vertex cursorPosition = renderer()->coordconv2_3 (data.ev->pos(), false); |
|
105 QPoint cursorPosition2D (data.ev->pos()); |
|
106 const Axis relZ = renderer()->getRelativeZ(); |
|
107 QVector<Vertex> vertices = renderer()->document()->inlineVertices(); |
|
108 |
|
109 // Sort the vertices in order of distance to camera |
|
110 std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool |
|
111 { |
|
112 if (renderer()->getFixedCamera (renderer()->camera()).negatedDepth) |
|
113 return a[relZ] > b[relZ]; |
|
114 |
|
115 return a[relZ] < b[relZ]; |
|
116 }); |
|
117 |
|
118 for (const Vertex& vrt : vertices) |
|
119 { |
|
120 // If the vertex in 2d space is very close to the cursor then we use |
|
121 // it regardless of depth. |
|
122 QPoint vect2d = renderer()->coordconv3_2 (vrt) - cursorPosition2D; |
|
123 const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); |
|
124 if (distance2DSquared < 16.0 * 16.0) |
|
125 { |
|
126 closest = &vrt; |
|
127 break; |
|
128 } |
|
129 |
|
130 // Check if too far away from the cursor. |
|
131 if (distance2DSquared > 64.0 * 64.0) |
|
132 continue; |
|
133 |
|
134 // Not very close to the cursor. Compare using true distance, |
|
135 // including depth. |
|
136 const double distanceSquared = (vrt - cursorPosition).lengthSquared(); |
|
137 |
|
138 if (distanceSquared < minimumDistance) |
|
139 { |
|
140 minimumDistance = distanceSquared; |
|
141 closest = &vrt; |
|
142 } |
|
143 } |
|
144 |
|
145 if (closest != null) |
|
146 addDrawnVertex (*closest); |
|
147 |
|
148 return true; |
|
149 } |
|
150 |
|
151 if ((data.releasedButtons & Qt::RightButton) and (not m_drawedVerts.isEmpty())) |
|
152 { |
|
153 // Remove the last vertex |
|
154 m_drawedVerts.removeLast(); |
|
155 |
|
156 return true; |
|
157 } |
|
158 |
|
159 return false; |
|
160 } |
|
161 |
|
162 void AbstractDrawMode::finishDraw (LDObjectList const& objs) |
|
163 { |
|
164 int pos = g_win->getInsertionPoint(); |
|
165 |
|
166 if (objs.size() > 0) |
|
167 { |
|
168 for (LDObjectPtr obj : objs) |
|
169 { |
|
170 renderer()->document()->insertObj (pos++, obj); |
|
171 renderer()->compileObject (obj); |
|
172 } |
|
173 |
|
174 g_win->refresh(); |
|
175 g_win->endAction(); |
|
176 } |
|
177 |
|
178 m_drawedVerts.clear(); |
|
179 } |
|
180 |
|
181 void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1, |
|
182 const QPointF& v0p, const QPointF& v1p) const |
|
183 { |
|
184 if (not cfg::DrawLineLengths) |
|
185 return; |
|
186 |
|
187 const QString label = QString::number ((v1 - v0).length()); |
|
188 QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint(); |
|
189 painter.drawText (origin, label); |
|
190 } |
|
191 |
|
192 void AbstractDrawMode::renderPolygon (QPainter& painter, const QVector<Vertex>& poly3d, |
|
193 bool withlengths, bool withangles) const |
|
194 { |
|
195 QVector<QPoint> poly (poly3d.size()); |
|
196 QFontMetrics metrics = QFontMetrics (QFont()); |
|
197 |
|
198 // Convert to 2D |
|
199 for (int i = 0; i < poly3d.size(); ++i) |
|
200 poly[i] = renderer()->coordconv3_2 (poly3d[i]); |
|
201 |
|
202 // Draw the polygon-to-be |
|
203 painter.setBrush (m_polybrush); |
|
204 painter.drawPolygon (QPolygonF (poly)); |
|
205 |
|
206 // Draw vertex blips |
|
207 for (int i = 0; i < poly3d.size(); ++i) |
|
208 { |
|
209 QPoint& blip = poly[i]; |
|
210 painter.setPen (renderer()->linePen()); |
|
211 renderer()->drawBlip (painter, blip); |
|
212 |
|
213 // Draw their coordinates |
|
214 painter.setPen (renderer()->textPen()); |
|
215 painter.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true)); |
|
216 } |
|
217 |
|
218 // Draw line lenghts and angle info if appropriate |
|
219 if (poly3d.size() >= 2 and (withlengths or withangles)) |
|
220 { |
|
221 painter.setPen (renderer()->textPen()); |
|
222 |
|
223 for (int i = 0; i < poly3d.size(); ++i) |
|
224 { |
|
225 const int j = (i + 1) % poly3d.size(); |
|
226 const int h = (i - 1 >= 0) ? (i - 1) : (poly3d.size() - 1); |
|
227 |
|
228 if (withlengths) |
|
229 drawLength (painter, poly3d[i], poly3d[j], poly[i], poly[j]); |
|
230 |
|
231 if (withangles and cfg::DrawAngles) |
|
232 { |
|
233 QLineF l0 (poly[h], poly[i]), |
|
234 l1 (poly[i], poly[j]); |
|
235 |
|
236 double angle = 180 - l0.angleTo (l1); |
|
237 |
|
238 if (angle < 0) |
|
239 angle = 180 - l1.angleTo (l0); |
|
240 |
|
241 QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260")); |
|
242 QPoint pos = poly[i]; |
|
243 pos.setY (pos.y() + metrics.height()); |
|
244 |
|
245 painter.drawText (pos, label); |
|
246 } |
|
247 } |
|
248 } |
|
249 } |
|
250 |
|
251 bool AbstractDrawMode::keyReleased (QKeyEvent *ev) |
|
252 { |
|
253 if (Super::keyReleased (ev)) |
|
254 return true; |
|
255 |
|
256 if (not m_drawedVerts.isEmpty() and ev->key() == Qt::Key_Backspace) |
|
257 { |
|
258 m_drawedVerts.removeLast(); |
|
259 return true; |
|
260 } |
|
261 |
|
262 return false; |
|
263 } |
|