src/tools/drawtool.cpp

Tue, 24 May 2022 16:11:10 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Tue, 24 May 2022 16:11:10 +0300
changeset 188
64ea7282611e
parent 185
a38a0eb007b0
permissions
-rw-r--r--

more work on circle tool + cleanup

#include <QMessageBox>
#include <document.h>
#include "linetypes/edge.h"
#include "linetypes/triangle.h"
#include "linetypes/quadrilateral.h"
#include "drawtool.h"
#include "modeleditor.h"

static const QBrush pointBrush = {Qt::white};
static const QPen polygonPen = {QBrush{Qt::black}, 2.0, Qt::DashLine};
static const QPen badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine};
static const QPen pointPen = {QBrush{Qt::black}, 2.0};
static const QBrush greenPolygonBrush = {QColor{64, 255, 128, 192}};
static const QBrush redPolygonBrush = {QColor{255, 96, 96, 192}};

AbstractDrawTool::AbstractDrawTool(Document *document) :
	BaseTool{document}
{
}

DrawTool::DrawTool(Document* document) :
	AbstractDrawTool{document}
{
}

QString DrawTool::name() const
{
	static const QString result = tr("Draw");
	return result;
}

QString DrawTool::toolTip() const
{
	static const QString result = tr("Draw new elements into the model.");
	return result;
}

bool AbstractDrawTool::mouseClick(Canvas* canvas, QMouseEvent* event)
{
	if (event->button() == Qt::LeftButton)
	{
		this->addCurrentPoint(canvas);
		return true;
	}
	else if (event->button() == Qt::RightButton)
	{
		this->removeLastPoint();
		return true;
	}
	else
	{
		return false;
	}
}

bool AbstractDrawTool::mouseMove(Document* document, Canvas* canvas, QMouseEvent *event)
{
	static_cast<void>(document);
	static_cast<void>(event);
	const auto& worldPosition = canvas->getWorldPosition();
	if (worldPosition.has_value())
	{
		this->previewPoint = worldPosition.value();
		this->updatePreviewPolygon();
	}
	return false;
}

bool AbstractDrawTool::keyReleased(Document*, Canvas* canvas, QKeyEvent* event)
{
	if (event->key() == Qt::Key_Escape)
	{
		this->polygon.clear();
		this->updatePreviewPolygon();
		canvas->update();
		return true;
	}
	else
	{
		return false;
	}
}

void AbstractDrawTool::addCurrentPoint(Canvas* canvas)
{
	const auto& worldPosition = canvas->getWorldPosition();
	if (worldPosition.has_value())
	{
		const glm::vec3& pos = worldPosition.value();
		if (this->isCloseToExistingPoints(pos))
		{
			this->closeShape();
		}
		else
		{
			this->addPoint(pos);
		}
	}
}

void AbstractDrawTool::updatePreviewPolygon()
{
	this->previewPolygon = this->polygon;
	this->previewPolygon.resize(this->polygon.size() + 1);
	this->previewPolygon.back() = this->previewPoint;
	if (this->previewPolygon.size() > 2)
	{
		this->isconcave = not geom::isConvex(this->previewPolygon);
	}
}

void AbstractDrawTool::reset()
{
	this->polygon.clear();
}

void AbstractDrawTool::overpaint(Canvas* canvas, QPainter* painter) const
{
	painter->setPen(this->isconcave ? ::badPolygonPen : ::polygonPen);
	if (this->previewPolygon.size() > 2 and not this->isconcave)
	{
		if (canvas->worldPolygonWinding(this->previewPolygon) == Winding::Clockwise)
		{
			painter->setBrush(::greenPolygonBrush);
		}
		else
		{
			painter->setBrush(::redPolygonBrush);
		}
		canvas->drawWorldPolygon(painter, this->previewPolygon);
	}
	else
	{
		canvas->drawWorldPolyline(painter, this->previewPolygon);
	}
	painter->setBrush(::pointBrush);
	painter->setPen(::pointPen);
	for (const glm::vec3& point : this->polygon)
	{
		canvas->drawWorldPoint(painter, point);
	}
	canvas->drawWorldPoint(painter, this->previewPoint);
}

void AbstractDrawTool::addPoint(const glm::vec3 &pos)
{
	this->polygon.push_back(pos);
	this->updatePreviewPolygon();
}

void AbstractDrawTool::removeLastPoint()
{
	if (this->polygon.size() > 0)
	{
		this->polygon.erase(this->polygon.end() - 1);
		this->updatePreviewPolygon();
	}
}

void AbstractDrawTool::clearPoints()
{
	this->polygon.clear();
	this->updatePreviewPolygon();
}

bool AbstractDrawTool::isCloseToExistingPoints(const glm::vec3 &pos) const
{
	const auto isCloseToPos = [&](const glm::vec3& x)
	{
		return geom::isclose(x, pos);
	};
	return any(this->polygon, isCloseToPos);
}

QString DrawTool::iconName() const
{
	return ":/icons/pencil-outline.png";
}

void DrawTool::addPoint(const glm::vec3 &pos)
{
	AbstractDrawTool::addPoint(pos);
	if (this->polygon.size() == 4)
	{
		this->closeShape();
	}
}

template<std::size_t N, typename T>
std::array<T, N> vectorToArray(const std::vector<T>& x)
{
	std::array<T, N> result;
	for (std::size_t i = 0; i < x.size() and i < N; i += 1)
	{
		result[i] = x[i];
	}
	return result;
}

void DrawTool::closeShape()
{
	if (this->polygon.size() >= 2 and this->polygon.size() <= 4)
	{
		std::unique_ptr<ModelEditor> modelEditor = this->document->editModel();
		switch (this->polygon.size())
		{
		case 2:
			modelEditor->append<ldraw::Edge>(vectorToArray<2>(this->polygon), ldraw::EDGE_COLOR);
			break;
		case 3:
			modelEditor->append<ldraw::Triangle>(vectorToArray<3>(this->polygon), ldraw::MAIN_COLOR);
			break;
		case 4:
			modelEditor->append<ldraw::Quadrilateral>(vectorToArray<4>(this->polygon), ldraw::MAIN_COLOR);
			break;
		}
	}
	this->clearPoints();
}

mercurial