Skip to content

File ScannedModel.cpp

File List > AIAC > ScannedModel.cpp

Go to the documentation of this file

// #####################################################################
// >>>>>>>>>>>>>>>>>>>>> BEGINNING OF LEGAL NOTICE >>>>>>>>>>>>>>>>>>>>>
//######################################################################
//
// This source file, along with its associated content, was authored by
// Andrea Settimi, Hong-Bin Yang, Naravich Chutisilp, and numerous other
// contributors. The code was originally developed at the Laboratory for
// Timber Construction (IBOIS, director: Prof. Yves Weinand) at the School of 
// Architecture, Civil and Environmental Engineering (ENAC) at the Swiss
// Federal Institute of Technology in Lausanne (EPFL) for the Doctoral
// Research "Augmented Carpentry" (PhD researcher: Andrea Settimi,
// co-director: Dr. Julien Gamerro, director: Prof. Yves Weinand).
//
// Although the entire repository is distributed under the GPL license,
// these particular source files may also be used under the terms of the
// MIT license. By accessing or using this file, you agree to the following:
//
// 1. You may reproduce, modify, and distribute this file in accordance
//    with the terms of the MIT license.
// 2. You must retain this legal notice in all copies or substantial
//    portions of this file.
// 3. This file is provided "AS IS," without any express or implied
//    warranties, including but not limited to the implied warranties of
//    merchantability and fitness for a particular purpose.
//
// If you cannot or will not comply with the above conditions, you are
// not permitted to use this file. By proceeding, you acknowledge and
// accept all terms and conditions herein.
//
//######################################################################
// <<<<<<<<<<<<<<<<<<<<<<< END OF LEGAL NOTICE <<<<<<<<<<<<<<<<<<<<<<<<
// #####################################################################
//
#include "aiacpch.h"

#include "GeometryUtils.h"
#include "ScannedModel.h"

namespace AIAC
{
    bool ScannedModel::Load(std::string path) {
        // check if file is good
        if(path.empty()){
            AIAC_ERROR("ACInfoModel::Load() path is empty");
            return false;
        }
        if(!std::filesystem::exists(path)){
            AIAC_ERROR("ACInfoModel::Load() file does not exist");
            return false;
        }

        m_Mesh = GOMesh::LoadPly(path);
        if(m_Mesh == nullptr) return false;

        m_Mesh->SetVisibility(false);

        BuildBoundingBox();
        UpdateBboxGOLine();

        return true;
    }

    void ScannedModel::BuildBoundingBox() {
        struct Neighbor{
            Neighbor(){};
            Neighbor(float dist, glm::vec3 vec, glm::vec3 pt): dist(dist), vec(vec), pt(pt) {};
            float dist;
            glm::vec3 vec;
            glm::vec3 pt;
        };

        auto vertices = m_Mesh->GetVertices();
        auto basePt = vertices[0];
        std::vector<Neighbor> neighbors;

        for (auto& v : vertices) {
            auto vec = v - basePt;
            auto dist = glm::length(vec);
            neighbors.emplace_back(dist, vec, v);
        }

        // sort by distance
        std::sort(neighbors.begin(), neighbors.end(), [](Neighbor& a, Neighbor& b) {
            return a.dist < b.dist;
        });

        // filter out duplicate vertices based on point difference
        if (neighbors.size() > 8) {
            for (int i = 0; i < neighbors.size() - 1; i++) {
                if (glm::length(neighbors[i].pt - neighbors[i + 1].pt) < 0.05) {
                    neighbors.erase(neighbors.begin() + i);
                    i--;
                }
            }
        }

        auto rotationMat = GetRotationMatrix(neighbors[4].vec, 3.14159 / 2);
        auto pt1 = neighbors[1].pt - basePt;
        auto pt2 = neighbors[2].pt - basePt;
        auto test1 = glm::length(rotationMat * pt1 - pt2);
        auto test2 = glm::length(rotationMat * pt2 - pt1);
        // if it's in the correct order, test1 should be ~0,
        // therefore, when test1 > test2, it means that the order is wrong
        if (test1 > test2) {
            std::swap(neighbors[1], neighbors[2]);
        }

        basePt = neighbors[4].pt;
        pt1 = neighbors[5].pt - basePt;
        pt2 = neighbors[6].pt - basePt;
        test1 = glm::length(rotationMat * pt1 - pt2);
        test2 = glm::length(rotationMat * pt2 - pt1);
        // if it's in the correct order, test1 should be ~0,
        // therefore, when test1 > test2, it means that the order is wrong
        if (test1 > test2) {
            std::swap(neighbors[5], neighbors[6]);
        }

        // re-order the vertices, making it a counter-clockwise order start from the longer edge
        // first bottom, then top
        //
        //   (7)------------------------(6)
        //   / |                       / |  
        // (4)-----------------------(5) |
        //  |  |                      |  |
        //  | (3)---------------------|-(2)
        //  |/                        |/
        // (0)-----------------------(1)
        //
        m_Bbox = {
            neighbors[0].pt,
            neighbors[4].pt,
            neighbors[6].pt,
            neighbors[2].pt,
            neighbors[1].pt,
            neighbors[5].pt,
            neighbors[7].pt,
            neighbors[3].pt
        };

        // testing the rotation is correct
        float minTotalDist = 1e9;
        int rotIndex = -1;
        for(int i = 0 ; i < 4 ; i++){
            float dist = 0;
            for(int j = 0 ; j < 8 ; j+=2){
                dist += glm::length(m_Bbox[j] - m_Bbox[j + 1]);
            }
            if(dist < minTotalDist){
                minTotalDist = dist;
                rotIndex = i;
            }
            // perform rotation
            auto tmp = m_Bbox[0];
            m_Bbox[0] = m_Bbox[3];
            m_Bbox[3] = m_Bbox[7];
            m_Bbox[7] = m_Bbox[4];
            m_Bbox[4] = tmp;
        }
        // rotate to the correct order
        for(int i = 0 ; i < rotIndex ; i++){
            auto tmp = m_Bbox[0];
            m_Bbox[0] = m_Bbox[3];
            m_Bbox[3] = m_Bbox[7];
            m_Bbox[7] = m_Bbox[4];
            m_Bbox[4] = tmp;
        }
    }

    void ScannedModel::UpdateBboxGOLine() {
        // update the GOLine references
        for(auto& line : m_BboxGOLines)
            GOLine::Remove(line);

        for(auto& edge : m_BboxEdgesIndices){
            auto i = edge.first, k = edge.second;
            auto vec = glm::normalize(m_Bbox[k] - m_Bbox[i]);
            m_BboxGOLines.push_back(GOLine::Add(m_Bbox[i], m_Bbox[i] + vec, 2.0f));
            m_BboxGOLines.push_back(GOLine::Add(m_Bbox[k], m_Bbox[k] - vec, 2.0f));
        }
    }

    float ScannedModel::GetLength(){
        float dist = 0.0f;
        dist += glm::distance(m_Bbox[0], m_Bbox[1]);
        dist += glm::distance(m_Bbox[2], m_Bbox[3]);
        dist += glm::distance(m_Bbox[4], m_Bbox[5]);
        dist += glm::distance(m_Bbox[6], m_Bbox[7]);
        dist /= 4.0f;

        return dist;
    }

} // namespace AIAC