refactor cylinder generation

Mon, 26 Mar 2018 15:28:33 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Mon, 26 Mar 2018 15:28:33 +0300
changeset 1347
7dd217202126
parent 1346
c4cb7dc850aa
child 1348
ecad65ee47f2

refactor cylinder generation

src/primitives.cpp file | annotate | diff | comparison | revisions
src/primitives.h file | annotate | diff | comparison | revisions
--- a/src/primitives.cpp	Sun Mar 25 16:57:28 2018 +0300
+++ b/src/primitives.cpp	Mon Mar 26 15:28:33 2018 +0300
@@ -25,6 +25,7 @@
 #include "documentmanager.h"
 #include "editHistory.h"
 #include "algorithms/geometry.h"
+#include "generics/ring.h"
 #include "linetypes/comment.h"
 #include "linetypes/conditionaledge.h"
 #include "linetypes/edgeline.h"
@@ -257,8 +258,79 @@
 	categoriesFile.close();
 }
 
+// Length of a single LDraw edge circle segment. Ideally, it is sqrt(2 - 2 * cos(π / 8)), but
+// rounding errors come into play so it's a tiny bit larger than that.
+// This actual value is given by: hypot(0.0761, 0.3827)
+static const double chordLength = 0.3901929010117944;
+
+void PrimitiveModel::generateCylinder(Model& model) const
+{
+	Q_ASSERT(this->type == Cylinder);
+	auto circle = makeCircle(this->segments, this->divisions, 1);
+	bool useTangents = (this->segments != this->divisions);
+
+	QPointF tangent_1;
+	QPointF tangent_2;
+
+	if (useTangents)
+	{
+		tangent_1 = {1.0, -chordLength};
+
+		double angle = double(segments) * 2 * pi / double(divisions) + (pi / 2);
+		tangent_2 = {
+			circle.last().p2().x() + chordLength * cos(angle),
+			circle.last().p2().y() + chordLength * sin(angle),
+		};
+	}
+
+	for (int i = 0; i < this->segments; i += 1)
+	{
+		double x0 = circle[i].x1();
+		double x1 = circle[i].x2();
+		double z0 = circle[i].y1();
+		double z1 = circle[i].y2();
+
+		LDQuadrilateral* quad = model.emplace<LDQuadrilateral>(
+			Vertex {x1, 0.0, z1},
+			Vertex {x0, 0.0, z0},
+			Vertex {x0, 1.0, z0},
+			Vertex {x1, 1.0, z1}
+		);
+		quad->setColor(MainColor);
+	}
+
+	for (int i = 0; i < this->segments + 1; i += 1)
+	{
+		QPointF p0 = ::pointOnLDrawCircumference(i, divisions);
+		QPointF p2 = ::pointOnLDrawCircumference(i - 1, divisions);
+		QPointF p3 = ::pointOnLDrawCircumference(i + 1, divisions);
+
+		if (useTangents and i == 0)
+			p2 = tangent_1;
+		else if (useTangents and i == this->segments)
+			p3 = tangent_2;
+
+		Vertex v0 = {p0.x(), 1.0, p0.y()};
+		Vertex v1 = {v0.x, 0.0, v0.z};
+		Vertex v2 = {p2.x(), 1.0, p2.y()};
+		Vertex v3 = {p3.x(), 1.0, p3.y()};
+		LDConditionalEdge* line = model.emplace<LDConditionalEdge>();
+		line->setColor(EdgeColor);
+		line->setVertex(0, v0);
+		line->setVertex(1, v1);
+		line->setVertex(2, v2);
+		line->setVertex(3, v3);
+	}
+}
+
 void PrimitiveModel::generateBody(Model& model) const
 {
+	if (this->type == Cylinder)
+	{
+		this->generateCylinder(model);
+		return;
+	}
+
 	QVector<int> conditionalLineSegments;
 	QVector<QLineF> circle = makeCircle(segments, divisions, 1);
 
@@ -280,51 +352,35 @@
 			}
 			break;
 
-		case Cylinder:
 		case Ring:
 		case Cone:
 			{
 				double x2, x3, z2, z3;
 				double y0, y1, y2, y3;
+				x2 = x1 * (ringNumber + 1);
+				x3 = x0 * (ringNumber + 1);
+				z2 = z1 * (ringNumber + 1);
+				z3 = z0 * (ringNumber + 1);
+				x0 *= ringNumber;
+				x1 *= ringNumber;
+				z0 *= ringNumber;
+				z1 *= ringNumber;
 
-				if (type == Cylinder)
+				if (type == Ring)
 				{
-					x2 = x1;
-					x3 = x0;
-					z2 = z1;
-					z3 = z0;
-					y0 = y1 = 0.0;
-					y2 = y3 = 1.0;
+					y0 = y1 = y2 = y3 = 0.0;
 				}
 				else
 				{
-					x2 = x1 * (ringNumber + 1);
-					x3 = x0 * (ringNumber + 1);
-					z2 = z1 * (ringNumber + 1);
-					z3 = z0 * (ringNumber + 1);
-					x0 *= ringNumber;
-					x1 *= ringNumber;
-					z0 *= ringNumber;
-					z1 *= ringNumber;
-
-					if (type == Ring)
-					{
-						y0 = y1 = y2 = y3 = 0.0;
-					}
-					else
-					{
-						y0 = y1 = 1.0;
-						y2 = y3 = 0.0;
-					}
+					y0 = y1 = 1.0;
+					y2 = y3 = 0.0;
 				}
 
 				Vertex v0 = {x0, y0, z0};
 				Vertex v1 = {x1, y1, z1};
 				Vertex v2 = {x2, y2, z2};
 				Vertex v3 = {x3, y3, z3};
-
-				if (type == Cylinder || type == Ring)
-					qSwap(v1, v3);
+				qSwap(v1, v3);
 
 				LDQuadrilateral* quad = model.emplace<LDQuadrilateral>(v0, v1, v2, v3);
 				quad->setColor(MainColor);
@@ -375,23 +431,13 @@
 		QPointF p0 = ::pointOnLDrawCircumference(i, divisions);
 		QPointF p2 = ::pointOnLDrawCircumference(i - 1, divisions);
 		QPointF p3 = ::pointOnLDrawCircumference(i + 1, divisions);
-		Vertex v0 = {p0.x(), 0.0, p0.y()};
-		Vertex v1;
+		Vertex v0 = {p0.x() * ringNumber, 1.0, p0.y() * ringNumber};
+		Vertex v1 = {v0.x * (ringNumber + 1), 0.0, v0.z * (ringNumber + 1)};
 		Vertex v2 = {p2.x(), 0.0, p2.y()};
 		Vertex v3 = {p3.x(), 0.0, p3.y()};
-
-		if (type == Cylinder)
-		{
-			v1 = {v0.x, 1.0f, v0.z};
-		}
-		else if (type == Cone)
-		{
-			v1 = {v0.x * (ringNumber + 1), 0.0, v0.z * (ringNumber + 1)};
-			v0 = {v0.x * ringNumber, 1.0, v0.z * ringNumber};
-			v2 *= ringNumber;
-			v3 *= ringNumber;
-			v2.y = v3.y = 1.0;
-		}
+		v2 *= ringNumber;
+		v3 *= ringNumber;
+		v2.y = v3.y = 1.0;
 
 		LDConditionalEdge* line = model.emplace<LDConditionalEdge>();
 		line->setColor(EdgeColor);
--- a/src/primitives.h	Sun Mar 25 16:57:28 2018 +0300
+++ b/src/primitives.h	Mon Mar 26 15:28:33 2018 +0300
@@ -57,6 +57,7 @@
 
 	QString typeName() const;
 	void generateBody(Model& model) const;
+	void generateCylinder(Model& model) const;
 	static QString typeName(Type type);
 	QString makeFileName(FilenameStyle style) const;
 };

mercurial