Wed, 22 Sep 2021 13:28:53 +0300
Document model.h
#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))); } }