Revision 8999b0bc5b9ad2a29d852e212afdda5d44636a07 authored by jingma-git on 16 November 2021, 15:16:38 UTC, committed by jingma-git on 16 November 2021, 15:16:38 UTC
1 parent 6ea6874
Raw File
toyview.cpp
// Qt
#include <QVector4D>
#include <QMessageBox>
#include <QApplication>
#include <QPointF>
#include <QObject>
// Core
#include <core/XSubToy.h>
#include <core/XFunctions.h>
#include <core/CSketchManager.h>
#include <core/EGL.h>
#include <core/ContourExtractor.h>
#include <core/skeletor.h>
#include <igl/copyleft/cgal/partition.h>
#include <core/part_generator.h>
// egl
#include <egl/writeLine.h>
#include <egl/readLine.h>
// Viewer
#include "toyview.h"
#include "ColorManager.h"
#include "EasyGL.h"
#include "Three.h"
using namespace std;
using namespace Eigen;

//layer thickness, 0 for mesh, 0.1 for subContour spline, 0.2 for ctrlPts, ctrlRope, glueRope
#define POINT_SIZE 5.0f
#define LINE_WIDTH 3.0f

static void compute_stipple_line(const Eigen::Vector3d &s,
                                 const Eigen::Vector3d &d,
                                 const float len_step,
                                 std::vector<GLVec3> &data)
{
    Vector3d vec = d - s;
    float len = vec.norm();
    int num_steps = len / len_step;
    if (num_steps < 3)
    {
        data.emplace_back(s[0], s[1], s[2]);
        data.emplace_back(d[0], d[1], d[2]);
    }
    else
    {
        Vector3d unit_vec = vec / num_steps;
        for (int i = 0; i < num_steps; i++)
        {
            if (i % 2)
            {
                Vector3d p = s + i * unit_vec;
                data.emplace_back(p[0], p[1], p[2]);
            }
        }
    }
}

static GLVec3 heat_color(float s)
{
    using namespace Eigen;
    GLVec3 zero(1.0, 1.0, 1.0);
    GLVec3 one(1.0, 0.0, 0.0);
    GLVec3 plus(1.0, 1.0, 0.0);
    GLVec3 minus(0.0, 0.0, 1.0);

    if (s >= 2)
    {
        return plus; // Yellow
    }
    else if (s >= 1)
    {
        return (1 - (s - 1)) * one + (s - 1) * plus; // Red-yellow
    }
    else if (s >= 0)
    {
        return (1 - s) * zero + (s)*one; // White-red
    }
    else if (s >= -1)
    {
        return (1 - (s + 1)) * minus + (s + 1) * zero; // Blue-white
    }
    else
    {
        return minus; // Blue
    }
}

// QColor ToyView::CTRL_PT_COLOR(102, 51, 0, 255);
// QColor ToyView::SUBMESH_COLOR(222, 222, 222);
// QColor ToyView::PARENT_MESH_COLOR(204, 153, 255);

QVector<QColor> ToyView::rnd_colors = ColorManager::rndColors(200);

ToyView::ToyView()
{
    // set data
    m_toy = new XToy;
    m_subContourPool.SetBlockSize(XToy::NUM_INI_SUBTOYS);
    m_subCtrlPtsPool.SetBlockSize(XToy::NUM_INI_SUBTOYS);

    // sketch line
    sketchgl.setEdgePtr(&m_sketchline);
    sketchgl.setColor(QColor(Qt::red));
    sketchgl.setEdgeWidth(LINE_WIDTH);
    // outer contour
    contour_gl.setEdgePtr(&contourPts);
    contour_gl.setColor(QColor(Qt::red));
    contour_gl.setEdgeWidth(LINE_WIDTH);
    // def contour
    defContour_gl.setColor(QColor(Qt::red));
    defCtrlPts_gl.setColor(CTRL_PT_COLOR);
    // skeleton
    m_skelGL = new SkelGL(m_toy->skel);
    m_restSkelGL = new SkelGL(m_toy->restSkel);
    // arcball
    m_arcBallGL = new ArcBallGL();
    // plane
    m_planeGL = new PlaneGL();
}

ToyView::~ToyView()
{
    clearSubToyGL();
    delete m_toy;
    delete m_skelGL;
    delete m_restSkelGL;
    delete m_arcBallGL;
}

void ToyView::clearSubToyGL()
{
    for (EdgeContainer *subContGL : m_subContourGL)
        m_subContourPool.Recycle(subContGL);
    m_subContourGL.clear();
    subContours.clear();

    for (PointContainer *subCtrlPtsGL : m_subCtrlPtsGL)
        m_subCtrlPtsPool.Recycle(subCtrlPtsGL);
    m_subCtrlPtsGL.clear();
    subCtrlPts.clear();

    for (size_t i = 0; i < m_subMeshGLCoarse.size(); ++i) // 2D Coarse Mesh
    {
        delete m_subMeshGLCoarse[i];
    }
    m_subMeshGLCoarse.clear();

    for (size_t i = 0; i < m_subMeshGLFine.size(); ++i)
    {
        delete m_subMeshGLFine[i];
    }
    m_subMeshGLFine.clear();

    for (size_t i = 0; i < m_subMeshGLDeform.size(); ++i)
    {
        delete m_subMeshGLDeform[i];
    }
    m_subMeshGLDeform.clear();

    for (size_t i = 0; i < m_subMeshGL3D.size(); ++i)
    {
        delete m_subMeshGL3D[i];
    }
    m_subMeshGL3D.clear();
}

void ToyView::load(QString in_dir)
{
    clearSubToyGL();
    m_toy->load(in_dir.toStdString());

    // visualization
    int n = static_cast<int>(m_toy->m_subToys.size());
    for (int i = 0; i < n; ++i)
    {
        XSubToy *subToy = m_toy->m_subToys[i];
        // Contour Visulization
        getSubContourGL(subToy->GetID());
        getSubCtrlPtsGL(subToy->GetID());

        //2D Mesh Visulization
        MeshGL *meshglCoarse = getSubMeshGLCoarse(subToy->GetID(), &subToy->m_curMesh2DTemp);
        MeshGL *meshglFine = getSubMeshGLFine(subToy->GetID(), &subToy->m_curMesh2D);
        MeshGL *meshglDeform = getSubMeshGLDeform(subToy->GetID(), &subToy->m_defMesh2D);
        //3D Mesh Visulization
        MeshGL *meshgl = getSubMeshGL3D(subToy->GetID(), &subToy->m_curMesh3D);
    }

    updateSubToyGL();
}

void ToyView::save(std::string out_dir)
{
    m_toy->save(out_dir);
}

void ToyView::removeViewer(EasyGL *gl)
{
    M_DEBUG << " ToyView::removeViewer " << gl;
    // Components
    sketchgl.removeViewer(gl);
    defContour_gl.removeViewer(gl);
    defCtrlPts_gl.removeViewer(gl);
    for (int i = 0; i < m_subContourGL.size(); i++)
    {
        m_subContourGL[i]->removeViewer(gl);
        m_subCtrlPtsGL[i]->removeViewer(gl);
    }
    for (int i = 0; i < m_subMeshGLCoarse.size(); i++)
    {
        m_subMeshGLCoarse[i]->removeViewer(gl);
    }
    for (int i = 0; i < m_subMeshGLFine.size(); i++)
    {
        m_subMeshGLFine[i]->removeViewer(gl);
    }
    for (int i = 0; i < m_subMeshGLDeform.size(); i++)
    {
        m_subMeshGLDeform[i]->removeViewer(gl);
    }
    for (int i = 0; i < m_subMeshGL3D.size(); i++)
    {
        m_subMeshGL3D[i]->removeViewer(gl);
    }

    // Merged mesh
    if (m_meshGL2D)
        m_meshGL2D->removeViewer(gl);
    if (m_meshGL3D)
        m_meshGL3D->removeViewer(gl);
    if (m_skelGL)
    {
        m_skelGL->removeViewer(gl);
    }

    if (m_restSkelGL)
    {
        m_restSkelGL->removeViewer(gl);
    }

    if (m_arcBallGL)
    {
        m_arcBallGL->removeViewer(gl);
    }

    if (m_planeGL)
    {
        m_planeGL->removeViewer(gl);
    }
}

XSubToy *ToyView::getCurToy()
{
    return m_toy->GetCurToy();
}

void ToyView::draw_line(EasyGL *gl)
{
    if (m_sketchline.size() < 3)
        return;

    sketchgl.draw(gl);
}

void ToyView::update_line(EasyGL *gl)
{
    if (gl->m_opMode == OPMODE_DRAW_SYM)
    {
        m_sketchline.clear();
        std::vector<Pnt3> polygon;
        m_sym_sketch.to_polygon(polygon);
        for (const Pnt3 &p : polygon)
        {
            m_sketchline.push_back(GLVec3(p.x(), p.y(), p.z()));
        }
        sketchgl.setEdgePtr(&m_sketchline);
    }
    sketchgl.update(gl);
}

void ToyView::add_to_sym_line(const QPoint &point, EasyGL *gl)
{
    Pnt3 p = gl->Cvt2Dto3D(point.x(), point.y(), gl->GetWorldCenterZ());
    m_sym_sketch.set_view_dir(gl->GetViewDir());
    m_sym_sketch.add_pt(p);
}

void ToyView::set_plane(const Plane &plane)
{
    m_planeGL->set_plane(plane);
    m_planeGL->compute_element();

    for (auto v : EasyGL::EasyGLPool())
    {
        v->m_isDrawPlane = true;
        m_planeGL->update(v);
        v->repaint();
    }
}

void ToyView::draw_plane(EasyGL *gl)
{
    m_planeGL->draw(gl);
}

void ToyView::add_outer_contour(const std::vector<Pnt3> &outer_contour, EasyGL *gl)
{
    PartGenerator pg(outer_contour); //part_generator: ToDO: auto sketchline fitting
    M_DEBUG << "add_outer_contour" << endl;
    for (int i = 0; i < static_cast<int>(outer_contour.size()); ++i)
    {
        if (pg.part_idxs[i] == -1)
        {
            const Pnt3 &p = outer_contour[i];
            contourPts.emplace_back(p.x(), p.y(), p.z());
        }
    }
    M_DEBUG << "add_outer_contour successfully!" << endl;
}

void ToyView::draw_outer_contour(EasyGL *gl)
{
    contour_gl.draw(gl);
}

void ToyView::update_outer_contour(EasyGL *gl)
{
    contour_gl.update(gl);
}

void ToyView::draw_subContours(EasyGL *gl)
{
    if (subContours.size() <= 0)
        return;
    for (size_t i = 0; i < m_subContourGL.size(); ++i)
    {
        // !!! must add this line, because
        // subContours.push_back(gl_spline); //!!!this is where deallocation happens
        m_subContourGL[i]->setEdgePtr(&subContours[i]);
        m_subContourGL[i]->draw(gl);
    }
}

void ToyView::draw_subCtrlPts(EasyGL *gl)
{
    if (subCtrlPts.size() <= 0)
        return;
    int i = m_toy->m_curSubToyIdx;
    if (i < 0 || i > m_subCtrlPtsGL.size())
        return;

    float point_size = POINT_SIZE * gl->GetNDCDisOfOnePixelOn2D();
    m_subCtrlPtsGL[i]->setPointSize(point_size);
    m_subCtrlPtsGL[i]->setPointPtr(&subCtrlPts[i]);
    m_subCtrlPtsGL[i]->draw(gl);
}

void ToyView::sketchline2Spline(const vector<Pnt3> &ct_pts3, CSpline &spline, EasyGL *gl)
{
    // backend
    vector<Pnt3> ct_simPts3, ct_uniPts3;
    float fPixelDis = gl->Get3dDisOfOnePixelOn2D();
    // qDebug() << __FILE__ << " " << __LINE__ << " fPixelDis=" << fPixelDis;
    float tol = fPixelDis * 2;
    float avgLen = 40 * tol;
    SimplifyPolyLine(ct_pts3, ct_simPts3, tol);
    UniformPolyLine(ct_simPts3, ct_uniPts3, avgLen);
    spline.ChangeMode(CSpline::SPLMODE_CLOSED_SPLINE);
    while ((ct_uniPts3[0] - ct_uniPts3.back()).norm() < POINT_SIZE) //ToDO: improve
    {
        ct_uniPts3.pop_back();
    }
    for (const Pnt3 &p : ct_uniPts3)
    {
        spline.AddCtrlPoint(p);
    }
}

void ToyView::extract_part_contour(const std::vector<Pnt3> &outer_contour, EasyGL *gl)
{
    double line_len = CalLineLength(outer_contour);
    std::vector<Pnt3> simp_polyline;
    SimplifyPolyLine(outer_contour, simp_polyline, 10);
    if ((simp_polyline.back() - simp_polyline.front()).norm() < 0.1)
    {
        simp_polyline.pop_back();
    }

    std::vector<std::vector<Pnt3>> part_polys;
    igl::copyleft::cgal::partition(simp_polyline, part_polys);
    M_DEBUG << "part_polys=" << part_polys.size() << endl;

    cv::Mat img = cv::Mat::zeros(gl->height(), gl->width(), CV_8UC3);
    for (int i = 0; i < static_cast<int>(part_polys.size()); ++i)
    {
        std::vector<Pnt3> &part_poly = part_polys[i];
        std::vector<cv::Point> pts2d(part_poly.size());
        cv::Scalar color(rand() % 255, rand() % 255, rand() % 255);
        for (int j = 0; j < static_cast<int>(part_poly.size()); ++j)
        {
            const Pnt3 &p = part_poly[j];
            gl->Cvt3Dto2D(int(p[0]), int(p[1]), int(p[2]), pts2d[j].x, pts2d[j].y);
            cv::circle(img, pts2d[j], 3, color, -1);
            cv::putText(img, std::to_string(j), pts2d[j], cv::FONT_HERSHEY_PLAIN, 1.0, cv::Scalar(255, 255, 255));
        }
        for (int j = 0; j < static_cast<int>(pts2d.size() - 1); ++j)
        {
            cv::line(img, pts2d[j], pts2d[j + 1], color, 1);
        }
    }
    cv::imwrite("output/part_poly.jpg", img);
}

EdgeContainer *ToyView::getSubContourGL(int id)
{
    int n = static_cast<int>(m_subContourGL.size());
    XSubToy *subToy = m_toy->m_subToys[id];

    if (id > n - 1) // newly added subtoy
    {
        // interpolated spline
        std::vector<GLVec3> gl_spline;
        for (const Pnt3 &p : subToy->m_sketchPolyLine)
        {
            gl_spline.push_back(GLVec3(p.x(), p.y(), p.z() + 0.1));
        }
        subContours.push_back(gl_spline); //!!!this is where deallocation happens

        EdgeContainer *edgeContainer = m_subContourPool.Request();
        new (edgeContainer) EdgeContainer();
        m_subContourGL.push_back(edgeContainer);
        m_subContourGL[id]->setColor(rnd_colors[id]);
        m_subContourGL[id]->setEdgePtr(&subContours[id]);
        return edgeContainer;
    }

    EdgeContainer *edgeContainer = m_subContourGL[id];
    subContours[id].clear();
    int sign = subToy->m_layerDepth >= 0 ? 1 : -1;
    for (const Pnt3 &p : subToy->m_sketchPolyLine)
    {
        subContours[id].push_back(GLVec3(p.x(), p.y(), p.z() + sign * 0.1));
    }
    edgeContainer->setEdgePtr(&subContours[id]);
    return edgeContainer;
}

PointContainer *ToyView::getSubCtrlPtsGL(int id)
{
    int n = static_cast<int>(m_subCtrlPtsGL.size());
    XSubToy *subToy = m_toy->m_subToys[id];
    CSpline &spline = subToy->m_sketchSpline;

    if (id > n - 1) // newly added subtoy
    {
        // spline control points
        vector<GLVec3> gl_ctrlPts;
        for (int i = 0; i < spline.GetCtrlPointCount(); i++)
        {
            const Pnt3 &p = spline.GetCtrlPoint(i);
            gl_ctrlPts.push_back(GLVec3(p.x(), p.y(), p.z() + 0.2));
        }
        subCtrlPts.push_back(gl_ctrlPts); //!!!this is where deallocation happens

        PointContainer *pointContainer = m_subCtrlPtsPool.Request();
        new (pointContainer) PointContainer();
        pointContainer->setColor(CTRL_PT_COLOR);
        pointContainer->setPointPtr(&subCtrlPts[id]);
        m_subCtrlPtsGL.push_back(pointContainer);
        return pointContainer;
    }

    PointContainer *pointContainer = m_subCtrlPtsGL[id];
    int sign = subToy->m_layerDepth >= 0 ? 1 : -1;
    subCtrlPts[id].clear();
    for (int i = 0; i < spline.GetCtrlPointCount(); i++)
    {
        const Pnt3 &p = spline.GetCtrlPoint(i);
        subCtrlPts[id].push_back(GLVec3(p.x(), p.y(), p.z() + sign * 0.2));
    }
    pointContainer->setPointPtr(&subCtrlPts[id]);
    return pointContainer;
}

void ToyView::computeSubContour(int id)
{
    getSubContourGL(id);
    getSubCtrlPtsGL(id);
}

#ifdef SSKEL_DEBUG
#include <core/sskel_draw.h>
#endif
bool ToyView::add_subContour(const std::vector<GLVec3> &sketchline, EasyGL *gl) // the first most important function!!!
{
    if (sketchline.size() < 3)
        return false;
    if (gl->m_viewMode == VIEW_PERSP)
    {
        QMessageBox::warning(QApplication::activeWindow(),
                             "ERROR",
                             "Cannot sketch under perspective view!\n");
        return false;
    }

    // backend
    // sketchline-->simplify-->uniform discretization-->cubic interpolation-->m_sketchSpline
    vector<Pnt3> ct_pts3, ct_simPts3, ct_uniPts3;
    for (const GLVec3 &p : sketchline)
    {
        // ct_pts3.push_back(Pnt3(p.x, p.y, 0.0));
        ct_pts3.push_back(Pnt3(p.x, p.y, p.z));
    }

#ifdef SSKEL_DEBUG
    std::vector<Pnt3> input_pts;
    egl::readLine("/home/server/MaJing/cpp_proj/shadow_play/input/poly_subToy0.txt", input_pts);

    SkelDrawer drawer;
    drawer.set_sil(input_pts);
    double input_len = CalPolygonLength(input_pts);
    DiscreteLineByLen(input_pts, input_len, 2, ct_pts3);
    cv::Mat img_raw, img_raw_noised, img_raw_smooth;
    img_raw = drawer.draw_sketch_line(ct_pts3);
    // check sensitivity to small shape changes
    int num_pts = static_cast<int>(ct_pts3.size());
    for (int i = 0; i < num_pts;)
    {
        int flag = rand() % 2;
        if (flag)
        {
            int num_disturb = rand() % 10;
            int xoff = rand() % 4;
            int sign = rand() % 2;
            int count = 0;
            while (count < num_disturb)
            {
                Pnt3 &p = ct_pts3[i];
                p[0] += (sign == 0 ? -1 : 1) * xoff;
                ++i;
                ++count;
            }
        }
        else
        {
            ++i;
        }
    }
    egl::writeLine("input/poly_noised.txt", ct_pts3);
    img_raw_noised = drawer.draw_sketch_line(ct_pts3);
    cv::imwrite("output/raw_sketch.png", img_raw);
    cv::imwrite("output/raw_sketch_noised.png", img_raw_noised);
    // SmoothPolyLine(ct_pts3, ct_pts3, 0.2, 20);
    // SmoothPolyLineBiLaplician(ct_pts3, ct_pts3, 0.2, 20);
    DiscreteLineByLen(input_pts, input_len, 10, ct_pts3);
    img_raw_smooth = drawer.draw_sketch_line(ct_pts3);
    cv::imwrite("output/raw_sketch_smooth.png", img_raw_smooth);
#endif

    float fPixelDis = gl->Get3dDisOfOnePixelOn2D();
    float tol = fPixelDis * 2;
    float avgLen = 40 * tol;
    double ct_pts3Len = CalLineLength(ct_pts3);
    M_DEBUG << "fPixelDis=" << fPixelDis << " ct_pts3Len=" << ct_pts3Len << endl;
    std::vector<Pnt3> ct_pts3Tmp(ct_pts3);
    DiscreteLineByLen(ct_pts3Tmp, ct_pts3Len, 10, ct_pts3);
    SimplifyPolyLine(ct_pts3, ct_simPts3, tol);
    UniformPolyLine(ct_simPts3, ct_uniPts3, avgLen);
    if (IsClockWise(ct_uniPts3))
    {
        std::reverse(ct_uniPts3.begin(), ct_uniPts3.end());
    }

    XSubToy *subToy = m_toy->AddSubToy();
    subToy->SetInitViewDir(gl->GetViewDir());
    subToy->SetLayerID(gl->m_curLayer);
    if (gl->m_opMode == OPMODE_DRAW_SYM)
    {
        subToy->set_skel_opt(SKEL_SYM);
        subToy->SetSymLine(m_sym_sketch.sym_line());
        M_DEBUG << "SetSymLine successfully!" << endl;
    }

    // Prepare discrete line for triangulation and skeletonization
    subToy->InitContour(ct_uniPts3);
    subToy->SetCentroid();
    subToy->SetRotToXY();
    subToy->SetSketchPolyLineOnXY();
    subToy->ProcessSelfIsectSketchLine();
    egl::writeLine("output/sketchPolyOnXY.txt", subToy->m_sketchPolyLineOnXY);
    double pixel_dis = gl->Get3dDisOfOnePixelOn2D();
    // test sketch-line self-intersections
    std::vector<Pnt3> contPtsOnXY;
    subToy->Get2DDiscreteLine(subToy->m_sketchPolyLineOnXY, contPtsOnXY, 10, pixel_dis);
    // if (IsLineSelfIntersect(contPtsOnXY)) // comment out according to Reviewer2's advice
    // {
    // QMessageBox::warning(QApplication::activeWindow(),
    //                      "ERROR",
    //                      "Sketch line self-intersect!");
    // m_toy->DelSubToy(subToy);
    // return false;

    // }

    // visualization
    getSubContourGL(subToy->GetID());
    getSubCtrlPtsGL(subToy->GetID());

    M_DEBUG << *subToy << endl;
    return true;
}

void ToyView::deleteSubContour(int id)
{
    if (id != static_cast<int>(subContours.size()) - 1)
    {
        M_DEBUG << "to be deleted subContour is not the last one " << id << endl;
        std::swap(subContours[id], subContours.back());
        std::swap(subCtrlPts[id], subCtrlPts.back());
        std::swap(m_subContourGL[id], m_subContourGL.back());
        std::swap(m_subCtrlPtsGL[id], m_subCtrlPtsGL.back());
    }

    subContours.pop_back();
    subCtrlPts.pop_back();
    m_subContourGL.pop_back();
    m_subCtrlPtsGL.pop_back();
}

bool ToyView::delete_subContour()
{
    XSubToy *subToy = m_toy->GetCurToy();
    int id = subToy->GetID();
    M_DEBUG << " delete_subContour " << id << endl;
    if (id != static_cast<int>(m_toy->m_subToys.size()) - 1)
    {
        M_DEBUG << "to be deleted subToy is not the last one " << id << endl;
        std::swap(subContours[id], subContours.back());
        std::swap(subCtrlPts[id], subCtrlPts.back());
        std::swap(m_subContourGL[id], m_subContourGL.back());
        std::swap(m_subCtrlPtsGL[id], m_subCtrlPtsGL.back());
    }

    subContours.pop_back();
    subCtrlPts.pop_back();
    m_subContourGL.pop_back();
    m_subCtrlPtsGL.pop_back();

    m_toy->DelSubToy(subToy);

    for (auto v : EasyGL::EasyGLPool())
    {
        update_subContours(v);
        v->repaint();
    }
    return true;
}

void ToyView::update_subContours(EasyGL *gl)
{
    for (int i = 0; i < static_cast<int>(m_subContourGL.size()); ++i)
    {
        m_subContourGL[i]->update(gl);
        m_subCtrlPtsGL[i]->update(gl);
    }
}

void ToyView::update_subContours(int id, EasyGL *gl)
{
    m_subContourGL[id]->update(gl);
    m_subCtrlPtsGL[id]->update(gl);
}

void ToyView::add_symetry_toy(EasyGL *gl, SymetryPlane sym_plane, bool wstParent)
{
    if (getGlobalMode(gl->m_opMode) == OPMODE_OBJECT_MODE)
    {
        XSubToy *oSubToy = m_toy->GetCurToy();                                 // original subToy
        double plane_pos = wstParent ? oSubToy->CalSymPlanePos(sym_plane) : 0; // ToDO: let the user specify symmetry position
        XSubToy *subToy = m_toy->AddSymSubToy(oSubToy, sym_plane, plane_pos);

        // Contour Visulization
        getSubContourGL(subToy->GetID());
        getSubCtrlPtsGL(subToy->GetID());
        //2D Mesh Visulization
        MeshGL *meshglCoarse = getSubMeshGLCoarse(subToy->GetID(), &subToy->m_curMesh2DTemp);
        MeshGL *meshglFine = getSubMeshGLFine(subToy->GetID(), &subToy->m_curMesh2D);
        MeshGL *meshglDeform = getSubMeshGLDeform(subToy->GetID(), &subToy->m_defMesh2D);
        //3D Mesh Visulization
        MeshGL *meshgl = getSubMeshGL3D(subToy->GetID(), &subToy->m_curMesh3D);

        updateSubToyGL();
    }
}

void ToyView::compute_chordal_axis()
{
    // if (m_toy->m_subToys.size() > 1)
    // {
    //     cout << __FILE__ << " " << __LINE__ << " only support one SubToy for Debug purpose" << endl;
    //     return;
    // }

    // subChordals.resize(m_toy->m_subToys.size());
    // subChordal_gl.resize(m_toy->m_subToys.size());
    // for (int i = 0; i < m_toy->m_subToys.size(); i++)
    // {
    //     XSubToy *subToy = m_toy->m_subToys[i];
    //     if (!subToy->m_isChordalInit)
    //     {
    //         subChordals[i].resize(subToy->m_vAxisLines.size());
    //         subChordal_gl[i].resize(subToy->m_vAxisLines.size(), nullptr);

    //         for (int li = 0; li < subToy->m_vAxisLines.size(); li++) //li: line index
    //         {
    //             C3DLine &axisLine = subToy->m_vAxisLines[li];
    //             Line3 &chordal = subChordals[i][li];
    //             chordal.resize(axisLine.GetiptCount() + 2);
    //             // layerid * L_THICK
    //             const Pnt3 &p_st = axisLine.Getcpt(0);
    //             const Pnt3 &p_end = axisLine.Getcpt(1);
    //             chordal[0] = GLVec3(p_st.x(), p_st.y(), p_st.z());
    //             for (int j = 0; j < axisLine.GetiptCount(); j++)
    //             {
    //                 const Pnt3 &p = axisLine.Getipt(j);
    //                 chordal[j + 1] = GLVec3(p.x(), p.y(), p.z());
    //                 // cout << __FILE__ << " " << __LINE__ << " " << j << " p=" << p.transpose() << " c=" << subChordals[i][li][j + 1] << endl;
    //             }
    //             chordal[chordal.size() - 1] = GLVec3(p_end.x(), p_end.y(), p_end.z());

    //             EdgeContainer *edgeContainer = new EdgeContainer;
    //             edgeContainer->edge_ptr = &subChordals[i][li];
    //             if (li < 18)
    //                 edgeContainer->setColor(colors[li]);
    //             else
    //                 edgeContainer->setColor(rnd_colors[li]);
    //             subChordal_gl[i][li] = edgeContainer;
    //             QColor &lineC = edgeContainer->color;
    //             cout << __FILE__ << " " << __LINE__ << " "
    //                  << li << ":" << axisLine.Getcf(0) << "-" << axisLine.Getcf(1)
    //                  << " isects=" << axisLine.GetiptCount()
    //                  << " color=" << lineC.redF() << "," << lineC.greenF() << "," << lineC.blueF() << endl;
    //         }
    //         subToy->m_isChordalInit = true;
    //     }
    // }
}

void ToyView::draw_chordal_axis(EasyGL *gl)
{
    // for (int i = 0; i < subChordal_gl.size(); i++)
    // {
    //     for (int li = 0; li < subChordal_gl[i].size(); li++)
    //     {
    //         if (subChordal_gl[i][li])
    //         {

    //             EdgeContainer *edgeContainer = subChordal_gl[i][li];
    //             Line3 &chordal = *edgeContainer->edge_ptr;
    //             // edgeContainer->edge_ptr = &subChordals[i][li];
    //             // C3DLine &axisLine = m_toy->m_subToys[i]->m_vAxisLines[li];
    //             // const Pnt3 &p_st = axisLine.Getcpt(0);
    //             // const Pnt3 &p_end = axisLine.Getcpt(1);
    //             // const GLVec3 &c_st = chordal.front();
    //             // const GLVec3 &c_end = chordal.back();
    //             // double err = 0;
    //             // err += (Pnt3(c_st.x, c_st.y, c_st.z) - p_st).norm();
    //             // for (int j = 0; j < axisLine.GetiptCount(); j++)
    //             // {
    //             //     const Pnt3 &p = axisLine.Getcpt(j);
    //             //     const GLVec3 &c = chordal[j + 1];
    //             //     err += (Pnt3(c.x, c.y, c.z) - p).norm();
    //             // }
    //             // err += (Pnt3(c_end.x, c_end.y, c_end.z) - p_end).norm();
    //             // qDebug() << __FILE__ << " " << __LINE__ << " subToy" << i << " chordal" << li << " err=" << err;
    //             // qDebug() << __FILE__ << " " << __LINE__ << " subToy" << i << " chordal" << li << " isects=" << chordal.size();
    //             subChordal_gl[i][li]->draw(gl);
    //         }
    //     }
    // }
}

void ToyView::addSubMeshCoarse(EasyGL *gl)
{
    XSubToy *subToy = m_toy->GetCurToy();
    subToy->TriangulateCoarseFrom3DContour();
    getSubMeshGLCoarse(subToy->GetID(), &subToy->m_curMesh2DTemp);
}

MeshGL *ToyView::getSubMeshGLCoarse(int id, TMesh *mesh_ptr)
{
    int n = static_cast<int>(m_subMeshGLCoarse.size());
    if (id > n - 1)
    {
        MeshGL *meshgl = new MeshGL(mesh_ptr);
        m_subMeshGLCoarse.push_back(meshgl);
        return meshgl;
    }

    MeshGL *meshgl = m_subMeshGLCoarse[id];
    if (meshgl->mesh != mesh_ptr)
    {
        M_DEBUG << "subMeshGLCoarse" << id << ": old_mesh_ptr=" << meshgl->mesh << " new_mesh_ptr=" << mesh_ptr << endl;
        meshgl->set_mesh(mesh_ptr);
    }
    return meshgl;
}

void ToyView::deleteSubMeshCoarse(int id)
{
    int n = static_cast<int>(m_subMeshGLCoarse.size());
    if (id != n - 1)
    {
        std::swap(m_subMeshGLCoarse[id], m_subMeshGLCoarse.back());
    }
    m_subMeshGLCoarse.pop_back();
}

void ToyView::draw_subMeshCoarse(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGLCoarse.size(); ++i)
    {
        m_subMeshGLCoarse[i]->draw_mesh(gl);
    }
}

void ToyView::updateSubMeshCoarse(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGLCoarse.size(); i++)
    {
        m_subMeshGLCoarse[i]->update(gl);
    }
}

void ToyView::pick_all_subtoys()
{
    M_DEBUG << " pickAllSubToys " << endl;
    m_toy->pick_all_subtoys();
    update_mesh_color();
}

void ToyView::pick_current_subtoy()
{
    M_DEBUG << " pick_current_subtoy" << endl;
    m_toy->pick_current_subtoy();
    update_mesh_color();
}

void ToyView::unpick_all_subtoys()
{
    M_DEBUG << " unpickAllSubToys " << endl;
    m_toy->unpick_all_subtoys();
    update_mesh_color();
}

bool ToyView::pickSubToy(const QPoint &pos, EasyGL *gl, bool ctrl_down)
{
    using namespace Eigen;
    qDebug() << __FILE__ << " " << __LINE__ << " pickSubToy " << pos;
    m_toy->pick_subtoy(pos.x(), pos.y(), gl->width(), gl->height(), gl->m_mvp.data(), ctrl_down);
    if (!ctrl_down)
        pick_current_subtoy();
    update_mesh_color();
    return true;
}

bool ToyView::pickParentSubToy(const QPoint &pos, EasyGL *gl)
{
    using namespace Eigen;
    qDebug() << __FILE__ << " " << __LINE__ << " pickParentSubToy " << pos;
    m_toy->pick_parent_subtoy(pos.x(), pos.y(), gl->width(), gl->height(), gl->m_mvp.data());

    update_mesh_color();
    return true;
}

void ToyView::update_mesh_color()
{
    for (int i = 0; i < static_cast<int>(m_toy->m_subToys.size()); ++i)
    {
        XSubToy *subToy = m_toy->m_subToys[i]; // ToDO: consider the situation we delete model
        if (subToy == m_toy->GetParentToy())
        {
            QColor color(PARENT_MESH_COLOR);
            if (subToy->m_isSelected)
            {
                color = QColor(color.red() - 50, color.green() - 50, color.blue() - 50);
            }
            if (m_subMeshGLFine[i]->mesh_color != color)
            {
                // qDebug() << __FILE__ << " " << __LINE__ << subToy->name().c_str() << " is selected"
                //          << " its original color=" << m_subMeshGLFine[i]->mesh_color;
                m_subMeshGLFine[i]->setMeshColor(color);
                m_subMeshGLFine[i]->compute_color();
                m_subMeshGL3D[i]->setMeshColor(color);
                m_subMeshGL3D[i]->compute_color();

                for (EasyGL *v : EasyGL::EasyGLPool())
                {
                    m_subMeshGLFine[i]->update(v);
                    m_subMeshGL3D[i]->update(v);
                }
            }

            continue;
        }
        if (subToy->m_isSelected)
        {
            const QColor &c = SUBMESH_COLOR;
            QColor color(c.red() - 50, c.green() - 50, c.blue() - 50);
            // QColor color(255, 255, 0);
            // M_DEBUG << "update_mesh_color XSubToy " << subToy->name() << endl;
            if (m_subMeshGLFine[i]->mesh_color != color)
            {
                // qDebug() << __FILE__ << " " << __LINE__ << subToy->name().c_str() << " is selected"
                //          << " its original color=" << m_subMeshGLFine[i]->mesh_color;
                m_subMeshGLFine[i]->setMeshColor(color);
                m_subMeshGLFine[i]->compute_color();
                m_subMeshGL3D[i]->setMeshColor(color);
                m_subMeshGL3D[i]->compute_color();

                for (EasyGL *v : EasyGL::EasyGLPool())
                {
                    m_subMeshGLFine[i]->update(v);
                    m_subMeshGL3D[i]->update(v);
                }
            }
        }
        else
        {
            if (m_subMeshGLFine[i]->mesh_color != SUBMESH_COLOR)
            {
                // qDebug() << __FILE__ << " " << __LINE__ << subToy->name().c_str() << " is de-selected"
                //          << " its original color=" << m_subMeshGLFine[i]->mesh_color;
                m_subMeshGLFine[i]->setMeshColor(SUBMESH_COLOR);
                m_subMeshGLFine[i]->compute_color();
                m_subMeshGL3D[i]->setMeshColor(SUBMESH_COLOR);
                m_subMeshGL3D[i]->compute_color();
                for (EasyGL *v : EasyGL::EasyGLPool())
                {
                    m_subMeshGLFine[i]->update(v);
                    m_subMeshGL3D[i]->update(v);
                }
            }
        }
    }
}

void ToyView::translate(const QPoint &pos, EasyGL *gl)
{
    if (m_toy->m_curSubToyIdx == -1)
        return;
    m_toy->move_subtoy(pos.x(), pos.y(), gl->width(), gl->height(), gl->m_mvp.data());

    int id = m_toy->m_curSubToyIdx;
    computeSubContour(id);
    m_subMeshGLCoarse[id]->compute_element();
    m_subMeshGLFine[id]->compute_element();
    m_subMeshGLDeform[id]->compute_element();
    m_subMeshGL3D[id]->compute_element();
    m_skelGL->compute_element(gl);
    m_restSkelGL->compute_element(gl);
    update_mesh_color();
}

void ToyView::rotate(int axis, const QPoint &curP, const QPoint &lastP, EasyGL *gl)
{
    const Pnt3 &centroid = m_arcBallGL->centroid();
    Pnt2 p = gl->Cvt3Dto2D(centroid);
    Pnt3 p0(p[0], p[1], 0);
    Pnt3 p1(curP.x(), curP.y(), 0);
    Pnt3 p2(lastP.x(), lastP.y(), 0);
    Vec3 p01 = (p1 - p0).normalized();
    Vec3 p02 = (p2 - p0).normalized();
    double det = p01.x() * p02.y() - p01.y() * p02.x();
    double sign = det > 0 ? 1 : -1;
    double angle = sign * std::acos(p01.dot(p02)) * 180.0 / M_PI;
    // qDebug() << "rotate... curP=" << curP << " lastP=" << lastP << " angle=" << angle;
    m_arcBallGL->set_cur_point(gl->Cvt2Dto3D(curP.x(), curP.y(), centroid[2]), gl);

    rotate(axis, angle, gl);
}

void ToyView::rotate(int axis, int angle, EasyGL *gl)
{
    if (m_toy->m_curSubToyIdx == -1)
        return;
    m_toy->rotate_subtoy(axis, angle);

    int id = m_toy->m_curSubToyIdx;
    computeSubContour(id);
    m_subMeshGLCoarse[id]->compute_element();
    m_subMeshGLFine[id]->compute_element();
    m_subMeshGLDeform[id]->compute_element();
    m_subMeshGL3D[id]->compute_element();
    m_skelGL->compute_element(gl);
    m_restSkelGL->compute_element(gl);
    update_mesh_color();
}

void ToyView::setRotCenter()
{
    const Pnt3 &centroid = m_toy->GetCurToy()->m_centroid;
    m_arcBallGL->set_centroid(centroid);
}

void ToyView::draw_arcBall(EasyGL *gl)
{
    m_arcBallGL->draw(gl);
}

void ToyView::addSubMeshFine(EasyGL *gl)
{
    XSubToy *subToy = m_toy->GetCurToy();
    subToy->TriangulateFineFrom3DContour();
    subToy->m_defMesh2D = subToy->m_curMesh2D;
    getSubMeshGLFine(subToy->GetID(), &subToy->m_curMesh2D);
    getSubMeshGLDeform(subToy->GetID(), &subToy->m_defMesh2D);
}

MeshGL *ToyView::getSubMeshGLFine(int id, TMesh *mesh_ptr)
{
    int n = static_cast<int>(m_subMeshGLFine.size());
    if (id > n - 1)
    {
        MeshGL *meshgl = new MeshGL(mesh_ptr);
        m_subMeshGLFine.push_back(meshgl);
        return meshgl;
    }

    MeshGL *meshgl = m_subMeshGLFine[id];
    if (meshgl->mesh != mesh_ptr)
    {
        M_DEBUG << "subMeshGLFine" << id << ": old_mesh_ptr=" << meshgl->mesh << " new_mesh_ptr=" << mesh_ptr << endl;
        meshgl->set_mesh(mesh_ptr);
    }
    return meshgl;
}

MeshGL *ToyView::getSubMeshGLDeform(int id, TMesh *mesh_ptr)
{
    int n = static_cast<int>(m_subMeshGLDeform.size());
    if (id > n - 1)
    {
        MeshGL *meshgl = new MeshGL(mesh_ptr);
        m_subMeshGLDeform.push_back(meshgl);
        return meshgl;
    }

    MeshGL *meshgl = m_subMeshGLDeform[id];
    if (meshgl->mesh != mesh_ptr)
    {
        M_DEBUG << "subMeshGLDeform" << id << ": old_mesh_ptr=" << meshgl->mesh << " new_mesh_ptr=" << mesh_ptr << endl;
        meshgl->set_mesh(mesh_ptr);
    }
    return meshgl;
}

void ToyView::draw_subMeshFine(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGLFine.size(); ++i)
    {
        m_subMeshGLFine[i]->draw_mesh(gl);
    }
}

void ToyView::updateSubMeshFine(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGLFine.size(); ++i)
    {
        m_subMeshGLFine[i]->update(gl);
    }
}

void ToyView::updateSubMeshFine(int id, EasyGL *gl)
{
    if (id == -1)
        return;
    m_subMeshGLFine[id]->update(gl);
}

void ToyView::computeSubMeshFine(int id)
{
    if (id == -1)
        return;
    m_subMeshGLFine[id]->compute_element();
}

void ToyView::deleteSubMeshFine(int id)
{
    int n = static_cast<int>(m_subMeshGLFine.size());
    if (id != n - 1)
    {
        std::swap(m_subMeshGLFine[id], m_subMeshGLFine.back());
    }
    m_subMeshGLFine.pop_back();
}

void ToyView::draw_subMeshDeform(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGLDeform.size(); ++i)
    {
        m_subMeshGLDeform[i]->draw_mesh(gl);
    }
}

void ToyView::updateSubMeshDeform(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGLDeform.size(); ++i)
    {
        m_subMeshGLDeform[i]->update(gl);
    }
}

void ToyView::deleteSubMeshDeform(int id)
{
    int n = static_cast<int>(m_subMeshGLDeform.size());
    if (id != n - 1)
    {
        std::swap(m_subMeshGLDeform[id], m_subMeshGLDeform.back());
    }
    m_subMeshGLDeform.pop_back();
}

void ToyView::paintAuto2D(EasyGL *gl)
{
    for (int i = 0; i < m_subMeshGLFine.size(); i++)
    {
        m_subMeshGLFine[i]->setTexture(gl, gl->bg_texture);
        m_subMeshGLFine[i]->compute_uv(gl);
        m_subMeshGLFine[i]->update(gl);
    }
}

void ToyView::pickBone(const QPoint &pixel, bool shift_down, bool ctrl_down)
{
    EasyGL *m_gl = qobject_cast<EasyGL *>(Three::currentViewer());
    // Bone *bone = m_toy->skel->pick_bone(pixel.x(), pixel.y(), m_gl->width(), m_gl->height(), m_gl->m_mvp.data(), shift_down, ctrl_down);
    Bone *bone = m_toy->pick_bone(pixel.x(), pixel.y(), m_gl->width(), m_gl->height(), m_gl->m_mvp.data(), shift_down, ctrl_down);
    if (ctrl_down && bone != nullptr)
    {
        m_selBones.push_back(bone);
    }
    if (m_skelGL)
    {
        EasyGL *gl = qobject_cast<EasyGL *>(Three::currentViewer());
        update_skel(gl);
    }
}

void ToyView::dragBone(const QPoint &pixel, bool right_click, bool shift_down, bool ctrl_down)
{
    EasyGL *gl = qobject_cast<EasyGL *>(Three::currentViewer());
    m_toy->drag_bone(pixel.x(), pixel.y(),
                     gl->width(), gl->height(),
                     gl->m_viewMatrix.data(), gl->m_mvp.data(),
                     right_click, shift_down, ctrl_down);

    if (m_skelGL)
    {
        update_skel(gl);
    }

    if (!m_toy->skel->get_editing())
    {
        if (m_meshGL3D != nullptr)
        {
            m_meshGL3D->compute_element();
        }
    }
}

void ToyView::releaseBone(EasyGL *gl)
{
    m_toy->skel->up();

    if (m_skelGL)
    {
        update_skel(gl);
    }
}

void ToyView::deleteBone()
{ // ToDO:
    m_toy->skel->delete_bone();
    if (m_skelGL)
    {
        EasyGL *gl = qobject_cast<EasyGL *>(Three::currentViewer());
        update_skel(gl);
    }

    if (m_restSkelGL)
    {
        EasyGL *gl = qobject_cast<EasyGL *>(Three::currentViewer());
        update_restSkel(gl);
    }
}

void ToyView::recoverRestPose(EasyGL *gl)
{
    m_toy->set_rest_pose();
    if (m_meshGL3D)
    {
        m_meshGL3D->compute_element();
    }
}

void ToyView::update_skel(EasyGL *gl)
{
    m_skelGL->compute_element(gl);
    m_skelGL->update(gl);
}

void ToyView::update_restSkel(EasyGL *gl)
{
    m_restSkelGL->compute_element(gl);
    m_restSkelGL->update(gl);
}

void ToyView::setBoneParent()
{
    if (m_selBones.size() != 2)
    {
        QMessageBox::information(nullptr, "",
                                 "There should be exactly two bones, the first one is child, the second one is parent!");
        for (Bone *b : m_selBones)
        {
            b->is_tip_selected = false;
            b->is_selected = false;
        }
        m_selBones.clear();
        return;
    }

    Bone *child = m_selBones[0];
    Bone *parent = m_selBones[1];
    if (child->parent != nullptr)
    {
        QMessageBox::information(nullptr, "",
                                 "Child already has a parent! Could not specify new one!");
        for (Bone *b : m_selBones)
        {
            b->is_tip_selected = false;
            b->is_selected = false;
        }
        m_selBones.clear();
        return;
    }
    Skeleton<Bone> *skel = m_toy->skel;
    skel->roots.erase(std::remove(skel->roots.begin(), skel->roots.end(), child));
    child->offset = child->rest_tip() - parent->rest_tip();
    child->set_parent(parent);
    child->set_wi(gather_bones(skel->roots).size() - skel->roots.size());
    m_selBones.clear();
}

void ToyView::splitBone()
{
    if (m_selBones.size() != 1)
    {
        QMessageBox::information(nullptr, "",
                                 "There should be exactly one bone!");
        for (Bone *b : m_selBones)
        {
            b->is_line_segment_selected = false;
        }
        m_selBones.clear();
        return;
    }
    Bone *sel_bone = m_selBones[0];
    if (!sel_bone->is_line_segment_selected)
    {
        QMessageBox::information(nullptr, "",
                                 "Line segment should be selected!");
        return;
    }
    sel_bone->split();
    m_selBones.clear();
}

void ToyView::mergeBone()
{
}

void ToyView::drawBone(EasyGL *gl)
{
    m_skelGL->draw(gl);
}

void ToyView::drawRestBone(EasyGL *gl)
{
    m_restSkelGL->draw(gl);
}

void ToyView::draw_mesh2D(EasyGL *gl)
{
    if (m_meshGL2D)
    {
        m_meshGL2D->draw_mesh(gl);
    }
}

void ToyView::update_mesh3D(EasyGL *gl)
{
    if (m_meshGL3D)
    {
        m_meshGL3D->compute_element();
        m_meshGL3D->update(gl);
    }
}

void ToyView::draw_mesh3D(EasyGL *gl)
{
    glEnable(GL_CULL_FACE); // https://blog.csdn.net/shuan9999/article/details/105453951
    if (m_meshGL3D)
    {
        m_meshGL3D->draw_mesh(gl);
    }
}

void ToyView::plus_wi(EasyGL *gl)
{
    if (m_selected_wi >= m_toy->OW.cols() - 1)
    {
        m_selected_wi = 0;
    }
    else
    {
        m_selected_wi += 1;
    }

    if (m_toy->OW.size() > 0)
    {
        m_meshGL3D->compute_skinning_colors(m_toy->OW, m_selected_wi);
        m_meshGL3D->update(gl);
    }
}

void ToyView::minus_wi(EasyGL *gl)
{
    if (m_selected_wi <= 0)
    {
        m_selected_wi = m_toy->OW.cols() - 1;
    }
    else
    {
        m_selected_wi -= 1;
    }

    if (m_toy->OW.size() > 0)
    {
        m_meshGL3D->compute_skinning_colors(m_toy->OW, m_selected_wi);
        m_meshGL3D->update(gl);
    }
}

void ToyView::set_selected_bone_dof_type()
{
    if (m_cur_dof_type >= static_cast<int>(DegreeOfFreedomType::NUM_DOF_TYPES))
    {
        m_cur_dof_type = 0;
    }
    else
    {
        m_cur_dof_type++;
    }

    vector<Bone *> sel = m_toy->skel->find_all_selected();
    for (vector<Bone *>::iterator bi = sel.begin(); bi != sel.end(); bi++)
    {
        // case current value to bool
        int dof_type = (*bi)->tip_dof_type + 1;
        (*bi)->tip_dof_type = (DegreeOfFreedomType)(dof_type);
    }
}

bool ToyView::compute_weightsTet(EasyGL *gl)
{
    // Get the whole mesh to be deformed
    bool flag = m_toy->compute_V3D();
    if (flag == false)
    {
        QMessageBox::warning(QApplication::activeWindow(),
                             "ERROR",
                             "Merge 3D mesh fails, please move the toy to reconnect!\n");
        return false;
    }

    // Automatically compute weights by BBW
    m_toy->compute_weightsTet();
    if (m_meshGL3D == nullptr)
    {
        m_meshGL3D = new MeshGL(&m_toy->m_mesh3D);
    }
    if (m_meshGL3D)
    {
        Q_DEBUG << " m_toy->m_mesh3D=" << m_toy->m_mesh3D.number_of_vertices() << " weights=" << m_toy->OW.rows();
        if (m_toy->m_isSubToyMoved)
        {
            m_meshGL3D->compute_element();
            m_toy->m_isSubToyMoved = false;
        }
        m_selected_wi = 0;
        m_meshGL3D->compute_skinning_colors(m_toy->OW, m_selected_wi);

        gl->m_meshMode = MESH_MODE_SKINNING_COLORED;
        m_meshGL3D->update(gl);
        return true;
    }
    else
    {
        return false;
    }
}

void ToyView::switch_to_deform_mode(EasyGL *gl)
{
    if (m_toy->OW.rows() == 0)
    {
        compute_weightsTet(gl);
        for (EasyGL *v : EasyGL::EasyGLPool())
        {
            update_mesh3D(v);
        }
    }
}

// This is another very important function!!!
void ToyView::addSubMesh3D(EasyGL *gl)
{
    XSubToy *subToy = m_toy->GetCurToy();
    subToy->InflateLaplaceFrom3DPlane(subToy->m_layerThick); // mesh
    subToy->Skeletonize(subToy->m_skelOpt);                  // skeleton
    m_toy->SetupParentChildRelation3D(subToy);               // decide which part this subToy should be connected to
    subToy->Translate(subToy->GetAxisDepthVec());
    m_toy->global_skel_optim();
    getSubMeshGL3D(subToy->GetID(), &subToy->m_curMesh3D);
}

MeshGL *ToyView::getSubMeshGL3D(int id, TMesh *mesh_ptr)
{
    int n = static_cast<int>(m_subMeshGL3D.size());
    if (id > n - 1)
    {
        MeshGL *meshgl = new MeshGL(mesh_ptr);
        m_subMeshGL3D.push_back(meshgl);
        return meshgl;
    }

    MeshGL *meshgl = m_subMeshGL3D[id];
    if (meshgl->mesh != mesh_ptr)
    {
        M_DEBUG << "subMeshGL3D" << id << ": old_mesh_ptr=" << meshgl->mesh << " new_mesh_ptr=" << mesh_ptr << endl;
        meshgl->set_mesh(mesh_ptr);
    }
    return meshgl;
}

void ToyView::deleteSubMesh3D(int id)
{
    int n = static_cast<int>(m_subMeshGL3D.size());
    if (id != n - 1)
    {
        std::swap(m_subMeshGL3D[id], m_subMeshGL3D.back());
    }
    m_subMeshGL3D.pop_back();
}

void ToyView::computeSubMesh3D(int id)
{
    if (id == -1)
        return;
    m_subMeshGL3D[id]->compute_element();
}

void ToyView::computeSubMesh3D()
{
    for (size_t i = 0; i < m_subMeshGL3D.size(); ++i)
    {
        m_subMeshGL3D[i]->compute_element();
    }
}

void ToyView::updateSubMesh3D(int id, EasyGL *gl)
{
    if (id == -1)
        return;
    m_subMeshGL3D[id]->update(gl);
}

void ToyView::updateSubMesh3D(EasyGL *gl)
{
    for (int i = 0; i < m_subMeshGL3D.size(); i++)
    {
        m_subMeshGL3D[i]->update(gl);
    }
}

void ToyView::drawSelectedSubMesh3D(EasyGL *gl)
{
    int i = m_toy->m_curSubToyIdx;
    if (i >= 0 && i < static_cast<int>(m_subMeshGL3D.size()))
        m_subMeshGL3D[i]->draw_mesh(gl);
}

void ToyView::draw_subMesh3D(EasyGL *gl)
{
    for (size_t i = 0; i < m_subMeshGL3D.size(); ++i)
    {
        m_subMeshGL3D[i]->draw_mesh(gl);
    }
}

bool ToyView::pickDefCtrlPts(const QPoint &point, EasyGL *gl)
{
    XSubToy *subToy = m_toy->GetCurToy();

    if (!subToy)
        return false;

    // ToDO: remove IsFrontView
    if (subToy->m_curMesh3D.number_of_vertices() == 0)
        subToy->m_vDefCptIdx = pickPtOnSketchSpline(point, subToy->m_sketchSpline, gl);
    else
        subToy->m_vDefCptIdx = pickPtOnSketchSpline(point, subToy->m_defSpline, gl);

    qDebug() << __FILE__ << " " << __LINE__ << ": pick subToy" << subToy->name().c_str() << ", spline ctrlPt" << subToy->m_vDefCptIdx;

    return subToy->m_vDefCptIdx >= 0;
}

int ToyView::pickPtOnSketchSpline(const QPoint &point, const CSpline &spline, EasyGL *gl)
{
    // point: mouse point on screen space
    Pnt2 cursor(point.x(), point.y());
    cout << __FILE__ << " " << __LINE__ << " pickPtOnSketchSpline cursor=" << cursor.transpose() << endl;
    for (int i = 0; i < spline.GetCtrlPointCount(); i++)
    {
        const Pnt3 &ctrlP = spline.GetCtrlPoint(i);
        Pnt2 p = gl->Cvt3Dto2D(ctrlP);
        if ((p - cursor).norm() < POINT_SIZE)
        {
            cout << __FILE__ << " " << __LINE__ << " pickCtrlPt=" << p.transpose() << endl;
            return i;
        }
    }
    return -1;
}

void ToyView::init_contour_deform(EasyGL *gl)
{
    XSubToy *subToy = m_toy->GetCurToy();
    if (!subToy)
        return;
    // capture contour image
    Vec3 viewDir;
    gl->GetViewDir(viewDir[0], viewDir[1], viewDir[2]);
    gl->capture_contour();
    gl->repaint();
    // extract contour line
    cv::Mat &img = gl->m_contourImg;
    vector<vector<double>> &depth = gl->m_depthImg;
    vector<vector<cv::Point>> contours;
    ContourExtractor::detect_contour(img, contours);
    assert(contours.size() >= 1 && "Contour is not captured, cannot perform shape deformation!");
    vector<Pnt3> cont;
    double avgDepth = 0;
    for (const cv::Point &p : contours[0])
    {
        avgDepth += depth[p.y][p.x];
    }
    avgDepth /= contours[0].size();
    for (const cv::Point &p : contours[0])
    {
        cont.push_back(gl->Cvt2Dto3D(p.x, p.y, avgDepth));
        // cout << __FILE__ << " " << __LINE__ << " p=" << p
        //      << " avgDepth=" << avgDepth
        //      << " depth=" << depth[p.y][p.x] << " 3d=" << cont.back().transpose() << endl;
    }

    CSpline &spline = subToy->m_defSpline;
    vector<vector<Pnt3>> &sketchPolyLines = subToy->m_defPolyLines;
    vector<Pnt3> &sketchPolyLine = subToy->m_defPolyLine;

    spline.Clear();
    spline.ChangeMode(CSpline::SPLMODE_CLOSED_SPLINE);
    sketchPolyLines.clear();
    sketchPolyLine.clear();

    sketchline2Spline(cont, spline, gl);
    CSketchManager::ConvertSplineToPolyLines(spline, sketchPolyLines);
    CSketchManager::GetPolyLine(sketchPolyLines, sketchPolyLine);
    subToy->InitContourDeform(viewDir);

    // visualization
    computeDefContour();
    updateDefContour(gl);
}

void ToyView::modifySubContourOnMouseMove(const QPoint &point, EasyGL *gl)
{
    XSubToy *subToy = m_toy->GetCurToy();
    if (!subToy)
        return;

    int pickPtIdx = subToy->m_vDefCptIdx;
    if (pickPtIdx < 0)
        return;

    Vec3 viewDir;
    gl->GetViewDir(viewDir[0], viewDir[1], viewDir[2]);
    auto newP = gl->Cvt2Dto3D(point.x(), point.y());
    if (subToy->m_curMesh3D.number_of_vertices() == 0) // the newly created contour line, the meshes has not be created yet
    {
        vector<Pnt3> &ctrlPts = subToy->m_sketchSpline.P;
        ctrlPts[pickPtIdx] = Pnt3(newP.x(), newP.y(), newP.z());
        // update contour line
        vector<vector<Pnt3>> &sketchPolyLines = subToy->m_sketchPolyLines;
        vector<Pnt3> &sketchPolyLine = subToy->m_sketchPolyLine;
        CSketchManager::ConvertSplineToPolyLines(subToy->m_sketchSpline, sketchPolyLines);
        CSketchManager::GetPolyLine(sketchPolyLines, sketchPolyLine);
        subToy->SetCentroid();
        subToy->SetRotToXY();
        subToy->SetSketchPolyLineOnXY();

        // visualization
        // update control points
        int id = subToy->GetID();
        const GLVec3 &origP = subCtrlPts[id][pickPtIdx];
        subCtrlPts[id][pickPtIdx] = GLVec3(newP.x(), newP.y(), newP.z());

        std::vector<GLVec3> &gl_spline = subContours[id];
        gl_spline.clear();
        for (const Pnt3 &p : sketchPolyLine)
        {
            gl_spline.push_back(GLVec3(p.x(), p.y(), p.z()));
        }
    }
    else
    {
        vector<Pnt3> &ctrlPts = subToy->m_defSpline.P;
        ctrlPts[pickPtIdx] = Pnt3(newP.x(), newP.y(), newP.z());
        vector<vector<Pnt3>> &sketchPolyLines = subToy->m_defPolyLines;
        vector<Pnt3> &sketchPolyLine = subToy->m_defPolyLine;
        CSketchManager::ConvertSplineToPolyLines(subToy->m_defSpline, sketchPolyLines);
        CSketchManager::GetPolyLine(sketchPolyLines, sketchPolyLine);
        computeDefContour();

        subToy->DeformContour(viewDir);
        computeSubMesh3D();

        if (gl->m_viewMode != VIEW_PERSP)
        {
            subToy->InitContour(subToy->m_defSpline.P);
        }
    }
}

void ToyView::computeDefContour()
{
    XSubToy *subToy = m_toy->GetCurToy();
    if (!subToy)
        return;
    defContour.clear();
    for (const Pnt3 &p : subToy->m_defPolyLine)
    {
        defContour.push_back(GLVec3(p.x(), p.y(), p.z()));
    }

    CSpline &spline = subToy->m_defSpline;
    defCtrlPts.clear();
    for (int i = 0; i < spline.GetCtrlPointCount(); i++)
    {
        const Pnt3 &p = spline.GetCtrlPoint(i);
        defCtrlPts.push_back(GLVec3(p.x(), p.y(), p.z()));
    }
}

void ToyView::updateDefContour(EasyGL *gl)
{
    defContour_gl.update(gl);
    defCtrlPts_gl.update(gl);
}

void ToyView::drawDefContour(EasyGL *gl)
{
    if (defContour.size() <= 3)
        return;
    // !!! must add this line, because
    defContour_gl.setEdgePtr(&defContour);
    defContour_gl.draw(gl);
    float point_size = POINT_SIZE * gl->GetNDCDisOfOnePixelOn2D();
    defCtrlPts_gl.setPointSize(point_size);
    defCtrlPts_gl.setPointPtr(&defCtrlPts);
    defCtrlPts_gl.draw(gl);
}

void ToyView::addMeshes(EasyGL *gl)
{
    if (gl->m_viewMode != VIEW_FRONT && gl->m_curLayer != 0 && m_toy->GetCurToy()->m_curMesh3D.number_of_vertices() == 0)
    {
        QMessageBox::warning(QApplication::activeWindow(),
                             "ERROR",
                             "Layered draw function is only enabled under front view\n");
        delete_subContour();
        return;
    }

    addSubMeshCoarse(gl);
    addSubMeshFine(gl);
    addSubMesh3D(gl);

    XSubToy *subToy = m_toy->GetCurToy();
    int id = subToy->GetID();
    computeSubContour(id);
    m_subMeshGLCoarse[id]->compute_element();
    m_subMeshGLFine[id]->compute_element();
    m_subMeshGL3D[id]->compute_element();

    pick_current_subtoy();
    updateSubToyGL();
}

void ToyView::deleteSubToy()
{
    XSubToy *subToy = m_toy->GetCurToy();
    if (subToy)
    {
        int id = subToy->GetID();
        deleteSubContour(id);
        deleteSubMeshCoarse(id);
        deleteSubMeshFine(id);
        deleteSubMesh3D(id);
        m_toy->DelSubToy(subToy);

        updateSubToyGL();
    }
}

void ToyView::updateSubToyGL()
{
    for (auto v : EasyGL::EasyGLPool())
    {
        update_subContours(v);
        updateSubMeshCoarse(v);
        updateSubMeshFine(v);
        updateSubMesh3D(v);
        update_skel(v);
        update_restSkel(v);
        v->repaint();
    }
}

void ToyView::TexturePaintOnLBDown(QPoint point, EasyGL *gl)
{
    bool picked = m_toy->pick_subtoy(point.x(), point.y(), gl->width(), gl->height(), gl->m_mvp.data());
    if (picked)
    {
        XSubToy *subToy = m_toy->GetCurToy();
        TMesh &mesh = subToy->m_curMesh3D;
        FreehandLine &texPaintLine = mesh.m_texPaint.m_texPaintLine;
        int fid = m_toy->m_pickFid;
        Pnt3 barycoord = m_toy->m_pickFbc;
        Pnt3 baseP = m_toy->m_pickFPnt;

        texPaintLine.Clear();
        texPaintLine.fid.push_back(fid);
        texPaintLine.Pos3d.push_back(baseP);
        texPaintLine.eid.push_back(-1);
        texPaintLine.uvw.push_back(barycoord);

        TexturePaintDraw();
    }
}

void ToyView::TexturePaintDraw()
{
    QColor color(SUBMESH_COLOR);
    ColorI paintColor((BYTE)color.red(), (BYTE)color.green(), (BYTE)color.blue());
    m_toy->TexturePaintDraw(paintColor);
}

void ToyView::TextureImageOnMouseMove(QPoint point, EasyGL *gl)
{
}

void ToyView::TextureImageOnLBUp(QPoint point, EasyGL *gl)
{
    // extract region from background picture
    // CSketchImagePaintDlg
    //
}

void ToyView::SelTexImageBy2DSplines(EasyGL *gl)
{
    // CSketchImagePaintDlg: onOK
    // PolygonImage
}

void ToyView::TextureImageOnDbClk(QPoint point, EasyGL *gl)
{
    // PolygonImage
    // get map cpts and vfids
    // mesh.SurfaceTrim
    // map to bg picture and get uv of verts
    // copy image to texture
}

void ToyView::skeletonize(XSubToy *subToy)
{
    // cout << __FILE__ << " " << __LINE__ << " partition...." << endl;
    // std::vector<Pnt3> &sketchLine = subToy->m_sketchPolyLine;
    // double line_len = CalLineLength(sketchLine);
    // std::vector<Pnt3> simp_polyline;
    // SimplifyPolyLine(sketchLine, simp_polyline, 10);
    // if ((simp_polyline.back() - simp_polyline.front()).norm() < 0.1)
    // {
    //     simp_polyline.pop_back();
    // }
    // egl::writeLine("output/poly.txt", simp_polyline);
    // cout << __FILE__ << " " << __LINE__ << " simp_polyline=" << simp_polyline.size() << " line_len=" << line_len << endl;
    // std::vector<std::vector<Pnt3>> part_polys;
    // igl::copyleft::cgal::partition(simp_polyline, part_polys);
    // cout << __FILE__ << " " << __LINE__ << " part polys=" << part_polys.size() << endl;

    cout << __FILE__ << " " << __LINE__ << " skeletonize...." << endl;
    //----------------------------Convert Polygon to Binary Image----------------------

    BBox2 bbox;
    XSubToy *parent = subToy->m_parent;
    if (parent)
    {
        for (const Pnt3 &p : parent->m_sketchPolyLine)
        {
            if (p.x() < bbox.xmin)
            {
                bbox.xmin = p.x();
            }
            if (p.y() < bbox.ymin)
            {
                bbox.ymin = p.y();
            }
            if (p.x() > bbox.xmax)
            {
                bbox.xmax = p.x();
            }
            if (p.y() > bbox.ymax)
            {
                bbox.ymax = p.y();
            }
        }

        for (XSubToy *child : parent->m_children)
            for (const Pnt3 &p : child->m_sketchPolyLine)
            {
                if (p.x() < bbox.xmin)
                {
                    bbox.xmin = p.x();
                }
                if (p.y() < bbox.ymin)
                {
                    bbox.ymin = p.y();
                }
                if (p.x() > bbox.xmax)
                {
                    bbox.xmax = p.x();
                }
                if (p.y() > bbox.ymax)
                {
                    bbox.ymax = p.y();
                }
            }
    }
    else
    {
        for (const Pnt3 &p : subToy->m_sketchPolyLine)
        {
            if (p.x() < bbox.xmin)
            {
                bbox.xmin = p.x();
            }
            if (p.y() < bbox.ymin)
            {
                bbox.ymin = p.y();
            }
            if (p.x() > bbox.xmax)
            {
                bbox.xmax = p.x();
            }
            if (p.y() > bbox.ymax)
            {
                bbox.ymax = p.y();
            }
        }
    }

    double xmin = bbox.xmin + 0.5;
    double ymin = bbox.ymin + 0.5;
    double xmax = bbox.xmax + 0.5;
    double ymax = bbox.ymax + 0.5;
    double padding_x = (xmax - xmin) / 4.0; // 0.25
    double padding_y = (ymax - ymin) / 4.0;

    int W = (xmax - xmin) + 2 * padding_x;
    int H = (ymax - ymin) + 2 * padding_y;

    cv::Mat img = cv::Mat::zeros(H, W, CV_8UC1);
    if (parent)
    {
        std::vector<std::vector<cv::Point>> contours;
        vector<cv::Point> cont;
        for (const Pnt3 &p : parent->m_sketchPolyLine)
        {
            int x = int(p.x() + 0.5);
            int y = int(p.y() + 0.5);
            cont.emplace_back(x - xmin + padding_x, y - ymin + padding_y);
        }
        contours.push_back(cont);
        cv::drawContours(img, contours, -1, cv::Scalar(1), -1);

        cont.clear();
        contours.clear();
        for (XSubToy *child : parent->m_children)
        {
            for (const Pnt3 &p : child->m_sketchPolyLine)
            {
                int x = int(p.x() + 0.5);
                int y = int(p.y() + 0.5);
                cont.emplace_back(x - xmin + padding_x, y - ymin + padding_y);
            }
            contours.push_back(cont);
            cv::drawContours(img, contours, -1, cv::Scalar(1), -1);
        }
    }
    else
    {
        std::vector<std::vector<cv::Point>> contours;
        vector<cv::Point> cont;
        for (const Pnt3 &p : subToy->m_sketchPolyLine)
        {
            int x = int(p.x() + 0.5);
            int y = int(p.y() + 0.5);
            cont.emplace_back(x - xmin + padding_x, y - ymin + padding_y);
        }
        contours.push_back(cont);
        cv::drawContours(img, contours, -1, cv::Scalar(1), -1);
    }

    std::vector<std::vector<cv::Point>> contours;

    cout << __FILE__ << " " << __LINE__ << " img size W=" << W << " H=" << H << endl;
    // cv::Mat flip_img;
    // cv::flip(img, flip_img, 1);
    cv::imwrite("output/contour.jpg", img * 255);

    contours.clear();
    ContourExtractor::detect_contour(img, contours);
    cout << __FILE__ << " " << __LINE__ << " partition...." << endl;
    std::vector<Pnt3> sketchLine;
    for (const cv::Point &p : contours[0])
    {
        sketchLine.push_back(Pnt3(p.x, p.y, 0));
    }
    double line_len = CalLineLength(sketchLine);
    std::vector<Pnt3> simp_polyline;
    SimplifyPolyLine(sketchLine, simp_polyline, 3);
    if ((simp_polyline.back() - simp_polyline.front()).norm() < 0.1)
    {
        simp_polyline.pop_back();
    }
    if (IsClockWise(simp_polyline))
    {
        std::reverse(simp_polyline.begin(), simp_polyline.end());
    }
    egl::writeLine("output/poly.txt", simp_polyline);
    cout << __FILE__ << " " << __LINE__ << " simp_polyline=" << simp_polyline.size() << " line_len=" << line_len << endl;
    std::vector<std::vector<Pnt3>> part_polys;
    igl::copyleft::cgal::partition(simp_polyline, part_polys);
    cout << __FILE__ << " " << __LINE__ << " part polys=" << part_polys.size() << endl;

    cv::Mat img_tmp = img.clone();
    Skeletor skeletor(img_tmp.data, img_tmp.cols, img_tmp.rows);
    skeletor.thinning_zs();
    cv::imwrite("output/thin.jpg", img_tmp * 255);
    skeletor.get_branches();
}

void ToyView::push_keyframe()
{
    for (int i = 0; i < m_toy->m_subToys.size(); i++)
    {
        XSubToy *subToy = m_toy->m_subToys[i];
    }
}

void ToyView::pop_keyframe()
{
}

#ifdef ToBEDel
MeshGL::MeshGL(TMesh &mesh_, ToyView *toyview_, XToy *toy_, XSubToy *subToy_)
    : mesh(mesh_),
      toyview(toyview_),
      toy(toy_),
      subToy(subToy_)
{
    mesh_color = toyview_->SUBMESH_COLOR; //
    compute_element();
    compute_color();
    tc.vertices = &vertices;
    tc.idx_data = &idx_data;
    tc.v_normals = &v_normals;
    tc.v_colors = &v_colors;
    tc.v_skinning_colors = &v_skinning_colors;
    tc.v_texCoords = &v_texCoords;
}
#endif

MeshGL::MeshGL(TMesh *mesh_ptr)
    : mesh_color(SUBMESH_COLOR)
{
    set_mesh(mesh_ptr);
}

void MeshGL::removeViewer(EasyGL *gl)
{
    tc.removeViewer(gl);
}

void MeshGL::update(EasyGL *gl)
{
    tc.update(gl);
}

void MeshGL::set_mesh(TMesh *mesh_ptr)
{
    mesh = mesh_ptr;
    compute_element();
    compute_color();
    tc.vertices = &vertices;
    tc.idx_data = &idx_data;
    tc.v_normals = &v_normals;
    tc.v_colors = &v_colors;
    tc.v_skinning_colors = &v_skinning_colors;
    tc.v_texCoords = &v_texCoords;
}

void MeshGL::draw_mesh(EasyGL *gl)
{
    tc.draw(gl);
}

void MeshGL::compute_element()
{
    GetVData();
    GetFData();
    GetVNormal();
}

void MeshGL::compute_color()
{
    GetVColor();
}

void MeshGL::compute_uv(EasyGL *gl)
{
    if (v_texCoords.size() > 0) //ToDO: now only works when sketchline is totally overlapped with displayed image
        return;
    Q_DEBUG << "compute_uv";
    // ToDO: Notice: only works in front view now!!!
    const GLVec3 &v0 = gl->getBgVert(0);
    const GLVec3 &v3 = gl->getBgVert(3);
    Pnt3 p0(v0.x, v0.y, v0.z), p3(v3.x, v3.y, v3.z);
    const Pnt2 &lb_p = gl->Cvt3Dto2D(p0); //left bottom point
    Vec2 wh = gl->Cvt3Dto2D(p3) - gl->Cvt3Dto2D(p0);
    // v_colors.clear();

    for (Vd vd : mesh->vertices())
    {
        const Pnt3 &p = mesh->point(vd);
        Pnt2 p_screen = gl->Cvt3Dto2D(p);
        Vec2 cur_lb = p_screen - lb_p;
        double x_tex = cur_lb.x() / wh.x();
        double y_tex = cur_lb.y() / wh.y();
        v_texCoords.push_back(GLVec2(x_tex, y_tex));
        // QColor color = viewer->m_bgImg.pixelColor(x_tex * viewer->m_bgImg.width(), (1 - y_tex) * viewer->m_bgImg.height());
        // v_colors.push_back(GLVec3(color.redF(), color.greenF(), color.blueF()));
        // qDebug() << vd.idx() << ": " << p_screen.x() << "," << p_screen.y()
        //          << " cur_lb=" << cur_lb.x() << ", " << cur_lb.y()
        //          << " tex=" << x_tex << ", " << y_tex
        //          << " color=" << v_colors[vd.idx()].x << ", " << v_colors[vd.idx()].y << ", " << v_colors[vd.idx()].z;
    }
}

void MeshGL::compute_skinning_colors(const Eigen::MatrixXd &W, int wi /*weight index*/)
{
    //ToDO: change to GPU
    if (W.rows() <= 0)
    {
        return;
    }
    assert(W.rows() == mesh->number_of_vertices());
    v_skinning_colors.clear();
    for (int i = 0; i < W.rows(); i++)
    {
        v_skinning_colors.push_back(heat_color(W(i, wi)));
    }
    qDebug() << __FILE__ << " " << __LINE__ << " v_skinning_colors=" << v_skinning_colors.size();
}

void MeshGL::GetVData()
{
    vertices.clear();
    for (Vd vd : mesh->vertices())
    {
        const Pnt3 &p = mesh->point(vd);
        vertices.push_back(GLVec3(p.x(), p.y(), p.z())); //ToDO:
    }
}

void MeshGL::GetFData()
{
    idx_data.clear();
    for (Fd fd : mesh->faces())
    {
        Hd hd = mesh->halfedge(fd);
        Hd nxt_hd = mesh->next(hd);
        unsigned int v0 = mesh->source(hd).idx();
        unsigned int v1 = mesh->target(hd).idx();
        unsigned int v2 = mesh->target(nxt_hd).idx();
        idx_data.push_back(v0);
        idx_data.push_back(v1);
        idx_data.push_back(v2);
    }
}

void MeshGL::GetVNormal()
{
    v_normals.clear();
    mesh->ComputeVNormals();

    for (Vd vd : mesh->vertices())
    {
        const Vec3 &normal = mesh->GetVNormal(vd);
        v_normals.push_back(GLVec3(normal.x(), normal.y(), normal.z()));
    }
}

void MeshGL::GetVColor()
{
    v_colors.clear();
    for (Vd vd : mesh->vertices())
    {
        v_colors.push_back(GLVec3(mesh_color.redF(), mesh_color.greenF(), mesh_color.blueF()));
    }
}

// QColor SkelGL::bone_color = QColor(106., 106., 255.);
QColor SkelGL::bone_color = QColor(255, 0, 0);

SkelGL::SkelGL(Skeleton<Bone> *skel_) : skel(skel_)
{
    line_container.setDrawLines(true);
}

void SkelGL::removeViewer(EasyGL *gl)
{
    point_container.removeViewer(gl);
    line_container.removeViewer(gl);
    dragline_container[0].removeViewer(gl);
    dragline_container[1].removeViewer(gl);
}

void SkelGL::update(EasyGL *gl)
{
    float point_size = Bone::BONE_POINT_RADIUS * gl->GetNDCDisOfOnePixelOn2D();
    point_container.setPointSize(point_size);
    line_container.setColor(bone_color);
    line_container.setEdgeWidth(static_cast<float>(Bone::BONE_DIRECTED_LINE_SEGMENT_WIDTH));

    point_container.update(gl);
    line_container.update(gl);
    dragline_container[0].update(gl);
    dragline_container[1].update(gl);
}

void SkelGL::draw(EasyGL *gl)
{
    if (tips.size() < 0)
        return;

    point_container.setPointPtr(&tips);
    line_container.setEdgePtr(&lines);
    dragline_container[0].setEdgePtr(&drag_line[0]);
    dragline_container[1].setEdgePtr(&drag_line[1]);

    point_container.draw(gl);
    line_container.draw(gl);
    dragline_container[0].draw(gl);
    dragline_container[1].draw(gl);
}

void SkelGL::compute_element(EasyGL *gl)
{
    vector<Bone *> bones = gather_bones(skel->roots);
    vector<int> selected;
    vector<GLVec3> colors;
    tips.clear();
    lines.clear();

    int i = 0;
    for (Bone *b : bones)
    {
        if (b->is_root())
        {
            if (b->isAttachBone)
            {
                if (b->isAttachBoneActivated)
                {
                    const Pnt3 &tip = b->rest_tip();
                    tips.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
                }
            }
            else
            {
                if (gl->m_opMode == OPMODE_BBW_DEFORM)
                {
                    const Pnt3 &tip = b->tip_as_drawn();
                    tips.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
                }
                else
                {
                    const Pnt3 &tip = b->rest_tip();
                    tips.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
                }
            }

            double pcolor[3] = {1, 1, 0};
            // b->tip_color(pcolor);
            if (!b->isAttachBone || (b->isAttachBone && b->isAttachBoneActivated))
                colors.push_back(GLVec3(pcolor[0] * 0.5, pcolor[1] * 0.5, pcolor[2] * 0.5));
        }

        if (b->get_wi() >= 0)
        {
            if (gl->m_opMode == OPMODE_BBW_DEFORM)
            {
                const Pnt3 &tail = b->tail_as_drawn();
                const Pnt3 &tip = b->tip_as_drawn();
                tips.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
                lines.push_back(GLVec3(tail.x(), tail.y(), tail.z() + 0.2));
                lines.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
            }
            else
            {
                const Pnt3 &tail = b->rest_tail();
                const Pnt3 &tip = b->rest_tip();
                if (!b->isAttachBone || (b->isAttachBone && b->isAttachBoneActivated))
                {
                    tips.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
                    lines.push_back(GLVec3(tail.x(), tail.y(), tail.z() + 0.2));
                    lines.push_back(GLVec3(tip.x(), tip.y(), tip.z() + 0.2));
                }
            }

            // get foreground color for points
            double pcolor[3];
            b->tip_color(pcolor);
            colors.push_back(GLVec3(pcolor[0], pcolor[1], pcolor[2]));
        }

        if (b->is_tip_selected)
        {
            selected.push_back(i);
        }

        if (b->dragging_rotation)
        {
            compute_drag_line(gl, b);
        }
        else
        {
            drag_line[0].clear();
            drag_line[1].clear();
        }
        i++;
    }

    point_container.setSelectedIdxs(selected);
    point_container.setPointColors(colors);
}

void SkelGL::compute_drag_line(EasyGL *gl, Bone *bone)
{
    using namespace Eigen;
    // Draw a line from mouse down to mouse last
    Vector3d d = bone->tip_as_drawn();

    Vector3d s_d;
    EGL::project(d[0], d[1], d[2],
                 gl->width(), gl->height(), gl->m_mvp.data(),
                 s_d[0], s_d[1], s_d[2]);

    Vector3d last;
    EGL::unproject(bone->last_x, bone->last_y, s_d[2],
                   gl->width(), gl->height(), gl->m_mvp.data(),
                   last[0], last[1], last[2]);

    Vector3d down;
    EGL::unproject(bone->down_x, bone->down_y, s_d[2],
                   gl->width(), gl->height(), gl->m_mvp.data(),
                   down[0], down[1], down[2]);

    Vector3d last_x_only;
    EGL::unproject(bone->last_x, bone->down_y, s_d[2],
                   gl->width(), gl->height(), gl->m_mvp.data(),
                   last_x_only[0], last_x_only[1], last_x_only[2]);

    // std::vector<GLVec3> line[2];
    float fDis = 3 * gl->Get3dDisOfOnePixelOn2D();
    drag_line[0].clear();
    compute_stipple_line(down, last, fDis, drag_line[0]);
    compute_stipple_line(last, last_x_only, fDis, drag_line[0]);

    drag_line[1].clear();
    drag_line[1].emplace_back(down[0], down[1], down[2]);
    drag_line[1].emplace_back(last_x_only[0], last_x_only[1], last_x_only[2]);

    QColor guide_color(Bone::DRAG_LINE_GUIDE_COLOR[0],
                       Bone::DRAG_LINE_GUIDE_COLOR[1],
                       Bone::DRAG_LINE_GUIDE_COLOR[2], 255.);

    QColor drag_color(Bone::DRAG_LINE_COLOR[0],
                      Bone::DRAG_LINE_COLOR[1],
                      Bone::DRAG_LINE_COLOR[2], 255.);

    dragline_container[0].setColor(guide_color);
    dragline_container[0].setEdgeWidth(2.0);
    dragline_container[0].setDrawLines(true);
    dragline_container[1].setColor(drag_color);
    dragline_container[1].setEdgeWidth(3.0);
}

void ArcBallGL::removeViewer(EasyGL *gl)
{
    point_container.removeViewer(gl);
}

void ArcBallGL::update(EasyGL *gl)
{
    point_container.setPointPtr(&points);
    dragline_container.setEdgePtr(&drag_line);

    float point_size = 10 * gl->GetNDCDisOfOnePixelOn2D();
    point_container.setPointSize(point_size);
    point_container.setColor(QColor(255, 0, 0));

    dragline_container.setColor(QColor(255, 128, 0));
    dragline_container.setEdgeWidth(1.0);
    dragline_container.setDrawLines(true);

    point_container.update(gl);
    dragline_container.update(gl);
}

void ArcBallGL::draw(EasyGL *gl)
{
    point_container.draw(gl);
    dragline_container.draw(gl);
}

void ArcBallGL::set_centroid(const Pnt3 &p)
{
    centroid_ = p;
    points.clear();
    points.push_back(GLVec3(p.x(), p.y(), p.z()));
}

void ArcBallGL::set_cur_point(const QPoint &qp, EasyGL *gl)
{
    set_cur_point(gl->Cvt2Dto3D(qp.x(), qp.y(), centroid()[2]), gl);
}

void ArcBallGL::set_cur_point(const Pnt3 &cur_point, EasyGL *gl)
{
    cur_point_ = cur_point;

    float fDis = gl->Get3dDisOfOnePixelOn2D();
    drag_line.clear();
    compute_stipple_line(centroid(), cur_point, fDis, drag_line);
}

void PlaneGL::compute_element()
{
    idx_data.clear();
    vertices.clear();
    vnormals.clear();
    vcolors.clear();
    line.clear();

    idx_data.resize(2 * 3);
    vertices.resize(4);
    vnormals.resize(4);
    vcolors.resize(4);

    double half_len = edge_length * 0.5;
    Vec3 nor = plane.normal();
    Vec3 up;
    int min_idx;
    nor.minCoeff(&min_idx);
    if (min_idx == COORD_AXIS_X)
    {
        up = Vec3(0, -nor.z(), nor.y());
    }
    else if (min_idx == COORD_AXIS_Y)
    {
        up = Vec3(-nor.z(), 0, nor.x());
    }
    else if (min_idx == COORD_AXIS_Z)
    {
        up = Vec3(-nor.y(), nor.x(), 0);
    }
    Vec3 side = nor.cross(up);
    up = nor.cross(side);

    // M_DEBUG << "nor=" << nor.transpose() << " side=" << side.transpose() << " up=" << up.transpose() << endl;
    nor.normalize();
    up.normalize();
    side.normalize();

    Pnt3 cent;
    IntersectPlane(plane, Ray(Pnt3(0, 0, 0), nor), cent);
    // M_DEBUG << "plane_cent=" << cent.transpose() << endl;
    Pnt3 p0 = cent - side * half_len + up * half_len; // (-1, 1)
    Pnt3 p1 = cent - side * half_len - up * half_len; // (-1, -1)
    Pnt3 p2 = cent + side * half_len - up * half_len; // (-1, 1)
    Pnt3 p3 = cent + side * half_len + up * half_len; // (1, 1)

    vertices[0] = GLVec3(p0.x(), p0.y(), p0.z());
    vertices[1] = GLVec3(p1.x(), p1.y(), p1.z());
    vertices[2] = GLVec3(p2.x(), p2.y(), p2.z());
    vertices[3] = GLVec3(p3.x(), p3.y(), p3.z());

    for (int i = 0; i < 4; ++i)
    {
        vnormals[i] = GLVec3(nor.x(), nor.y(), nor.z());
        vcolors[i] = GLVec3(222. / 255., 222. / 255., 222. / 255.);
    }

    idx_data[0] = 0;
    idx_data[1] = 1;
    idx_data[2] = 2;
    idx_data[3] = 0;
    idx_data[4] = 2;
    idx_data[5] = 3;

    for (int i = 0; i < 4; ++i)
    {
        line.push_back(vertices[i]);
    }
    line.push_back(vertices[0]);
}

void PlaneGL::update(EasyGL *gl)
{
    tc.vertices = &vertices;
    tc.idx_data = &idx_data;
    tc.v_normals = &vnormals;
    tc.v_colors = &vcolors;

    ec.setEdgePtr(&line);
    ec.setColor(QColor(0, 0, 0));

    tc.update(gl);
    ec.update(gl);
}

void PlaneGL::draw(EasyGL *gl)
{
    tc.draw(gl);
    ec.draw(gl);
}

void AxisGL::compute_element(EasyGL *gl)
{
    calArrow(0.06, 0.12, 10, GLVec3(0, 0, 0), GLVec3(axis_length, 0, 0), axis_x_color, coord_data);
    calArrow(0.06, 0.12, 10, GLVec3(0, 0, 0), GLVec3(0, axis_length, 0), axis_y_color, coord_data);
    calArrow(0.06, 0.12, 10, GLVec3(0, 0, 0), GLVec3(0, 0, axis_length), axis_z_color, coord_data);
}

//  Draws a 3D arrow between the 3D point \p from and the 3D point \p to.
// \p data is filled with the three components of a point, then its normal, and then its color, which makes it filled like this:
// [P1.x-P1.y-P1.z-N1.x-N1.y-N1.z-C1.r-C1.g-C1.b|P2.x-P2.y-P2.z-N2.x-N2.y-N2.z-C2.r-C2.g-C2.b|...]
void AxisGL::calArrow(double r, double R, int prec, // cylinder radius, cone radius, precision
                      GLVec3 from, GLVec3 to,
                      GLVec3 color,
                      std::vector<float> &data)
{
    using std::acos;
    using std::cos;
    using std::sin;
    GLVec3 temp = to - from;
    QVector3D dir = QVector3D(float(temp.x), float(temp.y), float(temp.z));
    QMatrix4x4 mat;
    mat.setToIdentity();
    mat.translate(float(from.x), float(from.y), float(from.z));
    mat.scale(dir.length());
    dir.normalize();
    float angle = 0.0;
    if (std::sqrt((dir.x() * dir.x() + dir.y() * dir.y())) > 1)
        angle = 90.0f;
    else
        angle = float(acos(dir.y() / std::sqrt(dir.lengthSquared())) * 180.0 / M_PI);

    QVector3D axis;
    axis = QVector3D(dir.z(), 0, -dir.x());
    mat.rotate(angle, axis);

    //Head
    const float Rf = static_cast<float>(R);
    for (int d = 0; d < 360; d += 360 / prec)
    {
        float D = (float)(d * M_PI / 180.);
        float a = (float)std::atan(Rf / 0.33);
        QVector4D p(0., 1., 0, 1.);
        QVector4D n(Rf * sin(D), sin(a), Rf * cos(D), 1.);
        QVector4D pR = mat * p;
        QVector4D nR = mat * n;

        //point A1
        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back((float)color.x);
        data.push_back((float)color.y);
        data.push_back((float)color.z);

        //point B1
        p = QVector4D(Rf * sin(D), 0.66f, Rf * cos(D), 1.f);
        n = QVector4D(sin(D), sin(a), cos(D), 1.);
        pR = mat * p;
        nR = mat * n;
        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back((float)color.x);
        data.push_back((float)color.y);
        data.push_back((float)color.z);
        //point C1
        D = float((d + 360 / prec) * M_PI / 180.0);
        p = QVector4D(Rf * sin(D), 0.66f, Rf * cos(D), 1.f);
        n = QVector4D(sin(D), sin(a), cos(D), 1.0);
        pR = mat * p;
        nR = mat * n;

        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back((float)color.x);
        data.push_back((float)color.y);
        data.push_back((float)color.z);
    }

    //cylinder
    //body of the cylinder
    const float rf = static_cast<float>(r);
    for (int d = 0; d < 360; d += 360 / prec)
    {
        //point A1
        float D = float(d * M_PI / 180.0);
        QVector4D p(rf * sin(D), 0.66f, rf * cos(D), 1.f);
        QVector4D n(sin(D), 0.f, cos(D), 1.f);
        QVector4D pR = mat * p;
        QVector4D nR = mat * n;

        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back(float(color.x));
        data.push_back(float(color.y));
        data.push_back(float(color.z));
        //point B1
        p = QVector4D(rf * sin(D), 0, rf * cos(D), 1.0);
        n = QVector4D(sin(D), 0, cos(D), 1.0);
        pR = mat * p;
        nR = mat * n;

        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back(float(color.x));
        data.push_back(float(color.y));
        data.push_back(float(color.z));
        //point C1
        D = float((d + 360 / prec) * M_PI / 180.0);
        p = QVector4D(rf * sin(D), 0, rf * cos(D), 1.0);
        n = QVector4D(sin(D), 0, cos(D), 1.0);
        pR = mat * p;
        nR = mat * n;
        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back(float(color.x));
        data.push_back(float(color.y));
        data.push_back(float(color.z));
        //point A2
        D = float((d + 360 / prec) * M_PI / 180.0);

        p = QVector4D(rf * sin(D), 0, rf * cos(D), 1.0);
        n = QVector4D(sin(D), 0, cos(D), 1.0);
        pR = mat * p;
        nR = mat * n;
        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back(float(color.x));
        data.push_back(float(color.y));
        data.push_back(float(color.z));
        //point B2
        p = QVector4D(rf * sin(D), 0.66f, rf * cos(D), 1.f);
        n = QVector4D(sin(D), 0, cos(D), 1.0);
        pR = mat * p;
        nR = mat * n;
        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back(float(color.x));
        data.push_back(float(color.y));
        data.push_back(float(color.z));
        //point C2
        D = float(d * M_PI / 180.0);
        p = QVector4D(rf * sin(D), 0.66f, rf * cos(D), 1.f);
        n = QVector4D(sin(D), 0.f, cos(D), 1.f);
        pR = mat * p;
        nR = mat * n;
        data.push_back(pR.x());
        data.push_back(pR.y());
        data.push_back(pR.z());
        data.push_back(nR.x());
        data.push_back(nR.y());
        data.push_back(nR.z());
        data.push_back(float(color.x));
        data.push_back(float(color.y));
        data.push_back(float(color.z));
    }
}
back to top