Skip to content

File ACInfoModel.h

File List > AIAC > ACInfoModel.h

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 <<<<<<<<<<<<<<<<<<<<<<<<
// #####################################################################
//
#pragma once
#include <vector>
#include <map>
#include <set>
#include <string>
#include <glm/glm.hpp>

#include "pugixml.hpp"

#include "AIAC/GOSys/GOPrimitive.h"
#include "AIAC/Config.h"

namespace AIAC{

enum class ACIMState{
    NOT_DONE,
    CURRENT,
    DONE
};

static std::map<ACIMState, glm::vec4> HOLE_AXIS_COLOR = {
    {ACIMState::NOT_DONE, glm::vec4(0.1f, 0.9f, 0.9f, 1.0f)},
    {ACIMState::CURRENT, glm::vec4(0.1f, 0.9f, 0.5f, 1.0f)},
    {ACIMState::DONE, glm::vec4(0.3f, 0.3f, 0.3f, 0.5f)}
};

static std::map<ACIMState, glm::vec4> HOLE_CYLINDER_COLOR = {
    {ACIMState::NOT_DONE, glm::vec4(0.1f, 0.9f, 0.9f, 0.2f)},
    {ACIMState::CURRENT, glm::vec4(0.1f, 0.9f, 0.5f, 0.2f)},
    {ACIMState::DONE, glm::vec4(0.3f, 0.3f, 0.3f, 0.2f)}
};

static std::map<ACIMState, glm::vec4> CUT_FACE_COLOR = {
    {ACIMState::NOT_DONE, glm::vec4(0.9f, 0.6f, 0.2f, 0.2f)},
    {ACIMState::CURRENT, glm::vec4(0.1f, 0.9f, 0.5f, 0.2f)},
    {ACIMState::DONE, glm::vec4(0.3f, 0.3f, 0.3f, 0.2f)}
};

static std::map<ACIMState, glm::vec4> CUT_EDGE_COLOR = {
    {ACIMState::NOT_DONE, glm::vec4(0.9f, 0.6f, 0.2f, 1.0f)},
    {ACIMState::CURRENT, glm::vec4(0.1f, 0.9f, 0.5f, 1.0f)},
    {ACIMState::DONE, glm::vec4(0.3f, 0.3f, 0.3f, 0.5f)}
};

static ACIMState StringToState(std::string m_State);

static glm::vec3 StringToVec3(std::string str);

static std::vector<std::string> StringToTokens(std::string str);

static std::set<std::string> StringToSet(std::string str);

static bool StringToBool(std::string str);

static std::string Vec3ToString(glm::vec3 vec3);

class TimberInfo{
public:
    class Component {
    public:
        Component(std::string type) : m_Type(type) {
#ifndef HEADLESS_TEST
            m_Scale = AIAC::Config::Get<float>(AIAC::Config::SEC_AIAC, AIAC::Config::SCALE_FACTOR, 1.0f);
#endif
        }
        virtual void SetAsCurrent();
        virtual void SetAsDone();
        virtual void SetAsNotDone();
        virtual void SetVisibility(bool visible);
        virtual glm::vec3 GetCenter() const;

    public:
        bool IsMarkedDone; // This one is for UI

    public:  __always_inline
        std::string GetTypeString() const { return m_Type; }

    protected:
        float m_Scale; // When converting to SLAM world's coordinate, multiply this scale
        ACIMState m_State;
        std::string m_Type;
        pugi::xml_node m_ACIMDocNode;
        std::string m_ID;
        glm::vec3 m_Center;

        std::vector<std::shared_ptr<GOPrimitive>> m_GOPrimitives;

    friend class ACInfoModel;
    friend class TimberInfo;
    };

    class Hole: public Component{
    public:
        Hole() : Component("HOLE") {}
    public:
        virtual void SetAsCurrent();
        virtual void SetAsDone();
        virtual void SetAsNotDone();
        virtual void SetVisibility(bool visible);
        glm::vec3 GetCenter() const override { return (m_Start + m_End) * 0.5f; };
        void SwapStartEnd();


    public:  __always_inline
        std::shared_ptr<GOPoint> GetStartPointGO() { return m_StartPointGO; }
        std::shared_ptr<GOPoint> GetEndPointGO() { return m_EndPointGO; }

    public:  __always_inline
        double GetRadius() const { return m_Radius; }

    private:
        // These values uses original coordinate in xml file
        // i.e. not transformation (rotation / translation) is applied
        glm::vec3 m_Start;
        bool m_StartExposed;
        glm::vec3 m_End;
        bool m_EndExposed;
        double m_Radius;
        std::set<std::string> m_Neighbors;

        // GOPrimitives
        std::shared_ptr<GOLine> m_AxisGO;
        std::shared_ptr<GOCylinder> m_CylinderGO;
        std::shared_ptr<GOPoint> m_StartPointGO;
        std::shared_ptr<GOPoint> m_EndPointGO;
        std::shared_ptr<GOText> m_IDLabelGO;

        friend class ACInfoModel;
    };

    class Cut: public Component{
    public:
        Cut() : Component("CUT") {}

        virtual void SetAsCurrent();
        virtual void SetAsDone();
        virtual void SetAsNotDone();
        virtual void SetVisibility(bool visible);
        glm::vec3 GetCenter() const override { return m_Center; };

        // Sub-class Face
        class Face: public Component{
        public:
            Face() : Component("FACE") {}
            glm::vec3 GetCenter() const override { return m_Center; };

            bool IsExposed() const { return m_Exposed; }
            glm::vec3 GetNormal() const { return m_Normal; }
            std::vector<glm::vec3> GetCorners() const { return m_Corners; }
            std::set<std::string> GetEdges() const { return m_Edges; }
            std::set<std::string> GetNeighbors() const { return m_Neighbors; }

        private:
            bool m_Exposed;
            glm::vec3 m_Normal;
            std::vector<glm::vec3> m_Corners;
            std::set<std::string> m_Edges;
            std::set<std::string> m_Neighbors;
            std::shared_ptr<GOMesh> m_GO;

            friend class Cut;
            friend class TimberInfo;
            friend class ACInfoModel;
        };

        // Sub-class Edge
        class Edge: public Component{
        public:
            GOPoint GetStartPt() { return m_GO->GetPStart(); }
            GOPoint GetEndPt() { return m_GO->GetPEnd(); }
            Edge() : Component("EDGE") {}

            inline void SetCotasVisibility(bool visible) {
                for(auto& cota : m_Cotas) cota->SetVisibility(visible);
                for(auto& cotaLine : m_CotaLines) cotaLine->SetVisibility(visible);
                for(auto& cotaPt : m_CotaPts) cotaPt->SetVisibility(visible);
            }
            inline void ClearCotas() {
                m_Cotas.clear();
                m_CotaLines.clear();
                m_CotaPts.clear();
            }

        private:
            // These Start and End are original value (not transformed)
            glm::vec3 m_Start;
            glm::vec3 m_End;
            std::set<std::string> m_Neighbors;
            std::shared_ptr<GOLine> m_GO;

            std::vector<std::shared_ptr<GOText>> m_Cotas;
            std::vector<std::shared_ptr<GOLine>> m_CotaLines;
            std::vector<std::shared_ptr<GOPoint>> m_CotaPts;

            friend class Cut;
            friend class TimberInfo;
            friend class ACInfoModel;
        };

        inline bool IsSingleFace() const { return m_NonExposedFaceIDs.size() == 1; }
        inline Face& GetFace(std::string id) { if(m_Faces.count(id) == 0) throw std::invalid_argument("Face ID does not exist."); return m_Faces[id]; }
        inline Edge& GetEdge(std::string id) { if(m_Edges.count(id) == 0) throw std::invalid_argument("Edge ID does not exist."); return m_Edges[id]; }
        inline std::map<std::string, Face>& GetAllFaces() { return m_Faces; }
        inline std::map<std::string, Edge>& GetAllEdges() { return m_Edges; }
        inline std::set<std::string>& GetAllNonExposedFaceIDs() { return m_NonExposedFaceIDs; }
        inline std::set<std::string>& GetAllNonExposedEdgeIDs() { return m_NonExposedEdgeIDs; }

        void HighlightFace(const std::string& faceId, glm::vec4 color = glm::vec4(0));
        inline std::string GetHighlightedFaceID() const { return m_HighlightedFaceID; }
        inline TimberInfo::Cut::Face GetHighlightedFace() { return m_Faces[m_HighlightedFaceID]; }

        inline std::map<std::string, Face> GetFaceNeighbors(std::string faceID) {
            std::map<std::string, Face> neighbors;
            for(auto& neighborID : m_Faces[faceID].m_Neighbors){
                neighbors[neighborID] = m_Faces[neighborID];
            }
            return neighbors;
        }
        inline std::map<std::string, Face> GetHighlightedFaceNeighbors() {
            return GetFaceNeighbors(m_HighlightedFaceID);
        }

        inline void SetVisibilityAllCotas(bool visible) {
            for(auto& edge : m_Edges){
                edge.second.SetCotasVisibility(visible);
            }
        }

    private:
        std::string m_HighlightedFaceID;
        std::map<std::string, Face> m_Faces;
        std::map<std::string, Edge> m_Edges;
        std::set<std::string> m_NonExposedFaceIDs;
        std::set<std::string> m_NonExposedEdgeIDs;
        std::shared_ptr<GOText> m_IDLabelGO;

        friend class ACInfoModel;
    };

    inline std::string GetID() const { return m_ID; }
    std::vector<std::string> GetAllComponentsIDs() const;

    inline Component* GetComponent(const std::string& id) { return m_Components[id]; }
    inline Component* GetCurrentComponent() { 
        if(m_Components.find(m_CurrentComponentID) == m_Components.end())
            return nullptr;
        return m_Components[m_CurrentComponentID];
    }
    std::string GetCurrentComponentID() { return m_CurrentComponentID; }
    void SetCurrentComponentTo(std::string id);
    void SetNextComponentAsCurrent();
    void SetPrevComponentAsCurrent();

    inline std::vector<glm::vec3> GetBoundingBox() const { return m_Bbox; }
    inline std::vector<std::pair<int, int> > GetBboxEdgesIndices() const { return m_BboxEdgesIndices; }

    void HideAllComponentsExceptCurrent();
    void ShowAllComponents();
    void UpdateCotasVisibility(bool visible);

public: 
    inline int GetFabricatedComponents() {
        int count = 0;
        for(auto& comp : m_Components){
            if(comp.second->m_State == ACIMState::DONE)
                count++;
        }
        return count;
    }
    inline int GetTotalComponents() { return m_Components.size(); }
    inline float GetFabricationProgress() {
        return (float)GetFabricatedComponents() / GetTotalComponents() * 100;
    }

private:  
    std::string ShortenComponentID(std::string id);

public:
    bool IsShowingAllComponents = false;
    bool IsShowingCotas = false;

private:
    std::string m_ID;

    // The bounding box has this structure
    //
    //   (7)------------------------(6)
    //   / |                       / |  
    // (4)-----------------------(5) |
    //  |  |                      |  |
    //  | (3)---------------------|-(2)
    //  |/                        |/
    // (0)-----------------------(1)
    //
    std::vector<glm::vec3> m_Bbox;
    std::vector<std::pair<int, int> > m_BboxEdgesIndices = {
        {0, 1}, {1, 2}, {2, 3}, {3, 0}, // bottom
        {4, 5}, {5, 6}, {6, 7}, {7, 4}, // top
        {0, 4}, {1, 5}, {2, 6}, {3, 7}  // vertical
    };
    ACIMState m_State = ACIMState::NOT_DONE; // TODO: states instead of executed?
    std::map<std::string, Hole> m_Holes;
    std::map<std::string, Cut> m_Cuts;
    std::map<std::string, Component*> m_Components;  // FIXME: refactor with smart pointers
    std::string m_CurrentComponentID = "";

    friend class ACInfoModel;
};

class ACInfoModel
{
public:
    ACInfoModel(){
#ifndef HEADLESS_TEST
        m_Scale = AIAC::Config::Get<float>(AIAC::Config::SEC_AIAC, AIAC::Config::SCALE_FACTOR, 1.0f);
#endif
    };
    ~ACInfoModel(){};

    bool Load(std::string path);

    void Save();

    inline const std::string& GetFilePath() const { return m_FilePath; }

    inline const std::string GetName() const {
        auto nameWithExtension = m_FilePath.substr(m_FilePath.find_last_of("/\\") + 1);
        std::string nameWithoutExtension = nameWithExtension.substr(0, nameWithExtension.find_last_of("."));
        return nameWithoutExtension; }

    inline pugi::xml_document& GetDoc() { return m_ACIMDoc; }

    void Clear();

    inline TimberInfo& GetTimberInfo() { return m_TimberInfo; }

    void UpdateBboxGOLine();

    // /**
    //  * @brief Update the cotas
    //  */
    // void UpdateCotas();

    void Transform(glm::mat4x4 transformMat);

    float GetLength();

    inline float GetRealWorldLength() { return GetLength() / m_Scale; }

    void SetBboxVisibility(bool visible);

    void AddMeasuredBboxLength(const float diff);

    inline float GetMeasuredBboxLength() const { return m_MeasuredBboxLength; };

    void AdjustScale();

private:
    float m_EdgeWeight = 1.1f;
    float m_LabelSize = 0.75f;

    float m_Scale = 1.0f;
    float m_MeasuredBboxLength;

    std::string m_FilePath;
    pugi::xml_document m_ACIMDoc;

    TimberInfo m_TimberInfo;

    std::vector<std::shared_ptr<GOLine>> m_BboxGOLines;
};

}