src/editmodes/abstractEditMode.cpp

branch
projects
changeset 935
8d98ee0dc917
parent 933
f4c80d92e71e
child 941
f895379d7fab
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <QMouseEvent>
+#include <stdexcept>
+#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<Vertex>	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<Vertex>& poly3d,
+	bool withlengths, bool withangles) const
+{
+	QVector<QPoint> 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;
+}

mercurial