Add support for BFC CERTIFY statements

Wed, 29 Jun 2022 14:11:58 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Wed, 29 Jun 2022 14:11:58 +0300
changeset 305
d891da20abca
parent 304
5d280bceb713
child 306
6ad27b7d2697

Add support for BFC CERTIFY statements

src/circularprimitive.h file | annotate | diff | comparison | revisions
src/gl/compiler.cpp file | annotate | diff | comparison | revisions
src/layers/edittools.cpp file | annotate | diff | comparison | revisions
src/model.h file | annotate | diff | comparison | revisions
src/polygoncache.cpp file | annotate | diff | comparison | revisions
src/polygoncache.h file | annotate | diff | comparison | revisions
--- a/src/circularprimitive.h	Tue Jun 28 22:18:11 2022 +0300
+++ b/src/circularprimitive.h	Wed Jun 29 14:11:58 2022 +0300
@@ -17,23 +17,23 @@
 	case CircularPrimitive::Circle:
 		ldraw::circle(circ.fraction.segments, circ.fraction.divisions, [&]
 		(const glm::vec2&, const glm::vec2& p1, const glm::vec2& p2){
-			fn(edge(xform(p1, 0), xform(p2, 0)));
+			fn(LineSegment{xform(p1, 0), xform(p2, 0)}, EDGE_COLOR);
 		});
 		break;
 	case CircularPrimitive::Disc:
 		ldraw::circle(circ.fraction.segments, circ.fraction.divisions, [&]
 		(const glm::vec2&, const glm::vec2& p1, const glm::vec2& p2){
-			fn(triangle(primitiveOrigin, xform(p1, 0), xform(p2, 0)));
+			fn(Triangle{primitiveOrigin, xform(p1, 0), xform(p2, 0)}, MAIN_COLOR);
 		});
 		break;
 	case CircularPrimitive::Cylinder:
 		ldraw::circle(circ.fraction.segments, circ.fraction.divisions, [&]
 		(const glm::vec2&, const glm::vec2& p1, const glm::vec2& p2){
-			Colored<Quadrilateral> quad = quadrilateral(xform(p1, 1), xform(p2, 1), xform(p2, 0), xform(p1, 0));
+			Quadrilateral quad{xform(p1, 1), xform(p2, 1), xform(p2, 0), xform(p1, 0)};
 			if (invertedMatrix) {
 				std::swap(quad.p2, quad.p4);
 			}
-			fn(quad);
+			fn(quad, MAIN_COLOR);
 		});
 		break;
 	case CircularPrimitive::CylinderOpen:
@@ -77,7 +77,7 @@
 					{+1, -1},
 				};
 				const glm::vec2& corner = corners[i * 4 / circ.fraction.divisions];
-				fn(triangle(xform(p2, 0), xform(p1, 0), xform(corner, 0)));
+				fn(Triangle{xform(p2, 0), xform(p1, 0), xform(corner, 0)}, MAIN_COLOR);
 				++i;
 			});
 		}
@@ -86,7 +86,7 @@
 		for (unsigned int i = 1; i < circ.fraction.segments; ++i) {
 			const glm::vec2& p1 = ldraw::rimpoint(circ.fraction.divisions, i);
 			const glm::vec2& p2 = ldraw::rimpoint(circ.fraction.divisions, i + 1);
-			fn(triangle(xform(p2, 0), xform(p1, 0), xform({1, 0}, 0)));
+			fn(Triangle{xform(p2, 0), xform(p1, 0), xform({1, 0}, 0)}, MAIN_COLOR);
 		}
 		break;
 	}
--- a/src/gl/compiler.cpp	Tue Jun 28 22:18:11 2022 +0300
+++ b/src/gl/compiler.cpp	Wed Jun 29 14:11:58 2022 +0300
@@ -224,10 +224,10 @@
 template<typename Fn>
 void iterateModelPolygons(Model* model, DocumentManager* context, Fn&& fn)
 {
-	PolygonCache* cache = findPolygonCacheForModel(model, context);
+	PolygonCache* const cache = findPolygonCacheForModel(model, context);
 	if (cache != nullptr) {
-		const PolygonCache::vector_type* polygons = getCachedPolygons(cache, model, context);
-		for (const WithId<PolygonElement>& polygon : *polygons) {
+		recacheIfNeeded(cache, model, context);
+		for (const WithId<PolygonElement>& polygon : cache->polygons) {
 			fn(polygon);
 		}
 	}
--- a/src/layers/edittools.cpp	Tue Jun 28 22:18:11 2022 +0300
+++ b/src/layers/edittools.cpp	Wed Jun 29 14:11:58 2022 +0300
@@ -141,7 +141,19 @@
 	painter->drawPolygon(QPolygonF{convertWorldPointsToScreenPoints(points, renderer)});
 }
 
-static std::vector<std::vector<glm::vec3>> modelActionPoints(const ModelAction& action)
+//! \brief Conversion function from PlainPolygonElement to ModelElement
+ModelElement elementFromPolygonAndColor(const PlainPolygonElement& poly, ColorIndex color)
+{
+	// use std::visit with a templated lambda to resolve the type of poly.
+	return std::visit([color](const auto& resolvedPoly) -> ModelElement {
+		// unlike with normal templates we need to pry out the type out manually
+		using PolygonType = std::decay_t<decltype(resolvedPoly)>;
+		// add color and return as a model element.
+		return Colored<PolygonType>{resolvedPoly, color};
+	}, poly);
+}
+
+static std::vector<std::vector<glm::vec3>> polygonsToBeInserted(const ModelAction& action)
 {
 	std::vector<std::vector<glm::vec3>> result;
 	if (const AppendToModel* append = std::get_if<AppendToModel>(&action)) {
@@ -156,8 +168,10 @@
 			result.push_back({quad->p1, quad->p2, quad->p3, quad->p4});
 		}
 		else if (const CircularPrimitive* circ = std::get_if<Colored<CircularPrimitive>>(&newElement)) {
-			rasterize(*circ, [&](const ModelElement& element){
-				const auto& subpoints = modelActionPoints(AppendToModel{element});
+			// rasterize the circle down to polygons, and append them to the result.
+			rasterize(*circ, [&](const PlainPolygonElement& poly, const ColorIndex color){
+				AppendToModel append{elementFromPolygonAndColor(poly, color)};
+				const auto& subpoints = polygonsToBeInserted(append);
 				std::copy(subpoints.begin(), subpoints.end(), std::back_inserter(result));
 			});
 		}
@@ -232,7 +246,7 @@
 	const Pens& pens = *reinterpret_cast<const Pens*>(pensptr);
 	painter->setPen(pens.polygonPen);
 	for (const ModelAction& action : this->modelActions()) {
-		for (const std::vector<glm::vec3>& points : modelActionPoints(action)) {
+		for (const std::vector<glm::vec3>& points : polygonsToBeInserted(action)) {
 			if (points.size() == 2) {
 				drawWorldPolyline(painter, points, renderer);
 			}
--- a/src/model.h	Tue Jun 28 22:18:11 2022 +0300
+++ b/src/model.h	Wed Jun 29 14:11:58 2022 +0300
@@ -108,11 +108,13 @@
 	Empty,
 	ParseError>;
 
-using PolygonElement = Colored<std::variant<
+using PlainPolygonElement = std::variant<
 	LineSegment,
 	Triangle,
 	Quadrilateral,
-	ConditionalEdge>>;
+	ConditionalEdge>;
+
+using PolygonElement = Colored<PlainPolygonElement>;
 
 template<typename T>
 struct remove_color {};
--- a/src/polygoncache.cpp	Tue Jun 28 22:18:11 2022 +0300
+++ b/src/polygoncache.cpp	Wed Jun 29 14:11:58 2022 +0300
@@ -29,101 +29,106 @@
 	}
 }
 
-static std::vector<WithId<PolygonElement>> getPolygonsAt(
+template<typename Fn, typename Fn2>
+static void collectPolygons(
+	const ModelElement& element,
+	Winding& winding,
+	GetPolygonsContext* context,
+	Fn&& add,
+	Fn2&& reserve)
+{
+	std::visit<void>(overloaded{
+		[&](const Colored<LineSegment>& edge) {
+			add({edge, edge.color});
+		},
+		[&](const Colored<Triangle>& triangle) {
+			add({triangle, triangle.color});
+		},
+		[&](const Colored<Quadrilateral>& quad) {
+			add({quad, quad.color});
+		},
+		[&](const Colored<ConditionalEdge>& cedge) {
+			add({cedge, cedge.color});
+		},
+		[&add, context, &reserve](const Colored<SubfileReference>& ref) {
+			Model* dependency = context->documents->findDependencyByName(context->modelId, ref.name);
+			PolygonCache* cache = nullptr;
+			if (dependency != nullptr) {
+				cache = findPolygonCacheForModel(dependency, context->documents);
+			}
+			if (cache != nullptr) {
+				const bool needInverting = glm::determinant(ref.transformation) < 0;
+				recacheIfNeeded(cache, dependency, context->documents);
+				reserve(cache->polygons.size());
+				for (const PolygonElement& cacheElement : cache->polygons) {
+					PolygonElement polygon = transformed(cacheElement, ref.transformation);
+					if (needInverting != ref.inverted) {
+						gl::invert(polygon);
+					}
+					if (polygon.color == MAIN_COLOR) {
+						polygon.color = ref.color;
+					}
+					add(polygon);
+				}
+			}
+		},
+		[&add](const Colored<CircularPrimitive>& circ) {
+			rasterize(circ, [&](const PlainPolygonElement& polygon, ColorIndex color){
+				if (color == MAIN_COLOR) {
+					color = circ.color;
+				}
+				add(PolygonElement{polygon, color});
+			});
+		},
+		[&winding](const Comment& comment) {
+			if (comment.text == QStringLiteral("BFC CERTIFY CW")) {
+				winding = Clockwise;
+			} 
+			else if (comment.text == QStringLiteral("BFC CERTIFY CCW")) {
+				winding = Anticlockwise;
+			}
+			else if (comment.text == QStringLiteral("BFC NOCERTIFY")) {
+				winding = NoWinding;
+			}
+		},
+		[](const ModelElement&) {}
+	}, element);
+}
+
+static std::vector<WithId<PolygonElement>> inlinePolygons(
 	const Model* model,
 	GetPolygonsContext* context)
 {
+	Winding winding = NoWinding;
 	std::vector<WithId<PolygonElement>> result;
 	for (std::size_t i = 0; i < model->size(); i += 1)
 	{
 		const ModelElement& element = (*model)[i];
 		const ModelId id = model->idAt(i);
-		std::visit<void>(overloaded{
-			[&](const Colored<LineSegment>& edge) {
-				result.push_back({{edge, edge.color}, id});
-			},
-			[&](const Colored<Triangle>& triangle) {
-				result.push_back({{triangle, triangle.color}, id});
-			},
-			[&](const Colored<Quadrilateral>& quad) {
-				result.push_back({{quad, quad.color}, id});
-			},
-			[&](const Colored<ConditionalEdge>& cedge) {
-				result.push_back({{cedge, cedge.color}, id});
-			},
-			[&result, &id, context](const Colored<SubfileReference>& ref) {
-				Model* dependency = context->documents->findDependencyByName(context->modelId, ref.name);
-				PolygonCache* cache = nullptr;
-				if (dependency != nullptr) {
-					cache = findPolygonCacheForModel(dependency, context->documents);
+		collectPolygons(element, winding, context,
+			[&result, winding, id](const PolygonElement& poly){
+				result.push_back({poly, id});
+				if (winding == Winding::Clockwise) {
+					gl::invert(result.back());
 				}
-				if (cache != nullptr) {
-					const bool needInverting = glm::determinant(ref.transformation) < 0;
-					const PolygonCache::vector_type* modelPolygons = getCachedPolygons(
-						cache,
-						dependency,
-						context->documents);
-					reserveMore(result, modelPolygons->size());
-					for (WithId<PolygonElement> polygon : *modelPolygons) {
-						polygon = {transformed(polygon, ref.transformation), polygon.id};
-						if (needInverting != ref.inverted) {
-							gl::invert(polygon);
-						}
-						if (polygon.color == MAIN_COLOR) {
-							polygon.color = ref.color;
-						}
-						polygon.id = id;
-						result.push_back(polygon);
-					}
-				}
-			},
-			[&result, id](const Colored<CircularPrimitive>& circ) {
-				rasterize(circ, [&](const ModelElement& element){
-					std::visit<void>(overloaded{
-						// TODO: :-(
-						[&](const Colored<LineSegment>& edge) {
-							result.push_back({{edge, edge.color}, id});
-						},
-						[&](const Colored<Triangle>& triangle) {
-							result.push_back({{triangle, triangle.color}, id});
-						},
-						[&](const Colored<Quadrilateral>& quad) {
-							result.push_back({{quad, quad.color}, id});
-						},
-						[&](const Colored<ConditionalEdge>& cedge) {
-							result.push_back({{cedge, cedge.color}, id});
-						},
-						[&](const auto&){},
-					}, element);
-				});
-			},
-			[](const ModelElement&) {}
-		}, element);
+			}, [&result](std::size_t incomingsize){
+				reserveMore(result, incomingsize);
+			});
 	}
 	return result;
 }
 
-/**
- * @brief Gets a list of GL polygons that are used to represent this model.
- * @details Will build polygons if polygons are outdated.
- * @param documents Documents to use to resolve subfile references
- * @return vector of GL polygons
- */
-const PolygonCache::vector_type* getCachedPolygons(
-	PolygonCache *cache,
-	Model *model,
-	DocumentManager *documents)
+void recacheIfNeeded(PolygonCache *cache, Model *model, DocumentManager *documents)
 {
 	if (cache->needRecache)
 	{
-		cache->cachedPolygons.clear();
+		cache->polygons.clear();
 		const std::optional<ModelId> modelId = documents->findIdForModel(model);
 		if (modelId.has_value())
 		{
 			GetPolygonsContext context{modelId.value(), documents};
-			cache->cachedPolygons = getPolygonsAt(model, &context);
+			cache->polygons = inlinePolygons(model, &context);
 		}
 		cache->needRecache = false;
 	}
-	return &cache->cachedPolygons;
 }
--- a/src/polygoncache.h	Tue Jun 28 22:18:11 2022 +0300
+++ b/src/polygoncache.h	Wed Jun 29 14:11:58 2022 +0300
@@ -18,11 +18,11 @@
 struct PolygonCache
 {
 	using vector_type = std::vector<WithId<PolygonElement>>;
-	vector_type cachedPolygons;
+	vector_type polygons;
 	bool needRecache = true;
 };
 
-const PolygonCache::vector_type* getCachedPolygons(
+void recacheIfNeeded(
 	PolygonCache* cache,
 	Model* model,
 	class DocumentManager* documents);

mercurial