diff -r ab77deb851fa -r 8d98ee0dc917 src/editmodes/abstractEditMode.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/editmodes/abstractEditMode.cpp Tue Mar 03 22:29:27 2015 +0200
@@ -0,0 +1,263 @@
+/*
+ * 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
+#include "abstractEditMode.h"
+#include "selectMode.h"
+#include "drawMode.h"
+#include "rectangleMode.h"
+#include "circleMode.h"
+#include "magicWandMode.h"
+#include "linePathMode.h"
+#include "../mainWindow.h"
+#include "../glRenderer.h"
+
+CFGENTRY (Bool, DrawLineLengths, true)
+CFGENTRY (Bool, DrawAngles, false)
+
+AbstractEditMode::AbstractEditMode (GLRenderer* renderer) :
+ m_renderer (renderer) {}
+
+AbstractEditMode::~AbstractEditMode() {}
+
+AbstractEditMode* AbstractEditMode::createByType (GLRenderer* renderer, EditModeType type)
+{
+ switch (type)
+ {
+ case EditModeType::Select: return new SelectMode (renderer);
+ case EditModeType::Draw: return new DrawMode (renderer);
+ case EditModeType::Rectangle: return new RectangleMode (renderer);
+ case EditModeType::Circle: return new CircleMode (renderer);
+ case EditModeType::MagicWand: return new MagicWandMode (renderer);
+ case EditModeType::LinePath: return new LinePathMode (renderer);
+ }
+
+ throw std::logic_error ("bad type given to AbstractEditMode::createByType");
+}
+
+GLRenderer* AbstractEditMode::renderer() const
+{
+ return m_renderer;
+}
+
+AbstractDrawMode::AbstractDrawMode (GLRenderer* renderer) :
+ AbstractEditMode (renderer),
+ m_polybrush (QBrush (QColor (64, 192, 0, 128)))
+{
+ // Disable the context menu - we need the right mouse button
+ // for removing vertices.
+ renderer->setContextMenuPolicy (Qt::NoContextMenu);
+
+ // Use the crosshair cursor when drawing.
+ renderer->setCursor (Qt::CrossCursor);
+
+ // Clear the selection when beginning to draw.
+ CurrentDocument()->clearSelection();
+
+ g_win->updateSelection();
+ m_drawedVerts.clear();
+}
+
+AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) :
+ AbstractEditMode (renderer)
+{
+ renderer->unsetCursor();
+ renderer->setContextMenuPolicy (Qt::DefaultContextMenu);
+}
+
+// =============================================================================
+//
+void AbstractDrawMode::addDrawnVertex (Vertex const& pos)
+{
+ if (preAddVertex (pos))
+ return;
+
+ m_drawedVerts << pos;
+}
+
+bool AbstractDrawMode::mouseReleased (MouseEventData const& data)
+{
+ if (Super::mouseReleased (data))
+ return true;
+
+ if ((data.releasedButtons & Qt::MidButton) and (m_drawedVerts.size() < 4) and (not data.mouseMoved))
+ {
+ // Find the closest vertex to our cursor
+ double minimumDistance = 1024.0;
+ const Vertex* closest = null;
+ Vertex cursorPosition = renderer()->coordconv2_3 (data.ev->pos(), false);
+ QPoint cursorPosition2D (data.ev->pos());
+ const Axis relZ = renderer()->getRelativeZ();
+ QVector vertices = renderer()->document()->inlineVertices();
+
+ // Sort the vertices in order of distance to camera
+ std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool
+ {
+ if (renderer()->getFixedCamera (renderer()->camera()).negatedDepth)
+ return a[relZ] > b[relZ];
+
+ return a[relZ] < b[relZ];
+ });
+
+ for (const Vertex& vrt : vertices)
+ {
+ // If the vertex in 2d space is very close to the cursor then we use
+ // it regardless of depth.
+ QPoint vect2d = renderer()->coordconv3_2 (vrt) - cursorPosition2D;
+ const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2);
+ if (distance2DSquared < 16.0 * 16.0)
+ {
+ closest = &vrt;
+ break;
+ }
+
+ // Check if too far away from the cursor.
+ if (distance2DSquared > 64.0 * 64.0)
+ continue;
+
+ // Not very close to the cursor. Compare using true distance,
+ // including depth.
+ const double distanceSquared = (vrt - cursorPosition).lengthSquared();
+
+ if (distanceSquared < minimumDistance)
+ {
+ minimumDistance = distanceSquared;
+ closest = &vrt;
+ }
+ }
+
+ if (closest != null)
+ addDrawnVertex (*closest);
+
+ return true;
+ }
+
+ if ((data.releasedButtons & Qt::RightButton) and (not m_drawedVerts.isEmpty()))
+ {
+ // Remove the last vertex
+ m_drawedVerts.removeLast();
+
+ return true;
+ }
+
+ return false;
+}
+
+void AbstractDrawMode::finishDraw (LDObjectList const& objs)
+{
+ int pos = g_win->getInsertionPoint();
+
+ if (objs.size() > 0)
+ {
+ for (LDObjectPtr obj : objs)
+ {
+ renderer()->document()->insertObj (pos++, obj);
+ renderer()->compileObject (obj);
+ }
+
+ g_win->refresh();
+ g_win->endAction();
+ }
+
+ m_drawedVerts.clear();
+}
+
+void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1,
+ const QPointF& v0p, const QPointF& v1p) const
+{
+ if (not cfg::DrawLineLengths)
+ return;
+
+ const QString label = QString::number ((v1 - v0).length());
+ QPoint origin = QLineF (v0p, v1p).pointAt (0.5).toPoint();
+ painter.drawText (origin, label);
+}
+
+void AbstractDrawMode::renderPolygon (QPainter& painter, const QVector& poly3d,
+ bool withlengths, bool withangles) const
+{
+ QVector poly (poly3d.size());
+ QFontMetrics metrics = QFontMetrics (QFont());
+
+ // Convert to 2D
+ for (int i = 0; i < poly3d.size(); ++i)
+ poly[i] = renderer()->coordconv3_2 (poly3d[i]);
+
+ // Draw the polygon-to-be
+ painter.setBrush (m_polybrush);
+ painter.drawPolygon (QPolygonF (poly));
+
+ // Draw vertex blips
+ for (int i = 0; i < poly3d.size(); ++i)
+ {
+ QPoint& blip = poly[i];
+ painter.setPen (renderer()->linePen());
+ renderer()->drawBlip (painter, blip);
+
+ // Draw their coordinates
+ painter.setPen (renderer()->textPen());
+ painter.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true));
+ }
+
+ // Draw line lenghts and angle info if appropriate
+ if (poly3d.size() >= 2 and (withlengths or withangles))
+ {
+ painter.setPen (renderer()->textPen());
+
+ for (int i = 0; i < poly3d.size(); ++i)
+ {
+ const int j = (i + 1) % poly3d.size();
+ const int h = (i - 1 >= 0) ? (i - 1) : (poly3d.size() - 1);
+
+ if (withlengths)
+ drawLength (painter, poly3d[i], poly3d[j], poly[i], poly[j]);
+
+ if (withangles and cfg::DrawAngles)
+ {
+ QLineF l0 (poly[h], poly[i]),
+ l1 (poly[i], poly[j]);
+
+ double angle = 180 - l0.angleTo (l1);
+
+ if (angle < 0)
+ angle = 180 - l1.angleTo (l0);
+
+ QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260"));
+ QPoint pos = poly[i];
+ pos.setY (pos.y() + metrics.height());
+
+ painter.drawText (pos, label);
+ }
+ }
+ }
+}
+
+bool AbstractDrawMode::keyReleased (QKeyEvent *ev)
+{
+ if (Super::keyReleased (ev))
+ return true;
+
+ if (not m_drawedVerts.isEmpty() and ev->key() == Qt::Key_Backspace)
+ {
+ m_drawedVerts.removeLast();
+ return true;
+ }
+
+ return false;
+}