src/vertexmap.cpp

Wed, 22 Sep 2021 12:30:48 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 22 Sep 2021 12:30:48 +0300
changeset 136
e8444e0d7f1a
parent 120
8c9fff699241
child 151
e628fc2e0c72
permissions
-rw-r--r--

Work on edit history

#include "vertexmap.h"
#include "linetypes/polygonobject.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 ldraw::Object* object, std::function<void(Edge&&)> fn)
{
	for (int i = 0; i < object->numPoints() - 1; i += 1)
	{
		const glm::vec3 p1 = object->getPoint(i);
		const glm::vec3 p2 = object->getPoint((i + 1) % object->numPoints());
		fn(Edge{p1, p2});
	}
}

void VertexMap::build()
{
	this->map.clear();
	this->vertices.clear();
	this->vertexHashes.clear();
	this->model->apply<ldraw::Object>([&](const ldraw::Object* object)
	{
		glm::mat4 matrix;
		if (object->numPoints() > 2)
		{
			const auto& p0 = object->getPoint(0);
			const auto& p1 = object->getPoint(1);
			const auto& p2 = object->getPoint(2);
			const glm::vec3 a = glm::normalize(p1 - p0);
			const glm::vec3 b = glm::normalize(p2 - p0);
			const glm::vec3 c = glm::normalize(glm::cross(a, b));
			const glm::vec3 d = glm::normalize(glm::cross(a, c));
			matrix = glm::mat4{{a, 0}, {-c, 0}, {d, 0}, {}};
		}
		for (int i = 0; i < object->numPoints(); i += 1)
		{
			const glm::vec3& point = object->getPoint(i);
			const unsigned int hash = hashVertex(point);
			VertexInfo& info = this->map[hash];
			info.point = point;
			info.objects.insert(object->id);
			if (object->numPoints() > 2 and not info.transformSet)
			{
				info.transform = matrix;
				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 ldraw::id_t objectId : info.objects)
		{
			edges(model->get(objectId), [&](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