src/editmodes/circleMode.cpp

changeset 1088
c6d242d2b619
parent 1082
1738bdaf36d6
child 1104
edddb9b0db9e
equal deleted inserted replaced
1087:80e25f6b0bb0 1088:c6d242d2b619
24 #include "../ringFinder.h" 24 #include "../ringFinder.h"
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 "../mathfunctions.h" 28 #include "../mathfunctions.h"
29 #include "../miscallenous.h"
29 #include "../grid.h" 30 #include "../grid.h"
30 31
31 CircleMode::CircleMode (GLRenderer* renderer) : 32 CircleMode::CircleMode(GLRenderer* renderer) :
32 Super (renderer) {} 33 Super {renderer} {}
33 34
34 35
35 EditModeType CircleMode::type() const 36 EditModeType CircleMode::type() const
36 { 37 {
37 return EditModeType::Circle; 38 return EditModeType::Circle;
38 } 39 }
39 40
40 41
41 double CircleMode::getCircleDrawDist (int pos) const 42 double CircleMode::getCircleDrawDist(int position) const
42 { 43 {
43 if (countof(m_drawedVerts) >= pos + 1) 44 if (countof(m_drawedVerts) >= position + 1)
44 { 45 {
45 Vertex v1; 46 Vertex v1;
46 47
47 if (countof(m_drawedVerts) >= pos + 2) 48 if (countof(m_drawedVerts) >= position + 2)
48 v1 = m_drawedVerts[pos + 1]; 49 v1 = m_drawedVerts[position + 1];
49 else 50 else
50 v1 = renderer()->convert2dTo3d (renderer()->mousePosition(), false); 51 v1 = renderer()->convert2dTo3d (renderer()->mousePosition(), false);
51 52
52 Axis localx, localy; 53 Axis localx, localy;
53 renderer()->getRelativeAxes (localx, localy); 54 renderer()->getRelativeAxes(localx, localy);
54 double dx = m_drawedVerts[0][localx] - v1[localx]; 55 double dx = m_drawedVerts[0][localx] - v1[localx];
55 double dy = m_drawedVerts[0][localy] - v1[localy]; 56 double dy = m_drawedVerts[0][localy] - v1[localy];
56 return grid()->snap(hypot(dx, dy), Grid::Coordinate); 57 return grid()->snap(hypot(dx, dy), Grid::Coordinate);
57 } 58 }
58 59
59 return 0.0; 60 return 0.0;
60 } 61 }
61 62
62 63
63 Matrix CircleMode::getCircleDrawMatrix (double scale) 64 Matrix CircleMode::getCircleDrawMatrix(double scale)
64 { 65 {
65 // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed. 66 // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed.
66 static const Matrix templates[3] = 67 static const Matrix templates[3] =
67 { 68 {
68 { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, 69 { 2, 0, 0, 0, 1, 0, 0, 0, 2 },
96 LDDocument* primitiveFile; 97 LDDocument* primitiveFile;
97 Matrix transform; 98 Matrix transform;
98 bool circleOrDisc = false; 99 bool circleOrDisc = false;
99 100
100 if (dist1 < dist0) 101 if (dist1 < dist0)
101 qSwap (dist0, dist1); 102 qSwap(dist0, dist1);
102 103
103 if (dist0 == dist1) 104 if (dist0 == dist1)
104 { 105 {
105 // If the radii are the same, there's no ring space to fill. Use a circle. 106 // If the radii are the same, there's no ring space to fill. Use a circle.
106 primitiveModel.type = PrimitiveModel::Circle; 107 primitiveModel.type = PrimitiveModel::Circle;
107 primitiveFile = primitives()->getPrimitive(primitiveModel); 108 primitiveFile = primitives()->getPrimitive(primitiveModel);
108 transform = getCircleDrawMatrix (dist0); 109 transform = getCircleDrawMatrix(dist0);
109 circleOrDisc = true; 110 circleOrDisc = true;
110 } 111 }
111 else if (dist0 == 0 or dist1 == 0) 112 else if (dist0 == 0 or dist1 == 0)
112 { 113 {
113 // If either radii is 0, use a disc. 114 // If either radii is 0, use a disc.
114 primitiveModel.type = PrimitiveModel::Disc; 115 primitiveModel.type = PrimitiveModel::Disc;
115 primitiveFile = primitives()->getPrimitive(primitiveModel); 116 primitiveFile = primitives()->getPrimitive(primitiveModel);
116 transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); 117 transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1);
117 circleOrDisc = true; 118 circleOrDisc = true;
118 } 119 }
119 else if (g_RingFinder.findRings (dist0, dist1)) 120 else if (g_RingFinder.findRings(dist0, dist1))
120 { 121 {
121 // The ring finder found a solution, use that. Add the component rings to the file. 122 // The ring finder found a solution, use that. Add the component rings to the file.
122 primitiveModel.type = PrimitiveModel::Ring; 123 primitiveModel.type = PrimitiveModel::Ring;
123 124
124 for (const RingFinder::Component& component : g_RingFinder.bestSolution()->getComponents()) 125 for (const RingFinder::Component& component : g_RingFinder.bestSolution()->getComponents())
132 { 133 {
133 // Ring finder failed, last resort: draw the ring with quads 134 // Ring finder failed, last resort: draw the ring with quads
134 Axis localx, localy, localz; 135 Axis localx, localy, localz;
135 renderer()->getRelativeAxes (localx, localy); 136 renderer()->getRelativeAxes (localx, localy);
136 localz = (Axis) (3 - localx - localy); 137 localz = (Axis) (3 - localx - localy);
137 double x0 (m_drawedVerts[0][localx]); 138 double x0 = m_drawedVerts[0][localx];
138 double y0 (m_drawedVerts[0][localy]); 139 double y0 = m_drawedVerts[0][localy];
139 140
140 Vertex templ; 141 Vertex templ;
141 templ.setCoordinate (localx, x0); 142 templ.setCoordinate(localx, x0);
142 templ.setCoordinate (localy, y0); 143 templ.setCoordinate(localy, y0);
143 templ.setCoordinate (localz, renderer()->getDepthValue()); 144 templ.setCoordinate(localz, renderer()->getDepthValue());
144 145
145 // Calculate circle coords 146 // Calculate circle coords
146 QVector<QLineF> c0 = makeCircle(primitiveModel.segments, primitiveModel.divisions, dist0); 147 QVector<QLineF> c0 = makeCircle(primitiveModel.segments, primitiveModel.divisions, dist0);
147 QVector<QLineF> c1 = makeCircle(primitiveModel.segments, primitiveModel.divisions, dist1); 148 QVector<QLineF> c1 = makeCircle(primitiveModel.segments, primitiveModel.divisions, dist1);
148 149
158 v2.setCoordinate (localy, v2[localy] + c1[i].y2()); 159 v2.setCoordinate (localy, v2[localy] + c1[i].y2());
159 v3.setCoordinate (localx, v3[localx] + c1[i].x1()); 160 v3.setCoordinate (localx, v3[localx] + c1[i].x1());
160 v3.setCoordinate (localy, v3[localy] + c1[i].y1()); 161 v3.setCoordinate (localy, v3[localy] + c1[i].y1());
161 162
162 LDQuad* quad = model.emplace<LDQuad>(v0, v1, v2, v3); 163 LDQuad* quad = model.emplace<LDQuad>(v0, v1, v2, v3);
163 quad->setColor (MainColor); 164 quad->setColor(MainColor);
164 165
165 // Ensure the quads always are BFC-front towards the camera 166 // Ensure the quads always are BFC-front towards the camera
166 if (renderer()->camera() % 3 <= 0) 167 if (renderer()->camera() % 3 <= 0)
167 quad->invert(); 168 quad->invert();
168 } 169 }
181 } 182 }
182 183
183 finishDraw (model); 184 finishDraw (model);
184 } 185 }
185 186
186 187 /*
187 double CircleMode::getAngleOffset() const 188 * Which way around will we place our circle primitive? This only makes a difference if we're not drawing a full circle.
188 { 189 * Result is an angle offset in radians.
189 if (m_drawedVerts.isEmpty()) 190 */
191 double CircleMode::orientation() const
192 {
193 if (not m_drawedVerts.isEmpty())
194 {
195 int divisions = m_window->ringToolHiRes() ? HighResolution : LowResolution;
196 QPointF originSpot = renderer()->convert3dTo2d(m_drawedVerts.first());
197 // Line from the origin of the circle to current mouse position
198 QLineF hand1 = {originSpot, renderer()->mousePositionF()};
199 // Line from the origin spot to
200 QLineF hand2 = {{0, 0}, {1, 0}};
201 // Calculate the angle between these hands and round it to whole divisions.
202 double angleoffset = roundToInterval(-hand1.angleTo(hand2), 360.0 / divisions);
203 // Take the camera's depth coefficient into account here. This way, the preview is flipped if the
204 // primitive also would be.
205 return angleoffset * pi / 180.0 * renderer()->depthNegateFactor();
206 }
207 else
208 {
190 return 0.0; 209 return 0.0;
191 210 }
192 int divisions = (m_window->ringToolHiRes() ? HighResolution : LowResolution);
193 QPointF originspot (renderer()->convert3dTo2d (m_drawedVerts.first()));
194 QLineF bearing (originspot, renderer()->mousePositionF());
195 QLineF bearing2 (originspot, QPointF (originspot.x(), 0.0));
196 double angleoffset (-bearing.angleTo (bearing2) + 90);
197 angleoffset /= (360.0 / divisions); // convert angle to 0-16 scale
198 angleoffset = round (angleoffset); // round to nearest 16th
199 angleoffset *= ((2 * pi) / divisions); // convert to radians
200 angleoffset *= renderer()->depthNegateFactor(); // negate based on camera
201 return angleoffset;
202 } 211 }
203 212
204 213
205 void CircleMode::render (QPainter& painter) const 214 void CircleMode::render (QPainter& painter) const
206 { 215 {
207 QFontMetrics metrics = QFontMetrics (QFont()); 216 QFontMetrics metrics = QFontMetrics (QFont());
208 217
209 // If we have not specified the center point of the circle yet, preview it on the screen. 218 // If we have not specified the center point of the circle yet, preview it on the screen.
210 if (m_drawedVerts.isEmpty()) 219 if (m_drawedVerts.isEmpty())
211 { 220 {
212 QPoint pos2d = renderer()->convert3dTo2d (renderer()->position3D()); 221 QPoint position2d = renderer()->convert3dTo2d(renderer()->position3D());
213 renderer()->drawPoint (painter, pos2d); 222 renderer()->drawPoint(painter, position2d);
214 renderer()->drawBlipCoordinates (painter, renderer()->position3D(), pos2d); 223 renderer()->drawBlipCoordinates(painter, renderer()->position3D(), position2d);
215 return; 224 return;
216 } 225 }
217 226
218 QVector<Vertex> innerverts, outerverts; 227 QVector<Vertex> innerverts, outerverts;
219 QVector<QPointF> innerverts2d, outerverts2d; 228 QVector<QPointF> innerverts2d, outerverts2d;
220 const double innerdistance (getCircleDrawDist (0)); 229 double innerdistance = getCircleDrawDist(0);
221 const double outerdistance (countof(m_drawedVerts) >= 2 ? getCircleDrawDist (1) : -1); 230 double outerdistance = countof(m_drawedVerts) >= 2 ? getCircleDrawDist (1) : -1;
222 const int divisions (m_window->ringToolHiRes() ? HighResolution : LowResolution); 231 int divisions = m_window->ringToolHiRes() ? HighResolution : LowResolution;
223 const int segments (m_window->ringToolSegments()); 232 int segments = m_window->ringToolSegments();
224 const double angleUnit (2 * pi / divisions); 233 double angleUnit = 2 * pi / divisions;
225 Axis relX, relY; 234 Axis relX, relY;
226 renderer()->getRelativeAxes (relX, relY); 235 renderer()->getRelativeAxes(relX, relY);
227 const double angleoffset (countof(m_drawedVerts) < 3 ? getAngleOffset() : m_angleOffset); 236 double angleoffset = (countof(m_drawedVerts) < 3 ? orientation() : m_angleOffset);
228 237
229 // Calculate the preview positions of vertices 238 // Calculate the preview positions of vertices
230 for (int i = 0; i < segments + 1; ++i) 239 for (int i = 0; i < segments + 1; ++i)
231 { 240 {
232 const double sinangle (sin (angleoffset + i * angleUnit)); 241 const double sinangle (sin (angleoffset + i * angleUnit));
244 outerverts << v; 253 outerverts << v;
245 outerverts2d << renderer()->convert3dTo2d (v); 254 outerverts2d << renderer()->convert3dTo2d (v);
246 } 255 }
247 } 256 }
248 257
249 QVector<QLineF> lines (segments); 258 QVector<QLineF> lines {segments};
250 259
251 if (outerdistance != -1 and outerdistance != innerdistance) 260 if (outerdistance != -1 and outerdistance != innerdistance)
252 { 261 {
253 painter.setBrush (m_polybrush); 262 painter.setBrush(m_polybrush);
254 painter.setPen (Qt::NoPen); 263 painter.setPen(Qt::NoPen);
255 264
256 // Compile polygons 265 // Compile polygons
257 for (int i = 0; i < segments; ++i) 266 for (int i = 0; i < segments; ++i)
258 { 267 {
259 QVector<QPointF> points; 268 QVector<QPointF> points;
260 points << innerverts2d[i] 269 points << innerverts2d[i]
261 << innerverts2d[i + 1] 270 << innerverts2d[i + 1]
262 << outerverts2d[i + 1] 271 << outerverts2d[i + 1]
263 << outerverts2d[i]; 272 << outerverts2d[i];
264 painter.drawPolygon (QPolygonF (points)); 273 painter.drawPolygon (QPolygonF (points));
265 lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); 274 lines.append({innerverts2d[i], innerverts2d[i + 1]});
266 lines << QLineF (outerverts2d[i], outerverts2d[i + 1]); 275 lines.append({outerverts2d[i], outerverts2d[i + 1]});
267 } 276 }
268 277
269 // Add bordering edges for unclosed rings/discs 278 // Add bordering edges for unclosed rings/discs
270 if (segments != divisions) 279 if (segments != divisions)
271 { 280 {
272 lines << QLineF (innerverts2d.first(), outerverts2d.first()); 281 lines.append({innerverts2d.first(), outerverts2d.first()});
273 lines << QLineF (innerverts2d.last(), outerverts2d.last()); 282 lines.append({innerverts2d.last(), outerverts2d.last()});
274 } 283 }
275 } 284 }
276 else 285 else
277 { 286 {
278 for (int i = 0; i < segments; ++i) 287 for (int i = 0; i < segments; ++i)
279 lines << QLineF (innerverts2d[i], innerverts2d[i + 1]); 288 lines.append({innerverts2d[i], innerverts2d[i + 1]});
280 } 289 }
281 290
282 // Draw green blips at where the points are 291 // Draw green blips at where the points are
283 for (QPointF const& point : innerverts2d + outerverts2d) 292 for (const QPointF& point : innerverts2d + outerverts2d)
284 renderer()->drawPoint (painter, point); 293 renderer()->drawPoint(painter, point);
285 294
286 // Draw edge lines 295 // Draw edge lines
287 painter.setPen (renderer()->linePen()); 296 painter.setPen(renderer()->linePen());
288 painter.drawLines (lines); 297 painter.drawLines(lines);
289 298
290 // Draw the current radius in the middle of the circle. 299 // Draw the current radius in the middle of the circle.
291 QPoint origin = renderer()->convert3dTo2d (m_drawedVerts[0]); 300 QPoint origin = renderer()->convert3dTo2d (m_drawedVerts[0]);
292 QString label = QString::number (innerdistance); 301 QString label = QString::number (innerdistance);
293 painter.setPen (renderer()->textPen()); 302 painter.setPen(renderer()->textPen());
294 painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); 303 painter.drawText(origin.x() - (metrics.width(label) / 2), origin.y(), label);
295 304
296 if (countof(m_drawedVerts) >= 2) 305 if (countof(m_drawedVerts) >= 2)
297 { 306 {
298 painter.drawText (origin.x() - (metrics.width (label) / 2), 307 painter.drawText(origin.x() - (metrics.width(label) / 2),
299 origin.y() + metrics.height(), QString::number (outerdistance)); 308 origin.y() + metrics.height(), QString::number(outerdistance));
300 } 309 }
301 } 310 }
302 311
303 312
304 bool CircleMode::preAddVertex (const Vertex&) 313 bool CircleMode::preAddVertex (const Vertex&)
305 { 314 {
306 m_angleOffset = getAngleOffset(); 315 m_angleOffset = orientation();
307 return false; 316 return false;
308 } 317 }
309 318
310 319
311 int CircleMode::maxVertices() const 320 int CircleMode::maxVertices() const

mercurial