Added polar grid rendering (which is disabled for now).

Sat, 04 Mar 2017 00:54:46 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Sat, 04 Mar 2017 00:54:46 +0200
changeset 1181
ca6d0ef9aadb
parent 1180
2005e4147ad6
child 1182
813d020f92d4

Added polar grid rendering (which is disabled for now).

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/grid.cpp file | annotate | diff | comparison | revisions
src/grid.h file | annotate | diff | comparison | revisions
--- a/src/basics.cpp	Fri Mar 03 23:23:28 2017 +0200
+++ b/src/basics.cpp	Sat Mar 04 00:54:46 2017 +0200
@@ -280,3 +280,83 @@
 
 	return lines;
 }
+
+/*
+ * Computes the shortest distance from a point to a rectangle.
+ *
+ * The code originates from the Unity3D wiki, and was translated from C# to Qt by me (Teemu Piippo):
+ *
+ * Original code:
+ *     http://wiki.unity3d.com/index.php/Distance_from_a_point_to_a_rectangle
+ *
+ * Copyright 2013 Philip Peterson.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+qreal distanceFromPointToRectangle(const QPointF& point, const QRectF& rectangle)
+{
+	//  Calculate a distance between a point and a rectangle.
+	//  The area around/in the rectangle is defined in terms of
+	//  several regions:
+	//
+	//  O--x
+	//  |
+	//  y
+	//
+	//
+	//        I   |    II    |  III
+	//      ======+==========+======   --yMin
+	//       VIII |  IX (in) |  IV
+	//      ======+==========+======   --yMax
+	//       VII  |    VI    |   V
+	//
+	//
+	//  Note that the +y direction is down because of Unity's GUI coordinates.
+
+	if (point.x() < rectangle.left())
+	{
+		// Region I, VIII, or VII
+		if (point.y() < rectangle.top()) // I
+			return QLineF {point, rectangle.topLeft()}.length();
+		else if (point.y() > rectangle.bottom()) // VII
+			return QLineF {point, rectangle.bottomLeft()}.length();
+		else // VIII
+			return rectangle.left() - point.x();
+	}
+	else if (point.x() > rectangle.right())
+	{
+		// Region III, IV, or V
+		if (point.y() < rectangle.top()) // III
+			return QLineF {point, rectangle.topRight()}.length();
+		else if (point.y() > rectangle.bottom()) // V
+			return QLineF {point, rectangle.bottomRight()}.length();
+		else // IV
+			return point.x() - rectangle.right();
+	}
+	else
+	{
+		// Region II, IX, or VI
+		if (point.y() < rectangle.top()) // II
+			return rectangle.top() - point.y();
+		else if (point.y() > rectangle.bottom()) // VI
+			return point.y() - rectangle.bottom();
+		else // IX
+			return 0;
+	}
+}
--- a/src/basics.h	Fri Mar 03 23:23:28 2017 +0200
+++ b/src/basics.h	Sat Mar 04 00:54:46 2017 +0200
@@ -202,6 +202,7 @@
 
 double getRadialPoint(int segment, int divisions, double(*func)(double));
 QVector<QLineF> makeCircle(int segments, int divisions, double radius);
+qreal distanceFromPointToRectangle(const QPointF& point, const QRectF& rectangle);
 
 /*
  * Implements a ring adapter over T. This class corrects indices given to the element-operator so that they're within bounds.
@@ -327,3 +328,57 @@
 	else
 		return x / qAbs(x);
 }
+
+/*
+ * Returns the maximum of a single parameter (the parameter itself).
+ */
+template <typename T>
+T max(T a)
+{
+	return a;
+}
+
+/*
+ * Returns the maximum of two parameters.
+ */
+template <typename T>
+T max(T a, T b)
+{
+	return a > b ? a : b;
+}
+
+/*
+ * Returns the maximum of n parameters.
+ */
+template <typename T, typename... Rest>
+T max(T a, Rest&&... rest)
+{
+	return max(a, max(rest...));
+}
+
+/*
+ * Returns the minimum of a single parameter (the parameter itself).
+ */
+template <typename T>
+T min(T a)
+{
+	return a;
+}
+
+/*
+ * Returns the minimum of two parameters.
+ */
+template <typename T>
+T min(T a, T b)
+{
+	return a < b ? a : b;
+}
+
+/*
+ * Returns the minimum of n parameters.
+ */
+template <typename T, typename... Rest>
+T min(T a, Rest&&... rest)
+{
+	return min(a, min(rest...));
+}
--- a/src/canvas.cpp	Fri Mar 03 23:23:28 2017 +0200
+++ b/src/canvas.cpp	Sat Mar 04 00:54:46 2017 +0200
@@ -95,47 +95,113 @@
  */
 void Canvas::drawFixedCameraBackdrop()
 {
+	static const enum { Cartesian, Polar } gridType = Cartesian;
+
 	// Find the top left corner of the grid
 	Vertex topLeft = currentCamera().idealize(currentCamera().convert2dTo3d({0, 0}));
 	Vertex bottomRight = currentCamera().idealize(currentCamera().convert2dTo3d({width(), height()}));
 	qreal gridSize = grid()->coordinateSnap();
-	qreal x0 = sign(topLeft.x()) * (fabs(topLeft.x()) - fmod(fabs(topLeft.x()), gridSize));
-	qreal y0 = sign(topLeft.y()) * (fabs(topLeft.y()) - fmod(fabs(topLeft.y()), gridSize));
 	glEnable(GL_LINE_STIPPLE);
 	glBegin(GL_LINES);
 
-	static const auto prepareGridLine = [](qreal value) -> bool
+	if (gridType == Cartesian)
 	{
-		if (not isZero(value))
+		qreal x0 = sign(topLeft.x()) * (fabs(topLeft.x()) - fmod(fabs(topLeft.x()), gridSize));
+		qreal y0 = sign(topLeft.y()) * (fabs(topLeft.y()) - fmod(fabs(topLeft.y()), gridSize));
+
+		static const auto prepareGridLine = [](qreal value) -> bool
 		{
-			if (isZero(fmod(value, 10.0)))
-				glColor4f(0, 0, 0, 0.6);
+			if (not isZero(value))
+			{
+				if (isZero(fmod(value, 10.0)))
+					glColor4f(0, 0, 0, 0.6);
+				else
+					glColor4f(0, 0, 0, 0.25);
+
+				return true;
+			}
 			else
-				glColor4f(0, 0, 0, 0.25);
+			{
+				return false;
+			}
+		};
 
-			return true;
+		for (qreal x = x0; x < bottomRight.x(); x += gridSize)
+		{
+			if (prepareGridLine(x))
+			{
+				glVertex(currentCamera().realize({x, -10000, 999}));
+				glVertex(currentCamera().realize({x, 10000, 999}));
+			}
 		}
-		else
+
+		for (qreal y = y0; y < bottomRight.y(); y += gridSize)
 		{
-			return false;
-		}
-	};
-
-	for (qreal x = x0; x < bottomRight.x(); x += gridSize)
-	{
-		if (prepareGridLine(x))
-		{
-			glVertex(currentCamera().realize({x, -10000, 999}));
-			glVertex(currentCamera().realize({x, 10000, 999}));
+			if (prepareGridLine(y))
+			{
+				glVertex(currentCamera().realize({-10000, y, 999}));
+				glVertex(currentCamera().realize({10000, y, 999}));
+			}
 		}
 	}
-
-	for (qreal y = y0; y < bottomRight.y(); y += gridSize)
+	else
 	{
-		if (prepareGridLine(y))
+		const QPointF pole = grid()->pole();
+		const qreal size = grid()->coordinateSnap();
+		Vertex topLeft = currentCamera().idealize(currentCamera().convert2dTo3d({0, 0}));
+		Vertex bottomRight = currentCamera().idealize(currentCamera().convert2dTo3d({width(), height()}));
+		QPointF topLeft2d {topLeft.x(), topLeft.y()};
+		QPointF bottomLeft2d {topLeft.x(), bottomRight.y()};
+		QPointF bottomRight2d {bottomRight.x(), bottomRight.y()};
+		QPointF topRight2d {bottomRight.x(), topLeft.y()};
+		qreal smallestRadius = distanceFromPointToRectangle(pole, QRectF{topLeft2d, bottomRight2d});
+		qreal largestRadius = max(QLineF {topLeft2d, pole}.length(),
+		                          QLineF {bottomLeft2d, pole}.length(),
+		                          QLineF {bottomRight2d, pole}.length(),
+		                          QLineF {topRight2d, pole}.length());
+
+		// Snap the radii to the grid.
+		smallestRadius = round(smallestRadius / size) * size;
+		largestRadius = round(largestRadius / size) * size;
+
+		// Is the pole at (0, 0)? If so, then don't render the polar axes above the real ones.
+		bool poleIsOrigin = isZero(pole.x()) and isZero(pole.y());
+		glColor4f(0, 0, 0, 0.25);
+
+		// Render the axes
+		for (int i = 0; i < grid()->polarDivisions() / 2; ++i)
 		{
-			glVertex(currentCamera().realize({-10000, y, 999}));
-			glVertex(currentCamera().realize({10000, y, 999}));
+			qreal azimuth = (2.0 * pi) * i / grid()->polarDivisions();
+
+			if (not poleIsOrigin or not isZero(fmod(azimuth, pi / 4)))
+			{
+				QPointF extremum = {cos(azimuth) * 10000, sin(azimuth) * 10000};
+				QPointF A = pole + extremum;
+				QPointF B = pole - extremum;
+				glVertex(currentCamera().realize({A.x(), A.y(), 999}));
+				glVertex(currentCamera().realize({B.x(), B.y(), 999}));
+			}
+		}
+
+		for (qreal radius = smallestRadius; radius <= largestRadius; radius += size)
+		{
+			if (not isZero(radius))
+			{
+				Vertex points[48];
+
+				for (int i = 0; i < grid()->polarDivisions(); ++i)
+				{
+					qreal azimuth = (2.0 * pi) * i / grid()->polarDivisions();
+					QPointF point = pole + QPointF {radius * cos(azimuth), radius * sin(azimuth)};
+					points[i] = currentCamera().realize({point.x(), point.y(), 999});
+				}
+
+				for (int i = 0; i < grid()->polarDivisions(); ++i)
+				{
+					glVertex(points[i]);
+					glVertex(ring(points, grid()->polarDivisions())[i + 1]);
+				}
+			}
 		}
 	}
 
--- a/src/grid.cpp	Fri Mar 03 23:23:28 2017 +0200
+++ b/src/grid.cpp	Sat Mar 04 00:54:46 2017 +0200
@@ -72,3 +72,28 @@
 	double size = coordinateSnap();
 	return {round(point.x() / size) * size, round(point.y() / size) * size};
 }
+
+/*
+ * Returns the pole of the grid, in ideal X/Y co-ordinates. Z is left up for the caller to decide.
+ */
+QPointF Grid::pole() const
+{
+	return {12, -17};
+}
+
+/*
+ * Returns the amount of divisions (slices) to be used in the polar grid.
+ */
+int Grid::polarDivisions() const
+{
+	switch (m_config->grid())
+	{
+	default:
+	case Coarse:
+	case Medium:
+		return LowResolution;
+
+	case Fine:
+		return HighResolution;
+	}
+}
--- a/src/grid.h	Fri Mar 03 23:23:28 2017 +0200
+++ b/src/grid.h	Sat Mar 04 00:54:46 2017 +0200
@@ -41,6 +41,8 @@
 	qreal angleAsRadians() const;
 	int bezierCurveSegments() const;
 	qreal coordinateSnap() const;
+	QPointF pole() const;
+	int polarDivisions() const;
 	QPointF snap(QPointF point) const;
 };
 

mercurial