src/editmodes/abstractEditMode.cpp

Sun, 04 Oct 2015 16:45:30 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 04 Oct 2015 16:45:30 +0300
changeset 1008
74cb432812d3
parent 1006
a6b462051ae0
child 1012
413ecd6b9801
permissions
-rw-r--r--

Fixed circle, rectangle and line path modes not working anymore. Add blip coordinates to curve and line path modes. Circle mode for now only can show the coordinates of the initial blip

/*
 *  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 "curvemode.h"
#include "../mainwindow.h"
#include "../glRenderer.h"
#include "../miscallenous.h"

ConfigOption (bool DrawLineLengths = true)
ConfigOption (bool DrawAngles = false)

AbstractEditMode::AbstractEditMode (GLRenderer* renderer) :
	QObject (renderer),
	HierarchyElement (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);
	case EditModeType::Curve: return new CurveMode (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)))
{
	renderer->setContextMenuPolicy (Qt::NoContextMenu); // We need the right mouse button for removing vertices
	renderer->setCursor (Qt::CrossCursor);
	m_window->currentDocument()->clearSelection();
	m_window->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 = nullptr;
		Vertex			cursorPosition = renderer()->convert2dTo3d (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()->convert3dTo2d (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)
			addDrawnVertex (*closest);

		return true;
	}

	if ((data.releasedButtons & Qt::RightButton) and (not m_drawedVerts.isEmpty()))
	{
		// Remove the last vertex
		m_drawedVerts.removeLast();
		return true;
	}

	if (data.releasedButtons & Qt::LeftButton)
	{
		if (maxVertices() and m_drawedVerts.size() >= maxVertices())
		{
			endDraw();
			return true;
		}

		addDrawnVertex (getCursorVertex());
		return true;
	}

	return false;
}

void AbstractDrawMode::finishDraw (LDObjectList const& objs)
{
	int pos = m_window->suggestInsertPoint();

	if (objs.size() > 0)
	{
		for (LDObject* obj : objs)
		{
			renderer()->document()->insertObj (pos++, obj);
			renderer()->compileObject (obj);
		}

		m_window->refresh();
		m_window->endAction();
	}

	m_drawedVerts.clear();
}

void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1,
	const QPointF& v0p, const QPointF& v1p) const
{
	if (not m_config->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()->convert3dTo2d (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)
	{
		renderer()->drawBlip (painter, poly[i]);
		renderer()->drawBlipCoordinates (painter, poly3d[i], poly[i]);
	}

	// 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 m_config->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;
}

template<typename T>
T intervalClamp (T a, T interval)
{
	T remainder = a % interval;

	if (remainder >= float (interval / 2))
		a += interval;

	a -= remainder;
	return a;
}

Vertex AbstractDrawMode::getCursorVertex() const
{
	Vertex result = renderer()->position3D();

	if (renderer()->keyboardModifiers() & Qt::ControlModifier
		and not m_drawedVerts.isEmpty())
	{
		Vertex const& v0 = m_drawedVerts.last();
		Vertex const& v1 = result;
		Axis relX, relY;

		renderer()->getRelativeAxes (relX, relY);
		QLineF ln (v0[relX], v0[relY], v1[relX], v1[relY]);
		ln.setAngle (intervalClamp<int> (ln.angle(), 45));
		result.setCoordinate (relX, Grid::Snap (ln.x2(), Grid::Coordinate));
		result.setCoordinate (relY, Grid::Snap (ln.y2(), Grid::Coordinate));
	}

	return result;
}

mercurial