src/document.cpp

changeset 217
6d95c1a41e6e
parent 214
8e1fe64ce4e3
child 222
72b456f2f3c2
--- a/src/document.cpp	Mon Jun 13 02:18:25 2022 +0300
+++ b/src/document.cpp	Tue Jun 14 17:55:50 2022 +0300
@@ -17,65 +17,193 @@
  */
 
 #include <QMouseEvent>
-#include <QMessageBox>
-#include <QVBoxLayout>
+#include <QPainter>
 #include "document.h"
 #include "model.h"
 #include "ui/objecteditor.h"
+#include "gl/partrenderer.h"
 
-EditTools::EditTools(
-	Model* model,
-	const ColorTable& colorTable,
-	QObject* parent) :
+EditTools::EditTools(QObject* parent) :
 	QObject{parent},
-	colorTable{colorTable},
-	model{model},
-	vertexMap{model}
+	RenderLayer{}
 {
-#if 0
-	connect(this->canvas, &Canvas::mouseClick, this, &EditTools::canvasMouseClick);
-	connect(this->canvas, &Canvas::mouseMove, this, &EditTools::canvasMouseMove);
-	connect(this->canvas, &Canvas::newStatusText, this, &EditTools::newStatusText);
-	connect(this->model, &Model::dataChanged, this->canvas, qOverload<>(&Canvas::update));
-	connect(&this->vertexMap, &VertexMap::verticesChanged, [&]()
-	{
-		this->canvas->rebuildVertices(&this->vertexMap);
-	});
-	this->canvas->drawState = &this->drawState;
-#endif
 }
 
 EditTools::~EditTools()
 {
 }
 
-void EditTools::applyToVertices(VertexMap::ApplyFunction fn) const
-{
-	this->vertexMap.apply(fn);
-}
-
 void EditTools::setEditMode(EditingMode mode)
 {
-	this->drawState.mode = mode;
+	this->mode = mode;
+}
+
+void EditTools::setGridMatrix(const glm::mat4& gridMatrix)
+{
+	this->gridMatrix = gridMatrix;
+	this->gridPlane = planeFromTriangle({
+		this->gridMatrix * glm::vec4{0, 0, 0, 1},
+		this->gridMatrix * glm::vec4{1, 0, 0, 1},
+		this->gridMatrix * glm::vec4{0, 1, 0, 1},
+	});
+}
+
+void EditTools::mvpMatrixChanged(const glm::mat4& matrix)
+{
+	this->mvpMatrix = matrix;
+}
+
+void EditTools::mouseMoved(const QMouseEvent* event)
+{
+	this->worldPosition = this->renderer->screenToModelCoordinates(event->pos(), this->gridPlane);
+	if (this->worldPosition.has_value())
+	{
+		// Snap the position to grid. This procedure is basically the "change of basis" and almost follows the
+		// A⁻¹ × M × A formula which is used to perform a transformation in some other coordinate system, except
+		// we actually use the inverted matrix first and the regular one last to perform the transformation of
+		// grid coordinates in our XY coordinate system. Also, we're rounding the coordinates which is obviously
+		// not a linear transformation, but fits the pattern anyway.
+		// First transform the coordinates to the XY plane...
+		this->worldPosition = glm::inverse(this->gridMatrix) * glm::vec4{*this->worldPosition, 1};
+		// Then round the coordinates to integer precision...
+		this->worldPosition = glm::round(*this->worldPosition);
+		// And finally transform it back to grid coordinates by transforming it with the
+		// grid matrix.
+		this->worldPosition = this->gridMatrix * glm::vec4{*this->worldPosition, 1};
+	}
+	this->updatePreviewPolygon();
+}
+
+static QVector<QPointF> convertWorldPointsToScreenPoints(
+	const std::vector<glm::vec3> &worldPoints,
+	const PartRenderer* renderer)
+{
+	QVector<QPointF> points2d;
+	points2d.reserve(worldPoints.size());
+	for (const glm::vec3& point : worldPoints)
+	{
+		points2d.push_back(renderer->modelToScreenCoordinates(point));
+	}
+	return points2d;
+}
+
+static Winding worldPolygonWinding(
+	const std::vector<glm::vec3> &points,
+	const PartRenderer* renderer)
+{
+	return winding(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
+}
+
+static void drawWorldPoint(
+	QPainter* painter,
+	const glm::vec3& worldPoint,
+	const PartRenderer* renderer)
+{
+	const QPointF center = renderer->modelToScreenCoordinates(worldPoint);
+	painter->drawEllipse(inscribe(CircleF{center, 5}));
 }
 
-void updatePreviewPolygon(DrawState* drawState)
+static void drawWorldPolyline(
+	QPainter *painter,
+	const std::vector<glm::vec3> &points,
+	const PartRenderer* renderer)
+{
+	painter->drawPolyline(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
+}
+
+static void drawWorldPolygon(
+	QPainter* painter,
+	const std::vector<glm::vec3> &points,
+	const PartRenderer* renderer)
+{
+	painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
+}
+
+void EditTools::overpaint(QPainter* painter)
 {
-	drawState->previewPolygon = drawState->polygon;
-	drawState->previewPolygon.resize(drawState->polygon.size() + 1);
-	drawState->previewPolygon.back() = drawState->previewPoint;
-	if (drawState->previewPolygon.size() > 2)
+	struct Pens
 	{
-		drawState->isconcave = not isConvex(drawState->previewPolygon);
+		const QBrush pointBrush;
+		const QPen pointPen;
+		const QPen polygonPen;
+		const QPen badPolygonPen;
+		const QBrush greenPolygonBrush;
+		const QBrush redPolygonBrush;
+	};
+	static const Pens brightPens{
+		.pointBrush = {Qt::white},
+		.pointPen = {QBrush{Qt::black}, 2.0},
+		.polygonPen = {QBrush{Qt::black}, 2.0, Qt::DashLine},
+		.badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine},
+		.greenPolygonBrush = {QColor{64, 255, 128, 192}},
+		.redPolygonBrush = {QColor{255, 96, 96, 192}},
+	};
+	static const Pens darkPens{
+		.pointBrush = {Qt::black},
+		.pointPen = {QBrush{Qt::white}, 2.0},
+		.polygonPen = {QBrush{Qt::white}, 2.0, Qt::DashLine},
+		.badPolygonPen = {QBrush{Qt::red}, 2.0, Qt::DashLine},
+		.greenPolygonBrush = {QColor{64, 255, 128, 192}},
+		.redPolygonBrush = {QColor{255, 96, 96, 192}},
+	};
+	const Pens& pens = (this->renderer->isDark() ? darkPens : brightPens);
+	switch(this->mode) {
+	case SelectMode:
+		break;
+	case DrawMode:
+		{
+			painter->setPen(this->isconcave ? pens.badPolygonPen : pens.polygonPen);
+			if (this->previewPolygon.size() > 2 and not this->isconcave)
+			{
+				if (worldPolygonWinding(this->previewPolygon, this->renderer) == Winding::Clockwise) {
+					painter->setBrush(pens.greenPolygonBrush);
+				}
+				else {
+					painter->setBrush(pens.redPolygonBrush);
+				}
+				drawWorldPolygon(painter, this->previewPolygon, this->renderer);
+			}
+			else {
+				drawWorldPolyline(painter, this->previewPolygon, this->renderer);
+			}
+			painter->setBrush(pens.pointBrush);
+			painter->setPen(pens.pointPen);
+			for (const glm::vec3& point : this->polygon) {
+				drawWorldPoint(painter, point, this->renderer);
+			}
+		}
+		break;
+	}
+	if (this->worldPosition.has_value())
+	{
+		painter->setRenderHint(QPainter::Antialiasing);
+		painter->setPen(Qt::white);
+		painter->setBrush(Qt::green);
+		const QPointF pos = this->renderer->modelToScreenCoordinates(*this->worldPosition);
+		painter->drawEllipse(pos, 5, 5);
+		painter->drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition));
 	}
 }
 
-void removeLastPoint(DrawState* drawState)
+void EditTools::updatePreviewPolygon()
 {
-	if (drawState->polygon.size() > 0)
+	this->previewPolygon = this->polygon;
+	if (this->worldPosition.has_value()) {
+		this->previewPolygon.resize(this->polygon.size() + 1);
+		this->previewPolygon.back() = *this->worldPosition;
+	}
+	if (this->previewPolygon.size() > 2)
 	{
-		drawState->polygon.erase(drawState->polygon.end() - 1);
-		updatePreviewPolygon(drawState);
+		this->isconcave = not isConvex(this->previewPolygon);
+	}
+}
+
+void EditTools::removeLastPoint()
+{
+	if (this->polygon.size() > 0)
+	{
+		this->polygon.erase(this->polygon.end() - 1);
+		this->updatePreviewPolygon();
 	}
 }
 
@@ -84,106 +212,51 @@
 	return any(points, std::bind(isclose, std::placeholders::_1, pos));
 }
 
-void EditTools::canvasMouseClick(QMouseEvent*)
+EditingMode EditTools::currentEditingMode() const
 {
-#if 0
-	switch(this->drawState.mode)
-	{
+	return this->mode;
+}
+
+void EditTools::mouseClick(const QMouseEvent* event)
+{
+	switch(this->mode) {
 	case SelectMode:
-		if (event->button() == Qt::LeftButton)
-		{
-			const ModelId highlighted = this->canvas->getHighlightedObject();
-			QSet<ModelId> selected;
-			if (highlighted != ModelId{0}) {
-				selected.insert(highlighted);
-			}
-			//this->select(selected);
-			event->accept();
+		if (event->button() == Qt::LeftButton) {
+			const ModelId highlighted = this->renderer->pick(event->pos());
+			Q_EMIT this->select({highlighted}, false);
 		}
 		break;
 	case DrawMode:
-		if (event->button() == Qt::LeftButton) {
-			if (isCloseToExistingPoints(this->drawState.polygon, worldPosition)) {
+		if (event->button() == Qt::LeftButton and this->worldPosition.has_value()) {
+			if (isCloseToExistingPoints(this->polygon, *this->worldPosition)) {
 				this->closeShape();
 			}
 			else {
-				this->drawState.polygon.push_back(pos);
-				updatePreviewPolygon(&this->drawState);
+				this->polygon.push_back(*this->worldPosition);
+				this->updatePreviewPolygon();
 			}
-			event->accept();
 		}
 		else if (true
 			and event->button() == Qt::RightButton
-			and this->drawState.polygon.size() > 0
+			and this->polygon.size() > 0
 		) {
-			this->drawState.polygon.erase(this->drawState.polygon.end() - 1);
-			updatePreviewPolygon(&this->drawState);
-			event->accept();
+			this->polygon.erase(this->polygon.end() - 1);
+			updatePreviewPolygon();
 		}
 		break;
 	}
-#endif
 }
-void EditTools::canvasMouseMove(QMouseEvent*)
-{
-#if 0
-	switch(this->drawState.mode)
-	{
-	case SelectMode:
-		break;
-	case DrawMode:
-		if (this->canvas->worldPosition.has_value())
-		{
-			this->drawState.previewPoint = this->canvas->worldPosition.value();
-			updatePreviewPolygon(&this->drawState);
-			this->update();
-		}
-		event->accept();
-		break;
-	}
-#endif
-}
-#if 0
-/*
 
-void EditorTabWidget::select(const QSet<ModelId> &selected)
-{
-	QItemSelectionModel* selectionModel = this->ui.listView->selectionModel();
-	QItemSelection itemSelection;
-	for (const ModelId id : selected)
-	{
-		const std::optional<int> row = this->model->find(id);
-		if (row.has_value())
-		{
-			const QModelIndex qindex = this->model->index(*row);
-			itemSelection.select(qindex, qindex);
-		}
-	}
-	selectionModel->select(itemSelection, QItemSelectionModel::ClearAndSelect);
-}
-*/
-const QSet<ModelId> EditTools::selectedObjects() const
-{
-	return this->canvas->selectedObjects();
-}
-#endif
-EditingMode EditTools::currentEditingMode() const
-{
-	return this->drawState.mode;
-}
-#if 0
 void EditTools::closeShape()
 {
-	if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4)
-	{
-		switch (this->drawState.polygon.size())
-		{
+	if (this->polygon.size() >= 2 and this->polygon.size() <= 4) {
+		switch (this->polygon.size()) {
 		case 2:
 			Q_EMIT this->modelAction(AppendToModel{
 				.newElement = Colored<LineSegment>{
 					LineSegment{
-						.p1 = this->drawState.polygon[0],
-						.p2 = this->drawState.polygon[1],
+						.p1 = this->polygon[0],
+						.p2 = this->polygon[1],
 					},
 					EDGE_COLOR,
 				}
@@ -193,9 +266,9 @@
 			Q_EMIT this->modelAction(AppendToModel{
 				.newElement = Colored<Triangle>{
 					Triangle{
-						.p1 = this->drawState.polygon[0],
-						.p2 = this->drawState.polygon[1],
-						.p3 = this->drawState.polygon[2],
+						.p1 = this->polygon[0],
+						.p2 = this->polygon[1],
+						.p3 = this->polygon[2],
 					},
 					MAIN_COLOR,
 				}
@@ -205,10 +278,10 @@
 			Q_EMIT this->modelAction(AppendToModel{
 				.newElement = Colored<Quadrilateral>{
 					Quadrilateral{
-						.p1 = this->drawState.polygon[0],
-						.p2 = this->drawState.polygon[1],
-						.p3 = this->drawState.polygon[2],
-						.p4 = this->drawState.polygon[3],
+						.p1 = this->polygon[0],
+						.p2 = this->polygon[1],
+						.p3 = this->polygon[2],
+						.p4 = this->polygon[3],
 					},
 					MAIN_COLOR,
 				}
@@ -216,8 +289,6 @@
 			break;
 		}
 	}
-	this->drawState.polygon.clear();
-	updatePreviewPolygon(&this->drawState);
+	this->polygon.clear();
+	this->updatePreviewPolygon();
 }
-
-#endif

mercurial