src/vertexmap.cpp

Mon, 20 Jun 2022 22:41:34 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Mon, 20 Jun 2022 22:41:34 +0300
changeset 244
065db0753f05
parent 200
ca23936b455b
child 250
2837b549e616
permissions
-rw-r--r--

a bit of refactor in mapbox::util::nth

#include "vertexmap.h"
#include "gl/common.h"

unsigned int hashVertex(const glm::vec3& vec)
{
	return qHash(glm::ivec3{
		int(vec.x * 10000),
		int(vec.y * 10000),
		int(vec.z * 10000),
	});
}

VertexMap::VertexMap(const Model *model) :
	model{model}
{
	connect(
		model,
		&Model::dataChanged,
		this,
		&VertexMap::build
	);
	connect(
		model,
		&Model::rowsInserted,
		this,
		&VertexMap::build
	);
	connect(
		model,
		&Model::rowsRemoved,
		this,
		&VertexMap::build
	);
	this->build();
}

struct Edge
{
	const glm::vec3& a;
	const glm::vec3& b;
};

inline void edges(const ModelElement& element, std::function<void(Edge&&)> fn)
{
	std::visit<void>(overloaded{
		[fn](const Colored<LineSegment>& edge) {
			fn(Edge{edge.p1, edge.p2});
		},
		[fn](const Colored<Triangle>& triangle) {
			fn(Edge{triangle.p1, triangle.p2});
			fn(Edge{triangle.p2, triangle.p3});
			fn(Edge{triangle.p3, triangle.p1});
		},
		[fn](const Colored<Quadrilateral>& quad) {
			fn(Edge{quad.p1, quad.p2});
			fn(Edge{quad.p2, quad.p3});
			fn(Edge{quad.p3, quad.p4});
			fn(Edge{quad.p4, quad.p1});
		},
		[fn](const Colored<ConditionalEdge>& cedge) {
			fn(Edge{cedge.p1, cedge.p2});
		},
		[](const ModelElement&&){}
	}, element);
}

inline void points(
	const ModelElement& element,
	std::function<void(const glm::vec3&)> fn)
{
	std::visit<void>(overloaded{
		[fn](const Colored<LineSegment>& edge) {
			fn(edge.p1);
			fn(edge.p2);
		},
		[fn](const Colored<Triangle>& triangle) {
			fn(triangle.p1);
			fn(triangle.p2);
			fn(triangle.p3);
		},
		[fn](const Colored<Quadrilateral>& quad) {
			fn(quad.p1);
			fn(quad.p2);
			fn(quad.p3);
			fn(quad.p4);
		},
		[fn](const Colored<ConditionalEdge>& cedge) {
			fn(cedge.p1);
			fn(cedge.p2);
			fn(cedge.c1);
			fn(cedge.c2);
		},
		[](const ModelElement&&){}
	}, element);
}

template<typename R>
auto ifplanar(
	const ModelElement& element,
	std::function<R(const glm::vec3&, const glm::vec3&, const glm::vec3&)> fn
)
{
	return std::visit(overloaded{
		[fn](const Triangle& triangle) -> std::optional<R> {
			return fn(triangle.p1, triangle.p2, triangle.p3);
		},
		[fn](const Quadrilateral quad) -> std::optional<R> {
			return fn(quad.p1, quad.p2, quad.p3);
		},
		[](const ModelElement&&) -> std::optional<R> {return {};}
	}, element);
}

void VertexMap::build()
{
	this->map.clear();
	this->vertices.clear();
	this->vertexHashes.clear();
	for (int i = 0; i < this->model->size(); ++i)
	{
		const ModelElement& element = this->model->at(i);
		std::optional<glm::mat4> matrix = ifplanar<glm::mat4>(
			element,
			[](const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3)
			{
				const glm::vec3 a = glm::normalize(p2 - p1);
				const glm::vec3 b = glm::normalize(p3 - p1);
				const glm::vec3 c = glm::normalize(glm::cross(a, b));
				const glm::vec3 d = glm::normalize(glm::cross(a, c));
				return glm::mat4{{a, 0}, {-c, 0}, {d, 0}, {}};
			});
		points(element, [&](const glm::vec3 point) {
			const unsigned int hash = hashVertex(point);
			VertexInfo& info = this->map[hash];
			info.point = point;
			info.objects.insert(this->model->idAt(i));
			if (matrix.has_value() and not info.transformSet)
			{
				info.transform = matrix.value();
				info.transform[3] = {point, 1};
				info.transformSet = true;
			}
			if (not this->vertexHashes.contains(hash))
			{
				this->vertexHashes.insert(hash);
				this->vertices.push_back(point);
			}
		});
	}
	for (auto& pair : this->map)
	{
		auto& info = pair.second;
		// If there's no transformation calculated yet, make up a simple one that at least
		// contains translation.
		if (not info.transformSet)
		{
			info.transform = glm::translate(glm::identity<glm::mat4>(), info.point);
			info.transformSet = true;
		}
		float scale = 1.0f;
		for (const ModelId objectId : info.objects)
		{
			const std::optional<int> index = model->find(objectId);
			if (index.has_value()) {
				edges(this->model->at(index.value()), [&](Edge&& edge)
				{
					if (hashVertex(edge.a) == pair.first or hashVertex(edge.b) == pair.first)
					{
						scale = std::min(scale, glm::length(edge.b - edge.a) / 3);
					}
				});
			}
		}
		info.transform = glm::scale(info.transform, glm::vec3{scale, scale, scale});
	}
	Q_EMIT this->verticesChanged();
}

/**
 * @brief Apply \c fn for all vertices in the map.
 * @param fn
 */
void VertexMap::apply(ApplyFunction fn) const
{
	for (unsigned int i = 0; i < this->vertices.size(); i += 1)
	{
		const glm::vec3& point = this->vertices[i];
		fn(point, this->map.at(hashVertex(point)));
	}
}

mercurial