25 #include "../primitives.h" |
25 #include "../primitives.h" |
26 #include "../glRenderer.h" |
26 #include "../glRenderer.h" |
27 #include "../mainwindow.h" |
27 #include "../mainwindow.h" |
28 #include "../ldObjectMath.h" |
28 #include "../ldObjectMath.h" |
29 |
29 |
30 CircleMode::CircleMode (GLRenderer* renderer) : |
30 CircleMode::CircleMode(GLRenderer* renderer) : |
31 Super (renderer) {} |
31 Super(renderer) {} |
32 |
32 |
33 EditModeType CircleMode::type() const |
33 EditModeType CircleMode::type() const |
34 { |
34 { |
35 return EditModeType::Circle; |
35 return EditModeType::Circle; |
36 } |
36 } |
37 |
37 |
38 double CircleMode::getCircleDrawDist (int pos) const |
38 double CircleMode::getCircleDrawDist(int pos) const |
39 { |
39 { |
40 if (m_drawedVerts.size() >= pos + 1) |
40 if (m_drawedVerts.size() >= pos + 1) |
41 { |
41 { |
42 Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : |
42 Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : |
43 renderer()->convert2dTo3d (renderer()->mousePosition(), false); |
43 renderer()->convert2dTo3d(renderer()->mousePosition(), false); |
44 Axis localx, localy; |
44 Axis localx, localy; |
45 renderer()->getRelativeAxes (localx, localy); |
45 renderer()->getRelativeAxes(localx, localy); |
46 double dx = m_drawedVerts[0][localx] - v1[localx]; |
46 double dx = m_drawedVerts[0][localx] - v1[localx]; |
47 double dy = m_drawedVerts[0][localy] - v1[localy]; |
47 double dy = m_drawedVerts[0][localy] - v1[localy]; |
48 return Grid::Snap (sqrt ((dx * dx) + (dy * dy)), Grid::Coordinate); |
48 return Grid::Snap(sqrt((dx * dx) +(dy * dy)), Grid::Coordinate); |
49 } |
49 } |
50 |
50 |
51 return 0.0; |
51 return 0.0; |
52 } |
52 } |
53 |
53 |
54 Matrix CircleMode::getCircleDrawMatrix (double scale) |
54 Matrix CircleMode::getCircleDrawMatrix(double scale) |
55 { |
55 { |
56 // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed. |
56 // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed. |
57 static const Matrix templates[3] = |
57 static const Matrix templates[3] = |
58 { |
58 { |
59 { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, |
59 { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, |
75 } |
75 } |
76 |
76 |
77 void CircleMode::buildCircle() |
77 void CircleMode::buildCircle() |
78 { |
78 { |
79 LDObjectList objs; |
79 LDObjectList objs; |
80 const int segments (m_window->ringToolSegments()); |
80 const int segments(m_window->ringToolSegments()); |
81 const int divisions (m_window->ringToolHiRes() ? HighResolution : LowResolution); |
81 const int divisions(m_window->ringToolHiRes() ? HighResolution : LowResolution); |
82 double dist0 (getCircleDrawDist (0)); |
82 double dist0(getCircleDrawDist(0)); |
83 double dist1 (getCircleDrawDist (1)); |
83 double dist1(getCircleDrawDist(1)); |
84 LDDocument* refFile; |
84 LDDocument* refFile; |
85 Matrix transform; |
85 Matrix transform; |
86 bool circleOrDisc = false; |
86 bool circleOrDisc = false; |
87 |
87 |
88 if (dist1 < dist0) |
88 if (dist1 < dist0) |
89 qSwap (dist0, dist1); |
89 qSwap(dist0, dist1); |
90 |
90 |
91 if (dist0 == dist1) |
91 if (dist0 == dist1) |
92 { |
92 { |
93 // If the radii are the same, there's no ring space to fill. Use a circle. |
93 // If the radii are the same, there's no ring space to fill. Use a circle. |
94 refFile = GetPrimitive (::Circle, segments, divisions, 0); |
94 refFile = GetPrimitive(::Circle, segments, divisions, 0); |
95 transform = getCircleDrawMatrix (dist0); |
95 transform = getCircleDrawMatrix(dist0); |
96 circleOrDisc = true; |
96 circleOrDisc = true; |
97 } |
97 } |
98 else if (dist0 == 0 or dist1 == 0) |
98 else if (dist0 == 0 or dist1 == 0) |
99 { |
99 { |
100 // If either radii is 0, use a disc. |
100 // If either radii is 0, use a disc. |
101 refFile = GetPrimitive (::Disc, segments, divisions, 0); |
101 refFile = GetPrimitive(::Disc, segments, divisions, 0); |
102 transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); |
102 transform = getCircleDrawMatrix((dist0 != 0) ? dist0 : dist1); |
103 circleOrDisc = true; |
103 circleOrDisc = true; |
104 } |
104 } |
105 else if (g_RingFinder.findRings (dist0, dist1)) |
105 else if (g_RingFinder.findRings(dist0, dist1)) |
106 { |
106 { |
107 // The ring finder found a solution, use that. Add the component rings to the file. |
107 // The ring finder found a solution, use that. Add the component rings to the file. |
108 for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) |
108 for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) |
109 { |
109 { |
110 refFile = GetPrimitive (::Ring, segments, divisions, cmp.num); |
110 refFile = GetPrimitive(::Ring, segments, divisions, cmp.num); |
111 LDSubfileReference* ref = LDSpawn<LDSubfileReference>(); |
111 LDSubfileReference* ref = LDSpawn<LDSubfileReference>(); |
112 ref->setFileInfo (refFile); |
112 ref->setFileInfo(refFile); |
113 ref->setTransform (getCircleDrawMatrix (cmp.scale)); |
113 ref->setTransform(getCircleDrawMatrix(cmp.scale)); |
114 ref->setPosition (m_drawedVerts[0]); |
114 ref->setPosition(m_drawedVerts[0]); |
115 ref->setColor (MainColor); |
115 ref->setColor(MainColor); |
116 objs << ref; |
116 objs << ref; |
117 } |
117 } |
118 } |
118 } |
119 else |
119 else |
120 { |
120 { |
121 // Ring finder failed, last resort: draw the ring with quads |
121 // Ring finder failed, last resort: draw the ring with quads |
122 QList<QLineF> c0, c1; |
122 QList<QLineF> c0, c1; |
123 Axis localx, localy, localz; |
123 Axis localx, localy, localz; |
124 renderer()->getRelativeAxes (localx, localy); |
124 renderer()->getRelativeAxes(localx, localy); |
125 localz = (Axis) (3 - localx - localy); |
125 localz = (Axis)(3 - localx - localy); |
126 double x0 (m_drawedVerts[0][localx]); |
126 double x0(m_drawedVerts[0][localx]); |
127 double y0 (m_drawedVerts[0][localy]); |
127 double y0(m_drawedVerts[0][localy]); |
128 |
128 |
129 Vertex templ; |
129 Vertex templ; |
130 templ.setCoordinate (localx, x0); |
130 templ.setCoordinate(localx, x0); |
131 templ.setCoordinate (localy, y0); |
131 templ.setCoordinate(localy, y0); |
132 templ.setCoordinate (localz, renderer()->getDepthValue()); |
132 templ.setCoordinate(localz, renderer()->getDepthValue()); |
133 |
133 |
134 // Calculate circle coords |
134 // Calculate circle coords |
135 MakeCircle (segments, divisions, dist0, c0); |
135 MakeCircle(segments, divisions, dist0, c0); |
136 MakeCircle (segments, divisions, dist1, c1); |
136 MakeCircle(segments, divisions, dist1, c1); |
137 |
137 |
138 for (int i = 0; i < segments; ++i) |
138 for (int i = 0; i < segments; ++i) |
139 { |
139 { |
140 Vertex v0, v1, v2, v3; |
140 Vertex v0, v1, v2, v3; |
141 v0 = v1 = v2 = v3 = templ; |
141 v0 = v1 = v2 = v3 = templ; |
142 v0.setCoordinate (localx, v0[localx] + c0[i].x1()); |
142 v0.setCoordinate(localx, v0[localx] + c0[i].x1()); |
143 v0.setCoordinate (localy, v0[localy] + c0[i].y1()); |
143 v0.setCoordinate(localy, v0[localy] + c0[i].y1()); |
144 v1.setCoordinate (localx, v1[localx] + c0[i].x2()); |
144 v1.setCoordinate(localx, v1[localx] + c0[i].x2()); |
145 v1.setCoordinate (localy, v1[localy] + c0[i].y2()); |
145 v1.setCoordinate(localy, v1[localy] + c0[i].y2()); |
146 v2.setCoordinate (localx, v2[localx] + c1[i].x2()); |
146 v2.setCoordinate(localx, v2[localx] + c1[i].x2()); |
147 v2.setCoordinate (localy, v2[localy] + c1[i].y2()); |
147 v2.setCoordinate(localy, v2[localy] + c1[i].y2()); |
148 v3.setCoordinate (localx, v3[localx] + c1[i].x1()); |
148 v3.setCoordinate(localx, v3[localx] + c1[i].x1()); |
149 v3.setCoordinate (localy, v3[localy] + c1[i].y1()); |
149 v3.setCoordinate(localy, v3[localy] + c1[i].y1()); |
150 |
150 |
151 LDQuad* quad (LDSpawn<LDQuad> (v0, v1, v2, v3)); |
151 LDQuad* quad(LDSpawn<LDQuad>(v0, v1, v2, v3)); |
152 quad->setColor (MainColor); |
152 quad->setColor(MainColor); |
153 |
153 |
154 // Ensure the quads always are BFC-front towards the camera |
154 // Ensure the quads always are BFC-front towards the camera |
155 if (renderer()->camera() % 3 <= 0) |
155 if (renderer()->camera() % 3 <= 0) |
156 quad->invert(); |
156 quad->invert(); |
157 |
157 |
160 } |
160 } |
161 |
161 |
162 if (circleOrDisc and refFile) |
162 if (circleOrDisc and refFile) |
163 { |
163 { |
164 LDSubfileReference* ref = LDSpawn<LDSubfileReference>(); |
164 LDSubfileReference* ref = LDSpawn<LDSubfileReference>(); |
165 ref->setFileInfo (refFile); |
165 ref->setFileInfo(refFile); |
166 ref->setTransform (transform); |
166 ref->setTransform(transform); |
167 ref->setPosition (m_drawedVerts[0]); |
167 ref->setPosition(m_drawedVerts[0]); |
168 ref->setColor (MainColor); |
168 ref->setColor(MainColor); |
169 objs << ref; |
169 objs << ref; |
170 } |
170 } |
171 |
171 |
172 if (not objs.isEmpty()) |
172 if (not objs.isEmpty()) |
173 { |
173 { |
174 Axis relZ = renderer()->getRelativeZ();; |
174 Axis relZ = renderer()->getRelativeZ();; |
175 const int l (relZ == X ? 1 : 0); |
175 const int l(relZ == X ? 1 : 0); |
176 const int m (relZ == Y ? 1 : 0); |
176 const int m(relZ == Y ? 1 : 0); |
177 const int n (relZ == Z ? 1 : 0); |
177 const int n(relZ == Z ? 1 : 0); |
178 RotateObjects (l, m, n, -m_angleOffset, objs); |
178 RotateObjects(l, m, n, -m_angleOffset, objs); |
179 } |
179 } |
180 |
180 |
181 finishDraw (objs); |
181 finishDraw(objs); |
182 } |
182 } |
183 |
183 |
184 double CircleMode::getAngleOffset() const |
184 double CircleMode::getAngleOffset() const |
185 { |
185 { |
186 if (m_drawedVerts.isEmpty()) |
186 if (m_drawedVerts.isEmpty()) |
187 return 0.0; |
187 return 0.0; |
188 |
188 |
189 const int divisions (m_window->ringToolHiRes() ? HighResolution : LowResolution); |
189 const int divisions(m_window->ringToolHiRes() ? HighResolution : LowResolution); |
190 QPointF originspot (renderer()->convert3dTo2d (m_drawedVerts.first())); |
190 QPointF originspot(renderer()->convert3dTo2d(m_drawedVerts.first())); |
191 QLineF bearing (originspot, renderer()->mousePositionF()); |
191 QLineF bearing(originspot, renderer()->mousePositionF()); |
192 QLineF bearing2 (originspot, QPointF (originspot.x(), 0.0)); |
192 QLineF bearing2(originspot, QPointF(originspot.x(), 0.0)); |
193 double angleoffset (-bearing.angleTo (bearing2) + 90); |
193 double angleoffset(-bearing.angleTo(bearing2) + 90); |
194 angleoffset /= (360.0 / divisions); // convert angle to 0-16 scale |
194 angleoffset /= (360.0 / divisions); // convert angle to 0-16 scale |
195 angleoffset = round (angleoffset); // round to nearest 16th |
195 angleoffset = round(angleoffset); // round to nearest 16th |
196 angleoffset *= ((2 * Pi) / divisions); // convert to radians |
196 angleoffset *= ((2 * Pi) / divisions); // convert to radians |
197 angleoffset *= renderer()->depthNegateFactor(); // negate based on camera |
197 angleoffset *= renderer()->depthNegateFactor(); // negate based on camera |
198 return angleoffset; |
198 return angleoffset; |
199 } |
199 } |
200 |
200 |
201 void CircleMode::render (QPainter& painter) const |
201 void CircleMode::render(QPainter& painter) const |
202 { |
202 { |
203 QFontMetrics metrics = QFontMetrics (QFont()); |
203 QFontMetrics metrics = QFontMetrics(QFont()); |
204 |
204 |
205 // If we have not specified the center point of the circle yet, preview it on the screen. |
205 // If we have not specified the center point of the circle yet, preview it on the screen. |
206 if (m_drawedVerts.isEmpty()) |
206 if (m_drawedVerts.isEmpty()) |
207 { |
207 { |
208 QPoint pos2d = renderer()->convert3dTo2d (renderer()->position3D()); |
208 QPoint pos2d = renderer()->convert3dTo2d(renderer()->position3D()); |
209 renderer()->drawBlip (painter, pos2d); |
209 renderer()->drawBlip(painter, pos2d); |
210 renderer()->drawBlipCoordinates (painter, renderer()->position3D(), pos2d); |
210 renderer()->drawBlipCoordinates(painter, renderer()->position3D(), pos2d); |
211 return; |
211 return; |
212 } |
212 } |
213 |
213 |
214 QVector<Vertex> innerverts, outerverts; |
214 QVector<Vertex> innerverts, outerverts; |
215 QVector<QPointF> innerverts2d, outerverts2d; |
215 QVector<QPointF> innerverts2d, outerverts2d; |
216 const double innerdistance (getCircleDrawDist (0)); |
216 const double innerdistance(getCircleDrawDist(0)); |
217 const double outerdistance (m_drawedVerts.size() >= 2 ? getCircleDrawDist (1) : -1); |
217 const double outerdistance(m_drawedVerts.size() >= 2 ? getCircleDrawDist(1) : -1); |
218 const int divisions (m_window->ringToolHiRes() ? HighResolution : LowResolution); |
218 const int divisions(m_window->ringToolHiRes() ? HighResolution : LowResolution); |
219 const int segments (m_window->ringToolSegments()); |
219 const int segments(m_window->ringToolSegments()); |
220 const double angleUnit (2 * Pi / divisions); |
220 const double angleUnit(2 * Pi / divisions); |
221 Axis relX, relY; |
221 Axis relX, relY; |
222 renderer()->getRelativeAxes (relX, relY); |
222 renderer()->getRelativeAxes(relX, relY); |
223 const double angleoffset (m_drawedVerts.size() < 3 ? getAngleOffset() : m_angleOffset); |
223 const double angleoffset(m_drawedVerts.size() < 3 ? getAngleOffset() : m_angleOffset); |
224 |
224 |
225 // Calculate the preview positions of vertices |
225 // Calculate the preview positions of vertices |
226 for (int i = 0; i < segments + 1; ++i) |
226 for (int i = 0; i < segments + 1; ++i) |
227 { |
227 { |
228 const double sinangle (sin (angleoffset + i * angleUnit)); |
228 const double sinangle(sin(angleoffset + i * angleUnit)); |
229 const double cosangle (cos (angleoffset + i * angleUnit)); |
229 const double cosangle(cos(angleoffset + i * angleUnit)); |
230 Vertex v (Origin); |
230 Vertex v(Origin); |
231 v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * innerdistance)); |
231 v.setCoordinate(relX, m_drawedVerts[0][relX] +(cosangle * innerdistance)); |
232 v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * innerdistance)); |
232 v.setCoordinate(relY, m_drawedVerts[0][relY] +(sinangle * innerdistance)); |
233 innerverts << v; |
233 innerverts << v; |
234 innerverts2d << renderer()->convert3dTo2d (v); |
234 innerverts2d << renderer()->convert3dTo2d(v); |
235 |
235 |
236 if (outerdistance != -1) |
236 if (outerdistance != -1) |
237 { |
237 { |
238 v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * outerdistance)); |
238 v.setCoordinate(relX, m_drawedVerts[0][relX] +(cosangle * outerdistance)); |
239 v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * outerdistance)); |
239 v.setCoordinate(relY, m_drawedVerts[0][relY] +(sinangle * outerdistance)); |
240 outerverts << v; |
240 outerverts << v; |
241 outerverts2d << renderer()->convert3dTo2d (v); |
241 outerverts2d << renderer()->convert3dTo2d(v); |
242 } |
242 } |
243 } |
243 } |
244 |
244 |
245 QVector<QLineF> lines (segments); |
245 QVector<QLineF> lines(segments); |
246 |
246 |
247 if (outerdistance != -1 and outerdistance != innerdistance) |
247 if (outerdistance != -1 and outerdistance != innerdistance) |
248 { |
248 { |
249 painter.setBrush (m_polybrush); |
249 painter.setBrush(m_polybrush); |
250 painter.setPen (Qt::NoPen); |
250 painter.setPen(Qt::NoPen); |
251 |
251 |
252 // Compile polygons |
252 // Compile polygons |
253 for (int i = 0; i < segments; ++i) |
253 for (int i = 0; i < segments; ++i) |
254 { |
254 { |
255 QVector<QPointF> points; |
255 QVector<QPointF> points; |
256 points << innerverts2d[i] |
256 points << innerverts2d[i] |
257 << innerverts2d[i + 1] |
257 << innerverts2d[i + 1] |
258 << outerverts2d[i + 1] |
258 << outerverts2d[i + 1] |
259 << outerverts2d[i]; |
259 << outerverts2d[i]; |
260 painter.drawPolygon (QPolygonF (points)); |
260 painter.drawPolygon(QPolygonF(points)); |
261 lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); |
261 lines << QLineF(innerverts2d[i], innerverts2d[i + 1]); |
262 lines << QLineF (outerverts2d[i], outerverts2d[i + 1]); |
262 lines << QLineF(outerverts2d[i], outerverts2d[i + 1]); |
263 } |
263 } |
264 |
264 |
265 // Add bordering edges for unclosed rings/discs |
265 // Add bordering edges for unclosed rings/discs |
266 if (segments != divisions) |
266 if (segments != divisions) |
267 { |
267 { |
268 lines << QLineF (innerverts2d.first(), outerverts2d.first()); |
268 lines << QLineF(innerverts2d.first(), outerverts2d.first()); |
269 lines << QLineF (innerverts2d.last(), outerverts2d.last()); |
269 lines << QLineF(innerverts2d.last(), outerverts2d.last()); |
270 } |
270 } |
271 } |
271 } |
272 else |
272 else |
273 { |
273 { |
274 for (int i = 0; i < segments; ++i) |
274 for (int i = 0; i < segments; ++i) |
275 lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); |
275 lines << QLineF(innerverts2d[i], innerverts2d[i + 1]); |
276 } |
276 } |
277 |
277 |
278 // Draw green blips at where the points are |
278 // Draw green blips at where the points are |
279 for (QPointF const& point : innerverts2d + outerverts2d) |
279 for (QPointF const& point : innerverts2d + outerverts2d) |
280 renderer()->drawBlip (painter, point); |
280 renderer()->drawBlip(painter, point); |
281 |
281 |
282 // Draw edge lines |
282 // Draw edge lines |
283 painter.setPen (renderer()->linePen()); |
283 painter.setPen(renderer()->linePen()); |
284 painter.drawLines (lines); |
284 painter.drawLines(lines); |
285 |
285 |
286 // Draw the current radius in the middle of the circle. |
286 // Draw the current radius in the middle of the circle. |
287 QPoint origin = renderer()->convert3dTo2d (m_drawedVerts[0]); |
287 QPoint origin = renderer()->convert3dTo2d(m_drawedVerts[0]); |
288 QString label = QString::number (innerdistance); |
288 QString label = QString::number(innerdistance); |
289 painter.setPen (renderer()->textPen()); |
289 painter.setPen(renderer()->textPen()); |
290 painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); |
290 painter.drawText(origin.x() -(metrics.width(label) / 2), origin.y(), label); |
291 |
291 |
292 if (m_drawedVerts.size() >= 2) |
292 if (m_drawedVerts.size() >= 2) |
293 { |
293 { |
294 painter.drawText (origin.x() - (metrics.width (label) / 2), |
294 painter.drawText(origin.x() -(metrics.width(label) / 2), |
295 origin.y() + metrics.height(), QString::number (outerdistance)); |
295 origin.y() + metrics.height(), QString::number(outerdistance)); |
296 } |
296 } |
297 } |
297 } |
298 |
298 |
299 bool CircleMode::mouseReleased (MouseEventData const& data) |
299 bool CircleMode::mouseReleased(MouseEventData const& data) |
300 { |
300 { |
301 if (data.releasedButtons & Qt::LeftButton) |
301 if (data.releasedButtons & Qt::LeftButton) |
302 { |
302 { |
303 if (m_drawedVerts.size() < 2) |
303 if (m_drawedVerts.size() < 2) |
304 addDrawnVertex (renderer()->position3D()); |
304 addDrawnVertex(renderer()->position3D()); |
305 else |
305 else |
306 buildCircle(); |
306 buildCircle(); |
307 return true; |
307 return true; |
308 } |
308 } |
309 else |
309 else |
310 { |
310 { |
311 return Super::mouseReleased(data); |
311 return Super::mouseReleased(data); |
312 } |
312 } |
313 } |
313 } |
314 |
314 |
315 bool CircleMode::preAddVertex (const Vertex&) |
315 bool CircleMode::preAddVertex(const Vertex&) |
316 { |
316 { |
317 m_angleOffset = getAngleOffset(); |
317 m_angleOffset = getAngleOffset(); |
318 return false; |
318 return false; |
319 } |
319 } |