diff -r ab77deb851fa -r 8d98ee0dc917 src/editmodes/circleMode.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/editmodes/circleMode.cpp Tue Mar 03 22:29:27 2015 +0200
@@ -0,0 +1,315 @@
+/*
+ * LDForge: LDraw parts authoring CAD
+ * Copyright (C) 2013 - 2015 Teemu Piippo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include "circleMode.h"
+#include "../miscallenous.h"
+#include "../ldObject.h"
+#include "../ldDocument.h"
+#include "../ringFinder.h"
+#include "../primitives.h"
+#include "../glRenderer.h"
+#include "../mainWindow.h"
+#include "../ldObjectMath.h"
+
+CircleMode::CircleMode (GLRenderer* renderer) :
+ Super (renderer) {}
+
+EditModeType CircleMode::type() const
+{
+ return EditModeType::Circle;
+}
+
+double CircleMode::getCircleDrawDist (int pos) const
+{
+ assert (m_drawedVerts.size() >= pos + 1);
+ Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] :
+ renderer()->coordconv2_3 (renderer()->mousePosition(), false);
+ Axis localx, localy;
+ renderer()->getRelativeAxes (localx, localy);
+ double dx = m_drawedVerts[0][localx] - v1[localx];
+ double dy = m_drawedVerts[0][localy] - v1[localy];
+ return Grid::Snap (sqrt ((dx * dx) + (dy * dy)), Grid::Coordinate);
+}
+
+Matrix CircleMode::getCircleDrawMatrix (double scale)
+{
+ // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed.
+ static const Matrix templates[3] =
+ {
+ { 2, 0, 0, 0, 1, 0, 0, 0, 2 },
+ { 2, 0, 0, 0, 0, 2, 0, 1, 0 },
+ { 0, 1, 0, 2, 0, 0, 0, 0, 2 },
+ };
+
+ Matrix transform = templates[renderer()->camera() % 3];
+
+ for (int i = 0; i < 9; ++i)
+ {
+ if (transform[i] == 2)
+ transform[i] = scale;
+ elif (transform[i] == 1 and renderer()->camera() >= 3)
+ transform[i] = -1;
+ }
+
+ return transform;
+}
+
+void CircleMode::buildCircle()
+{
+ LDObjectList objs;
+ const int segments (g_win->ringToolSegments());
+ const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution);
+ double dist0 (getCircleDrawDist (0));
+ double dist1 (getCircleDrawDist (1));
+ LDDocumentPtr refFile;
+ Matrix transform;
+ bool circleOrDisc = false;
+
+ if (dist1 < dist0)
+ qSwap (dist0, dist1);
+
+ if (dist0 == dist1)
+ {
+ // If the radii are the same, there's no ring space to fill. Use a circle.
+ refFile = GetPrimitive (::Circle, segments, divisions, 0);
+ transform = getCircleDrawMatrix (dist0);
+ circleOrDisc = true;
+ }
+ elif (dist0 == 0 or dist1 == 0)
+ {
+ // If either radii is 0, use a disc.
+ refFile = GetPrimitive (::Disc, segments, divisions, 0);
+ transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1);
+ circleOrDisc = true;
+ }
+ elif (g_RingFinder.findRings (dist0, dist1))
+ {
+ // The ring finder found a solution, use that. Add the component rings to the file.
+ for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents())
+ {
+ refFile = GetPrimitive (::Ring, segments, divisions, cmp.num);
+ LDSubfilePtr ref = LDSpawn();
+ ref->setFileInfo (refFile);
+ ref->setTransform (getCircleDrawMatrix (cmp.scale));
+ ref->setPosition (m_drawedVerts[0]);
+ ref->setColor (MainColor());
+ objs << ref;
+ }
+ }
+ else
+ {
+ // Ring finder failed, last resort: draw the ring with quads
+ QList c0, c1;
+ Axis localx, localy, localz;
+ renderer()->getRelativeAxes (localx, localy);
+ localz = (Axis) (3 - localx - localy);
+ double x0 (m_drawedVerts[0][localx]);
+ double y0 (m_drawedVerts[0][localy]);
+
+ Vertex templ;
+ templ.setCoordinate (localx, x0);
+ templ.setCoordinate (localy, y0);
+ templ.setCoordinate (localz, renderer()->getDepthValue());
+
+ // Calculate circle coords
+ MakeCircle (segments, divisions, dist0, c0);
+ MakeCircle (segments, divisions, dist1, c1);
+
+ for (int i = 0; i < segments; ++i)
+ {
+ Vertex v0, v1, v2, v3;
+ v0 = v1 = v2 = v3 = templ;
+ v0.setCoordinate (localx, v0[localx] + c0[i].x1());
+ v0.setCoordinate (localy, v0[localy] + c0[i].y1());
+ v1.setCoordinate (localx, v1[localx] + c0[i].x2());
+ v1.setCoordinate (localy, v1[localy] + c0[i].y2());
+ v2.setCoordinate (localx, v2[localx] + c1[i].x2());
+ v2.setCoordinate (localy, v2[localy] + c1[i].y2());
+ v3.setCoordinate (localx, v3[localx] + c1[i].x1());
+ v3.setCoordinate (localy, v3[localy] + c1[i].y1());
+
+ LDQuadPtr quad (LDSpawn (v0, v1, v2, v3));
+ quad->setColor (MainColor());
+
+ // Ensure the quads always are BFC-front towards the camera
+ if (renderer()->camera() % 3 <= 0)
+ quad->invert();
+
+ objs << quad;
+ }
+ }
+
+ if (circleOrDisc and refFile != null)
+ {
+ LDSubfilePtr ref = LDSpawn();
+ ref->setFileInfo (refFile);
+ ref->setTransform (transform);
+ ref->setPosition (m_drawedVerts[0]);
+ ref->setColor (MainColor());
+ objs << ref;
+ }
+
+ unless (objs.isEmpty())
+ {
+ Axis relZ = renderer()->getRelativeZ();;
+ const int l (relZ == X ? 1 : 0);
+ const int m (relZ == Y ? 1 : 0);
+ const int n (relZ == Z ? 1 : 0);
+ RotateObjects (l, m, n, -m_angleOffset, objs);
+ }
+
+ finishDraw (objs);
+}
+
+double CircleMode::getAngleOffset() const
+{
+ if (m_drawedVerts.isEmpty())
+ return 0.0;
+
+ const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution);
+ QPointF originspot (renderer()->coordconv3_2 (m_drawedVerts.first()));
+ QLineF bearing (originspot, renderer()->mousePositionF());
+ QLineF bearing2 (originspot, QPointF (originspot.x(), 0.0));
+ double angleoffset (-bearing.angleTo (bearing2) + 90);
+ angleoffset /= (360.0 / divisions); // convert angle to 0-16 scale
+ angleoffset = round (angleoffset); // round to nearest 16th
+ angleoffset *= ((2 * Pi) / divisions); // convert to radians
+ angleoffset *= renderer()->depthNegateFactor(); // negate based on camera
+ return angleoffset;
+}
+
+void CircleMode::render (QPainter& painter) const
+{
+ QFontMetrics metrics = QFontMetrics (QFont());
+
+ // If we have not specified the center point of the circle yet, preview it on the screen.
+ if (m_drawedVerts.isEmpty())
+ {
+ renderer()->drawBlip (painter, renderer()->coordconv3_2 (renderer()->position3D()));
+ return;
+ }
+
+ QVector innerverts, outerverts;
+ QVector innerverts2d, outerverts2d;
+ const double innerdistance (getCircleDrawDist (0));
+ const double outerdistance (m_drawedVerts.size() >= 2 ? getCircleDrawDist (1) : -1);
+ const int divisions (g_win->ringToolHiRes() ? HighResolution : LowResolution);
+ const int segments (g_win->ringToolSegments());
+ const double angleUnit (2 * Pi / divisions);
+ Axis relX, relY;
+ renderer()->getRelativeAxes (relX, relY);
+ const double angleoffset (m_drawedVerts.size() < 3 ? getAngleOffset() : m_angleOffset);
+
+ // Calculate the preview positions of vertices
+ for (int i = 0; i < segments + 1; ++i)
+ {
+ const double sinangle (sin (angleoffset + i * angleUnit));
+ const double cosangle (cos (angleoffset + i * angleUnit));
+ Vertex v (Origin);
+ v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * innerdistance));
+ v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * innerdistance));
+ innerverts << v;
+ innerverts2d << renderer()->coordconv3_2 (v);
+
+ if (outerdistance != -1)
+ {
+ v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * outerdistance));
+ v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * outerdistance));
+ outerverts << v;
+ outerverts2d << renderer()->coordconv3_2 (v);
+ }
+ }
+
+ QVector lines (segments);
+
+ if (outerdistance != -1 and outerdistance != innerdistance)
+ {
+ painter.setBrush (m_polybrush);
+ painter.setPen (Qt::NoPen);
+
+ // Compile polygons
+ for (int i = 0; i < segments; ++i)
+ {
+ QVector points;
+ points << innerverts2d[i]
+ << innerverts2d[i + 1]
+ << outerverts2d[i + 1]
+ << outerverts2d[i];
+ painter.drawPolygon (QPolygonF (points));
+ lines << QLineF (innerverts2d[i], innerverts2d[i + 1]);
+ lines << QLineF (outerverts2d[i], outerverts2d[i + 1]);
+ }
+
+ // Add bordering edges for unclosed rings/discs
+ if (segments != divisions)
+ {
+ lines << QLineF (innerverts2d.first(), outerverts2d.first());
+ lines << QLineF (innerverts2d.last(), outerverts2d.last());
+ }
+ }
+ else
+ {
+ for (int i = 0; i < segments; ++i)
+ lines << QLineF (innerverts2d[i], innerverts2d[i + 1]);
+ }
+
+ // Draw a green blips at where the points are
+ for (QPointF const& point : innerverts2d + outerverts2d)
+ renderer()->drawBlip (painter, point);
+
+ // Draw edge lines
+ painter.setPen (renderer()->linePen());
+ painter.drawLines (lines);
+
+ // Draw the current radius in the middle of the circle.
+ QPoint origin = renderer()->coordconv3_2 (m_drawedVerts[0]);
+ QString label = QString::number (innerdistance);
+ painter.setPen (renderer()->textPen());
+ painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label);
+
+ if (m_drawedVerts.size() >= 2)
+ {
+ painter.drawText (origin.x() - (metrics.width (label) / 2),
+ origin.y() + metrics.height(), QString::number (outerdistance));
+ }
+}
+
+bool CircleMode::mouseReleased (MouseEventData const& data)
+{
+ if (Super::mouseReleased (data))
+ return true;
+
+ if (data.releasedButtons & Qt::LeftButton)
+ {
+ if (m_drawedVerts.size() < 3)
+ addDrawnVertex (renderer()->position3D());
+ else
+ buildCircle();
+
+ return true;
+ }
+
+ return false;
+}
+
+bool CircleMode::preAddVertex (const Vertex&)
+{
+ m_angleOffset = getAngleOffset();
+ return false;
+}