added draw plane feature (doesn't work with circle draw quite right yet)

Wed, 30 May 2018 22:31:06 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 30 May 2018 22:31:06 +0300
changeset 1390
3eace926af7f
parent 1389
3fb8ad4d27b1
child 1391
5fa4bf1fc781

added draw plane feature (doesn't work with circle draw quite right yet)

CMakeLists.txt file | annotate | diff | comparison | revisions
src/basics.cpp file | annotate | diff | comparison | revisions
src/basics.h file | annotate | diff | comparison | revisions
src/canvas.cpp file | annotate | diff | comparison | revisions
src/canvas.h file | annotate | diff | comparison | revisions
src/editmodes/abstractEditMode.cpp file | annotate | diff | comparison | revisions
src/editmodes/abstractEditMode.h file | annotate | diff | comparison | revisions
src/editmodes/circleMode.cpp file | annotate | diff | comparison | revisions
src/editmodes/rectangleMode.cpp file | annotate | diff | comparison | revisions
src/generics/functions.h file | annotate | diff | comparison | revisions
src/geometry/linesegment.h file | annotate | diff | comparison | revisions
src/geometry/plane.cpp file | annotate | diff | comparison | revisions
src/geometry/plane.h file | annotate | diff | comparison | revisions
src/linetypes/quadrilateral.cpp file | annotate | diff | comparison | revisions
src/linetypes/quadrilateral.h file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/mainwindow.ui file | annotate | diff | comparison | revisions
src/toolsets/viewtoolset.cpp file | annotate | diff | comparison | revisions
src/toolsets/viewtoolset.h file | annotate | diff | comparison | revisions
src/types/matrix.cpp file | annotate | diff | comparison | revisions
src/types/matrix.h file | annotate | diff | comparison | revisions
src/types/vertex.cpp file | annotate | diff | comparison | revisions
src/types/vertex.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sat May 26 17:28:15 2018 +0300
+++ b/CMakeLists.txt	Wed May 30 22:31:06 2018 +0300
@@ -76,6 +76,7 @@
 	src/editmodes/rectangleMode.cpp
 	src/editmodes/selectMode.cpp
 	src/geometry/linesegment.cpp
+	src/geometry/plane.cpp
 	src/linetypes/comment.cpp
 	src/linetypes/conditionaledge.cpp
 	src/linetypes/edgeline.cpp
@@ -153,6 +154,7 @@
 	src/generics/ring.h
 	src/generics/transform.h
 	src/geometry/linesegment.h
+	src/geometry/plane.h
 	src/linetypes/comment.h
 	src/linetypes/conditionaledge.h
 	src/linetypes/edgeline.h
--- a/src/basics.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/basics.cpp	Wed May 30 22:31:06 2018 +0300
@@ -192,3 +192,70 @@
 	function();
 	object->blockSignals(wasBlockingSignals);
 }
+
+/*
+ * Returns the angle between two vectors
+ */
+qreal vectorAngle(const QVector3D& vec_1, const QVector3D& vec_2)
+{
+	return acos(QVector3D::dotProduct(vec_1, vec_2) / vec_1.length() / vec_2.length());
+}
+
+/*
+ * Computes the determinant of a 2×2 matrix with each variable passed in row-major order.
+ */
+qreal determinant(qreal a, qreal b, qreal c, qreal d)
+{
+	return a * d - b * c;
+}
+
+/*
+ * Computes the determinant of a 3×3 matrix with each variable passed in row-major order.
+ */
+qreal determinant(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f, qreal g, qreal h, qreal i)
+{
+	return a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g;
+}
+
+/*
+ * Computes the determinant of a 2×2 matrix.
+ */
+qreal determinant(const QMatrix2x2& matrix)
+{
+	return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0);
+}
+
+/*
+ * Computes the determinant of a 3×3 matrix.
+ */
+qreal determinant(const QMatrix3x3& matrix)
+{
+	return sum(
+		+matrix(0, 0) * matrix(1, 1) * matrix(2, 2),
+		-matrix(0, 0) * matrix(1, 2) * matrix(2, 1),
+		-matrix(0, 1) * matrix(1, 0) * matrix(2, 2),
+		+matrix(0, 1) * matrix(1, 2) * matrix(2, 0),
+		+matrix(0, 2) * matrix(1, 0) * matrix(2, 1),
+		-matrix(0, 2) * matrix(1, 1) * matrix(2, 0));
+}
+
+/*
+ * Computes the determinant of a 4×4 matrix.
+ */
+qreal determinant(const QMatrix4x4& matrix)
+{
+	qreal sum = 0;
+
+	for (int column : {0, 1, 2, 3})
+	{
+		int column_1 = (column >= 1) ? 0 : 1;
+		int column_2 = (column >= 2) ? 1 : 2;
+		int column_3 = (column >= 3) ? 2 : 3;
+		sum += ((column % 1) ? -1 : 1) * determinant(
+			matrix(1, column_1), matrix(1, column_2), matrix(1, column_3),
+			matrix(2, column_1), matrix(2, column_2), matrix(2, column_3),
+			matrix(3, column_1), matrix(3, column_2), matrix(3, column_3));
+	}
+
+	return sum;
+}
--- a/src/basics.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/basics.h	Wed May 30 22:31:06 2018 +0300
@@ -89,6 +89,11 @@
 	return vector.length();
 }
 
+qreal determinant(qreal a, qreal b, qreal c, qreal d);
+qreal determinant(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f, qreal g, qreal h, qreal i);
+qreal determinant(const QMatrix2x2& matrix);
+qreal determinant(const QMatrix3x3& matrix);
+qreal determinant(const QMatrix4x4& matrix);
 QString formatFileSize(qint64 size);
 int gcd(int a, int b);
 QString joinStrings(const QList<class StringFormatArg>& values, QString delimeter = " ");
@@ -100,4 +105,5 @@
 QString superscript(int number);
 QString subscript(int number);
 QString fractionRep(int numerator, int denominator);
+qreal vectorAngle(const QVector3D& vec_1, const QVector3D& vec_2);
 void withSignalsBlocked(QObject* object, std::function<void()> function);
--- a/src/canvas.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/canvas.cpp	Wed May 30 22:31:06 2018 +0300
@@ -347,20 +347,16 @@
 
 // =============================================================================
 //
-void Canvas::setDepthValue (double depth)
+void Canvas::setDrawPlane(const Plane& plane)
 {
-	if (camera() < Camera::Free)
-		m_depthValues[static_cast<int>(camera())] = depth;
+	m_drawPlane = plane;
 }
 
 // =============================================================================
 //
-double Canvas::getDepthValue() const
+const Plane& Canvas::drawPlane() const
 {
-	if (camera() < Camera::Free)
-		return m_depthValues[static_cast<int>(camera())];
-	else
-		return 0.0;
+	return m_drawPlane;
 }
 
 void Canvas::contextMenuEvent(QContextMenuEvent* event)
--- a/src/canvas.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/canvas.h	Wed May 30 22:31:06 2018 +0300
@@ -19,6 +19,7 @@
 #pragma once
 #include "glrenderer.h"
 #include "editmodes/abstractEditMode.h"
+#include "geometry/plane.h"
 
 class Canvas : public GLRenderer
 {
@@ -27,6 +28,7 @@
 	~Canvas();
 
 	EditModeType currentEditModeType() const;
+	const Plane& drawPlane() const;
 	int depthNegateFactor() const;
 	LDDocument* document() const;
 	void drawPoint(QPainter& painter, QPointF pos, QColor color = QColor (64, 192, 0)) const;
@@ -34,12 +36,11 @@
 	void drawBlipCoordinates(QPainter& painter, const Vertex& pos3d, QPointF pos) const;
 	void clearCurrentCullValue();
 	double currentCullValue() const;
-	double getDepthValue() const;
 	void getRelativeAxes(Axis& relX, Axis& relY) const;
 	Axis getRelativeZ() const;
 	QPen linePen() const;
 	const Vertex& position3D() const;
-	void setDepthValue(double depth);
+	void setDrawPlane(const Plane& plane);
 	void setCullValue(double value);
 	void setEditMode(EditModeType type);
 
@@ -60,6 +61,6 @@
 	LDDocument& m_document;
 	AbstractEditMode* m_currentEditMode = nullptr;
 	Vertex m_position3D;
-	double m_depthValues[6] = {0};
+	Plane m_drawPlane;
 	double cullValues[6] = {0};
 };
--- a/src/editmodes/abstractEditMode.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/editmodes/abstractEditMode.cpp	Wed May 30 22:31:06 2018 +0300
@@ -314,7 +314,7 @@
 		result.setCoordinate(relativeY, point.y());
 	}
 
-	return result;
+	return projectToDrawPlane(result);
 }
 
 /*
@@ -349,3 +349,23 @@
  * TODO: the two method names are too similar and should be renamed.
  */
 void AbstractDrawMode::endDraw() {}
+
+/*
+ * Sets the depth value of the vertex so that it's in the draw plane.
+ */
+Vertex AbstractDrawMode::projectToDrawPlane(const Vertex& vertex) const
+{
+	Axis localx, localy;
+	renderer()->getRelativeAxes(localx, localy);
+	Axis localz = renderer()->getRelativeZ();
+	Line line;
+	line.v_1 = line.v_2 = vertex;
+	line.v_1[localz] = 1;
+	line.v_2[localz] = -1;
+	Plane::IntersectionResult intersection = renderer()->drawPlane().intersection(line);
+
+	if (intersection.intersected)
+		return intersection.vertex;
+	else
+		return vertex;
+}
--- a/src/editmodes/abstractEditMode.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/editmodes/abstractEditMode.h	Wed May 30 22:31:06 2018 +0300
@@ -95,6 +95,7 @@
 	virtual int maxVertices() const;
 	bool mouseReleased(const AbstractEditMode::MouseEventData& data) override;
 	virtual bool preAddVertex(Vertex const&);
+	Vertex projectToDrawPlane(const Vertex& vertex) const;
 	void renderPolygon(QPainter& painter, const QVector<Vertex>& poly3d, bool withlengths, bool withangles) const;
 };
 
--- a/src/editmodes/circleMode.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/editmodes/circleMode.cpp	Wed May 30 22:31:06 2018 +0300
@@ -60,6 +60,28 @@
 	return 0.0;
 }
 
+static Matrix shearMatrixForPlane(Canvas* renderer)
+{
+	const Plane& plane = renderer->drawPlane();
+	Axis localx, localy;
+	renderer->getRelativeAxes(localx, localy);
+	Axis localz = renderer->getRelativeZ();
+	Matrix shearMatrix = Matrix::identity;
+	Vertex normalAsVertex = Vertex::fromVector(plane.normal);
+
+	// Compute shear matrix values. The (Y, X) cell means a slope for Y in regard to X.
+	// If x grows by 2, y grows by 2 times this value. In the circle primitives,
+	// depth is Y, but in the orthogonal view, depth is Z. So Y and Z must be swapped.
+	if (not qFuzzyCompare(normalAsVertex[localz], 0.0))
+	{
+		// The slope of the vector is 90° offset from the normal vector. So Y/X slope is
+		// -X/Y from the normal vector.
+		shearMatrix(Y, X) = -normalAsVertex[localx] / normalAsVertex[localz];
+		shearMatrix(Y, Z) = -normalAsVertex[localy] / normalAsVertex[localz];
+	}
+
+	return shearMatrix;
+}
 
 void CircleMode::endDraw()
 {
@@ -72,28 +94,33 @@
 	double dist1 (getCircleDrawDist (1));
 	LDDocument* primitiveFile;
 	Matrix transform;
+
 	bool circleOrDisc = false;
 
 	if (dist1 < dist0)
 		qSwap(dist0, dist1);
 
-	if (dist0 == dist1)
+	if (qFuzzyCompare(dist0, dist1))
 	{
-		// If the radii are the same, there's no ring space to fill. Use a circle.
+		// Special case: radii are the same, there's no area. Use a circle.
 		primitiveModel.type = PrimitiveModel::Circle;
 		primitiveFile = primitives()->getPrimitive(primitiveModel);
-		transform = Matrix::fromRotationMatrix(renderer()->currentCamera().transformationMatrix(dist0));
+		// transform = shearMatrixForPlane(renderer());
+		transform = Matrix::fromQMatrix(renderer()->currentCamera().transformationMatrix(1));
+		transform *= Matrix::scaleMatrix(dist0);
 		circleOrDisc = true;
 	}
-	else if (dist0 == 0 or dist1 == 0)
+	else if (qFuzzyCompare(dist0, 0) or qFuzzyCompare(dist1, 0))
 	{
-		// If either radii is 0, use a disc.
+		// Special case #2: one radius is 0, so use a disc.
 		primitiveModel.type = PrimitiveModel::Disc;
 		primitiveFile = primitives()->getPrimitive(primitiveModel);
-		transform = Matrix::fromRotationMatrix(renderer()->currentCamera().transformationMatrix((dist0 != 0) ? dist0 : dist1));
+		//transform = shearMatrixForPlane(renderer());
+		transform = Matrix::fromQMatrix(renderer()->currentCamera().transformationMatrix(1));
+		transform *= Matrix::scaleMatrix(max(dist0, dist1));
 		circleOrDisc = true;
 	}
-	else if (g_RingFinder.findRings(dist0, dist1))
+	else if (g_RingFinder.findRings(dist0, dist1)) // Consult the ring finder now
 	{
 		// The ring finder found a solution, use that. Add the component rings to the file.
 		primitiveModel.type = PrimitiveModel::Ring;
@@ -102,7 +129,8 @@
 		{
 			primitiveModel.ringNumber = component.num;
 			primitiveFile = primitives()->getPrimitive(primitiveModel);
-			Matrix matrix = Matrix::fromRotationMatrix(renderer()->currentCamera().transformationMatrix(component.scale));
+			Matrix matrix = Matrix::fromQMatrix(renderer()->currentCamera().transformationMatrix(component.scale));
+			// matrix = shearMatrixForPlane(renderer()) * matrix;
 			model.emplace<LDSubfileReference>(primitiveFile->name(), matrix, m_drawedVerts.first());
 		}
 	}
@@ -118,7 +146,6 @@
 		Vertex templ;
 		templ.setCoordinate(localx, x0);
 		templ.setCoordinate(localy, y0);
-		templ.setCoordinate(localz, renderer()->getDepthValue());
 
 		// Calculate circle coords
 		QVector<QLineF> c0 = makeCircle(primitiveModel.segments, primitiveModel.divisions, dist0);
@@ -141,13 +168,19 @@
 			if (static_cast<int>(renderer()->camera()) % 3 <= 0)
 				qSwap(v1, v3);
 
+			// Project the vertices onto the draw plane.
+			for (Vertex* vertex : {&v0, &v1, &v2, &v3})
+				*vertex = projectToDrawPlane(*vertex);
+
 			LDQuadrilateral* quad = model.emplace<LDQuadrilateral>(v0, v1, v2, v3);
 			quad->setColor(MainColor);
 		}
 	}
 
 	if (circleOrDisc and primitiveFile)
+	{
 		model.emplace<LDSubfileReference>(primitiveFile->name(), transform, m_drawedVerts.first());
+	}
 
 	finishDraw (model);
 }
--- a/src/editmodes/rectangleMode.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/editmodes/rectangleMode.cpp	Wed May 30 22:31:06 2018 +0300
@@ -78,16 +78,16 @@
 	Axis localx, localy, localz;
 	renderer()->getRelativeAxes(localx, localy);
 	localz = renderer()->getRelativeZ();
-
-	for (int i = 0; i < 4; ++i)
-		m_rectangleVerts[i].setCoordinate (localz, renderer()->getDepthValue());
+	m_rectangleVerts[0].setCoordinate(localx, v0[localx]);
+	m_rectangleVerts[0].setCoordinate(localy, v0[localy]);
+	m_rectangleVerts[1].setCoordinate(localx, v1[localx]);
+	m_rectangleVerts[1].setCoordinate(localy, v0[localy]);
+	m_rectangleVerts[2].setCoordinate(localx, v1[localx]);
+	m_rectangleVerts[2].setCoordinate(localy, v1[localy]);
+	m_rectangleVerts[3].setCoordinate(localx, v0[localx]);
+	m_rectangleVerts[3].setCoordinate(localy, v1[localy]);
 
-	m_rectangleVerts[0].setCoordinate (localx, v0[localx]);
-	m_rectangleVerts[0].setCoordinate (localy, v0[localy]);
-	m_rectangleVerts[1].setCoordinate (localx, v1[localx]);
-	m_rectangleVerts[1].setCoordinate (localy, v0[localy]);
-	m_rectangleVerts[2].setCoordinate (localx, v1[localx]);
-	m_rectangleVerts[2].setCoordinate (localy, v1[localy]);
-	m_rectangleVerts[3].setCoordinate (localx, v0[localx]);
-	m_rectangleVerts[3].setCoordinate (localy, v1[localy]);
+	// Compute local z from the draw plane
+	for (int i = 0; i < 4; ++i)
+		m_rectangleVerts[i] = projectToDrawPlane(m_rectangleVerts[i]);
 }
--- a/src/generics/functions.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/generics/functions.h	Wed May 30 22:31:06 2018 +0300
@@ -224,6 +224,24 @@
 	return static_cast<T>(round(value / interval) * interval);
 }
 
+/*
+ * Returns the empty sum. (recursion base)
+ */
+template<typename T>
+T sum()
+{
+	return {};
+}
+
+/*
+ * Returns the sum of n arguments.
+ */
+template<typename T, typename... Rest>
+T sum(const T& arg, Rest&&... rest)
+{
+	return arg + sum<T>(rest...);
+}
+
 // Copy of qOverload so as to drop Qt version requirement from 5.7 to 5.5.
 #if (QT_VERSION < QT_VERSION_CHECK(5, 7, 0))
 template <typename... Args>
--- a/src/geometry/linesegment.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/geometry/linesegment.h	Wed May 30 22:31:06 2018 +0300
@@ -35,3 +35,5 @@
 unsigned int qHash(const LineSegment& segment);
 bool operator==(const LineSegment& one, const LineSegment& other);
 bool operator<(const LineSegment& one, const LineSegment& other);
+
+using Line = LineSegment;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/geometry/plane.cpp	Wed May 30 22:31:06 2018 +0300
@@ -0,0 +1,73 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2018 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 "plane.h"
+
+/*
+ * Constructs a plane from three points
+ */
+Plane Plane::fromPoints(const Vertex& v1, const Vertex& v2, const Vertex& v3)
+{
+	Plane result;
+	result.normal = QVector3D::crossProduct(v2 - v1, v3 - v1);
+	result.point = v1;
+	return result;
+}
+
+/*
+ * Returns whether or not this plane is valid.
+ */
+bool Plane::isValid() const
+{
+	return not normal.isNull();
+}
+
+/*
+ * Finds the intersection of a line and a plane. Returns a structure containing whether or not
+ * there was an intersection as well as the intersected vertex if there was one. Can return a point
+ * outside of the line segment.
+ *
+ * C.f. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form
+ */
+Plane::IntersectionResult Plane::intersection(const Line& line) const
+{
+	if (isValid())
+	{
+		QVector3D lineVector = line.v_2 - line.v_1;
+		qreal dot = QVector3D::dotProduct(lineVector, normal);
+
+		if (not qFuzzyCompare(dot, 0.0))
+		{
+			qreal factor = QVector3D::dotProduct(point - line.v_1, normal) / dot;
+			IntersectionResult result;
+			result.intersected = true;
+			result.vertex = Vertex::fromVector(lineVector * factor + (line.v_1 - Vertex {0, 0, 0}));
+			return result;
+		}
+		else
+		{
+			// did not intersect
+			return {};
+		}
+	}
+	else
+	{
+		// plane is invalid
+		return {};
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/geometry/plane.h	Wed May 30 22:31:06 2018 +0300
@@ -0,0 +1,42 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2018 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/>.
+ */
+
+#pragma once
+#include "../basics.h"
+#include "../types/vertex.h"
+#include "linesegment.h"
+
+/*
+ * Models a plane in 3D space.
+ */
+struct Plane
+{
+	struct IntersectionResult
+	{
+		bool intersected = false;
+		Vertex vertex;
+	};
+
+	QVector3D normal;
+	Vertex point;
+
+	IntersectionResult intersection(const Line& line) const;
+	bool isValid() const;
+
+	static Plane fromPoints(const Vertex& v1, const Vertex& v2, const Vertex& v3);
+};
--- a/src/linetypes/quadrilateral.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/linetypes/quadrilateral.cpp	Wed May 30 22:31:06 2018 +0300
@@ -55,3 +55,21 @@
 {
 	return LDObjectType::Quadrilateral;
 }
+
+/*
+ * Returns whether or not this quadrilateral is co-planar.
+ */
+bool LDQuadrilateral::isCoPlanar() const
+{
+	return planeAngle() < 0.001745329;
+}
+
+/*
+ * Returns the angle between the two planes in this quadrilateral.
+ */
+qreal LDQuadrilateral::planeAngle() const
+{
+	QVector3D vec_1 = QVector3D::crossProduct(vertex(2) - vertex(1), vertex(0) - vertex(1));
+	QVector3D vec_2 = QVector3D::crossProduct(vertex(0) - vertex(3), vertex(2) - vertex(3));
+	return vectorAngle(vec_1, vec_2);
+}
--- a/src/linetypes/quadrilateral.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/linetypes/quadrilateral.h	Wed May 30 22:31:06 2018 +0300
@@ -31,6 +31,8 @@
 	LDQuadrilateral(const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4);
 
 	QString asText() const override;
+	bool isCoPlanar() const;
+	qreal planeAngle() const;
 	int numVertices() const override;
 	int triangleCount(DocumentManager*) const override;
 	LDObjectType type() const override;
--- a/src/mainwindow.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/mainwindow.cpp	Wed May 30 22:31:06 2018 +0300
@@ -497,7 +497,8 @@
 	if (renderer()->camera() != Camera::Free)
 	{
 		contextMenu->addSeparator();
-		contextMenu->addAction (ui.actionSetDrawDepth);
+		contextMenu->addAction(ui.actionSetDrawPlane);
+		contextMenu->addAction(ui.actionClearDrawPlane);
 		contextMenu->addAction(ui.actionSetCullDepth);
 		contextMenu->addAction(ui.actionClearCullDepth);
 	}
--- a/src/mainwindow.ui	Sat May 26 17:28:15 2018 +0300
+++ b/src/mainwindow.ui	Wed May 30 22:31:06 2018 +0300
@@ -332,7 +332,8 @@
     <addaction name="actionModeCircle"/>
     <addaction name="actionModeLinePath"/>
     <addaction name="separator"/>
-    <addaction name="actionSetDrawDepth"/>
+    <addaction name="actionSetDrawPlane"/>
+    <addaction name="actionClearDrawPlane"/>
     <addaction name="separator"/>
     <addaction name="actionJumpTo"/>
    </widget>
@@ -1029,9 +1030,12 @@
     <string>D</string>
    </property>
   </action>
-  <action name="actionSetDrawDepth">
+  <action name="actionSetDrawPlane">
    <property name="text">
-    <string>Set Draw Dept&amp;h</string>
+    <string>Set draw plane</string>
+   </property>
+   <property name="toolTip">
+    <string>Locks draw tools into a plane defined by a triangle or a coplanar quadrilateral.</string>
    </property>
   </action>
   <action name="actionSetColor">
@@ -1769,6 +1773,14 @@
     <string>Clear cull depth</string>
    </property>
   </action>
+  <action name="actionClearDrawPlane">
+   <property name="text">
+    <string>Clear draw plane</string>
+   </property>
+   <property name="toolTip">
+    <string>Resets the drawing plane</string>
+   </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>
--- a/src/toolsets/viewtoolset.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/toolsets/viewtoolset.cpp	Wed May 30 22:31:06 2018 +0300
@@ -26,6 +26,7 @@
 #include "../colors.h"
 #include "../glcompiler.h"
 #include "../documentmanager.h"
+#include "../linetypes/quadrilateral.h"
 #include "viewtoolset.h"
 
 ViewToolset::ViewToolset (MainWindow *parent) :
@@ -168,28 +169,53 @@
 	m_window->renderer()->update();
 }
 
-void ViewToolset::setDrawDepth()
+/*
+ * If the given object is within a single plane, returns that plane.
+ * Should probably be smarter than this.
+ */
+static Plane drawPlaneFromObject(LDObject* object)
 {
-	if (m_window->renderer()->camera() == Camera::Free)
-		return;
+	switch (object->type())
+	{
+	case LDObjectType::Quadrilateral:
+		if (not static_cast<LDQuadrilateral*>(object)->isCoPlanar())
+			return {};
+	case LDObjectType::Triangle:
+		return Plane::fromPoints(object->vertex(0), object->vertex(1), object->vertex(2));
+
+	default:
+		return {};
+	}
+}
+
+void ViewToolset::setDrawPlane()
+{
+	QSet<LDObject*> objects = selectedObjects();
 
-	bool ok;
-	double depth = QInputDialog::getDouble(
-		m_window,
-		tr("Set draw depth"),
-		format(
-			tr("Depth value for %1:"),
-			m_window->renderer()->currentCamera().name()
-		),
-		m_window->renderer()->getDepthValue(),
-		-10000.0f,
-		10000.0f,
-		4,
-		&ok
-	);
+	if (objects.size() == 1)
+	{
+		LDObject* object = *objects.begin();
+		Plane plane = drawPlaneFromObject(object);
 
-	if (ok)
-		m_window->renderer()->setDepthValue(depth);
+		if (plane.isValid())
+		{
+			m_window->renderer()->setDrawPlane(plane);
+		}
+		else
+		{
+			QMessageBox::critical(
+				m_window,
+				tr("Error"),
+				tr("This object does not define a single plane"),
+				QMessageBox::Ok,
+				QMessageBox::Ok);
+		}
+	}
+}
+
+void ViewToolset::clearDrawPlane()
+{
+	m_window->renderer()->setDrawPlane({});
 }
 
 void ViewToolset::setCullDepth()
--- a/src/toolsets/viewtoolset.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/toolsets/viewtoolset.h	Wed May 30 22:31:06 2018 +0300
@@ -29,6 +29,7 @@
 	Q_INVOKABLE void axes();
 	Q_INVOKABLE void bfcView();
 	Q_INVOKABLE void clearCullDepth();
+	Q_INVOKABLE void clearDrawPlane();
 	Q_INVOKABLE void drawAngles();
 	Q_INVOKABLE void drawConditionalLines();
 	Q_INVOKABLE void drawEdgeLines();
@@ -41,8 +42,8 @@
 	Q_INVOKABLE void selectAll();
 	Q_INVOKABLE void selectByColor();
 	Q_INVOKABLE void selectByType();
+	Q_INVOKABLE void setDrawPlane();
 	Q_INVOKABLE void setCullDepth();
-	Q_INVOKABLE void setDrawDepth();
 	Q_INVOKABLE void visibilityHide();
 	Q_INVOKABLE void visibilityReveal();
 	Q_INVOKABLE void visibilityToggle();
--- a/src/types/matrix.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/types/matrix.cpp	Wed May 30 22:31:06 2018 +0300
@@ -238,13 +238,28 @@
 	return _row;
 }
 
-Matrix Matrix::fromRotationMatrix(const GLRotationMatrix& rotationMatrix)
+Matrix Matrix::fromQMatrix(const QMatrix4x4& matrix)
 {
 	Matrix result;
 
 	for (int i = 0; i < 3; ++i)
 	for (int j = 0; j < 3; ++j)
-		result(i, j) = rotationMatrix(i, j);
+		result(i, j) = matrix(i, j);
 
 	return result;
 }
+
+Matrix Matrix::scaleMatrix(qreal scalar)
+{
+	return {
+		scalar, 0, 0,
+		0, scalar, 0,
+		0, 0, scalar
+	};
+}
+
+Matrix& Matrix::operator*=(const Matrix& other)
+{
+	*this = *this * other;
+	return *this;
+}
--- a/src/types/matrix.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/types/matrix.h	Wed May 30 22:31:06 2018 +0300
@@ -51,9 +51,11 @@
 	ConstRowView operator[](int row) const;
 	double& operator()(int row, int column);
 	const double& operator()(int row, int column) const;
+	Matrix& operator*=(const Matrix& other);
 
 	static const Matrix identity;
-	static Matrix fromRotationMatrix(const GLRotationMatrix& rotationMatrix);
+	static Matrix fromQMatrix(const QMatrix4x4& matrix);
+	static Matrix scaleMatrix(qreal scalar);
 
 private:
 	double m_values[9];
--- a/src/types/vertex.cpp	Sat May 26 17:28:15 2018 +0300
+++ b/src/types/vertex.cpp	Wed May 30 22:31:06 2018 +0300
@@ -92,6 +92,11 @@
 		return ::format("%1 %2 %3", this->x, this->y, this->z);
 }
 
+Vertex Vertex::fromVector(const QVector3D& vector)
+{
+	return {vector.x(), vector.y(), vector.z()};
+}
+
 Vertex Vertex::operator*(qreal scalar) const
 {
 	return {this->x * scalar, this->y * scalar, this->z * scalar};
--- a/src/types/vertex.h	Sat May 26 17:28:15 2018 +0300
+++ b/src/types/vertex.h	Wed May 30 22:31:06 2018 +0300
@@ -48,6 +48,8 @@
 	double operator[](Axis ax) const;
 	bool operator==(const Vertex& other) const;
 	bool operator!=(const Vertex& other) const;
+
+	static Vertex fromVector(const QVector3D& vector);
 };
 
 inline Vertex operator*(qreal scalar, const Vertex& vertex)

mercurial