Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "tmeshimage.h"
Toshihiro Shimizu 890ddd
#include "tgl.h"
Toshihiro Shimizu 890ddd
#include "tundo.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzExt includes
Toshihiro Shimizu 890ddd
#include "ext/plasticdeformerstorage.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// tcg includes
Toshihiro Shimizu 890ddd
#include "tcg/tcg_macros.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_point_ops.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_iterator_ops.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_function_types.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_deleter_types.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_unique_ptr.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// boost includes
Toshihiro Shimizu 890ddd
#include <boost unordered_set.hpp=""></boost>
Toshihiro Shimizu 890ddd
#include <boost unordered_map.hpp=""></boost>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace tcg::bgl;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include <boost breadth_first_search.hpp="" graph=""></boost>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// STD includes
Toshihiro Shimizu 890ddd
#include <stack></stack>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "plastictool.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace PlasticToolLocals;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    Local namespace  stuff
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
typedef PlasticTool::MeshIndex MeshIndex;
Toshihiro Shimizu 890ddd
typedef TTextureMesh::vertex_type vertex_type;
Toshihiro Shimizu 890ddd
typedef TTextureMesh::edge_type edge_type;
Toshihiro Shimizu 890ddd
typedef TTextureMesh::face_type face_type;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool borderEdge(const TTextureMesh &mesh, int e) {
Shinya Kitaoka 120a6e
  return (mesh.edge(e).facesCount() < 2);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool borderVertex(const TTextureMesh &mesh, int v) {
Shinya Kitaoka 120a6e
  const TTextureVertex &vx = mesh.vertex(v);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  tcg::vertex_traits<ttexturevertex>::edges_const_iterator et,</ttexturevertex>
Shinya Kitaoka 120a6e
      eEnd(vx.edgesEnd());
Shinya Kitaoka 120a6e
  for (et = vx.edgesBegin(); et != eEnd; ++et) {
Shinya Kitaoka 120a6e
    if (borderEdge(mesh, *et)) return true;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return false;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool testSwapEdge(const TTextureMesh &mesh, int e) {
Shinya Kitaoka 120a6e
  return (mesh.edge(e).facesCount() == 2);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool testCollapseEdge(const TTextureMesh &mesh, int e) {
Shinya Kitaoka 120a6e
  struct Locals {
Shinya Kitaoka 120a6e
    const TTextureMesh &m_mesh;
Shinya Kitaoka 120a6e
    int m_e;
Shinya Kitaoka 120a6e
    const TTextureMesh::edge_type &m_ed;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    bool testTrianglesCount() {
Shinya Kitaoka 120a6e
      // There must be at least one remanining triangle
Shinya Kitaoka 120a6e
      return (m_mesh.facesCount() > m_ed.facesCount());
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    bool testBoundary() {
Shinya Kitaoka 120a6e
      // Must not join two non-adjacent boundary vertices
Shinya Kitaoka 120a6e
      return (!borderVertex(m_mesh, m_ed.vertex(0)) ||
Shinya Kitaoka 120a6e
              !borderVertex(m_mesh, m_ed.vertex(1)) || borderEdge(m_mesh, m_e));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    bool testAdjacency() {
Shinya Kitaoka 120a6e
      // See TriMesh<>::collapseEdge()
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Retrieve allowed adjacent vertices
Shinya Kitaoka 120a6e
      int f, fCount = m_ed.facesCount();
Shinya Kitaoka 120a6e
      int allowedV[6], *avt, *avEnd = allowedV + 3 * fCount;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      for (f = 0, avt = allowedV; f != fCount; ++f, avt += 3)
Shinya Kitaoka 120a6e
        m_mesh.faceVertices(m_ed.face(f), avt[0], avt[1], avt[2]);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Test adjacent vertices
Shinya Kitaoka 120a6e
      int v0 = m_ed.vertex(0), v1 = m_ed.vertex(1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      const vertex_type &vx0 = m_mesh.vertex(v0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      tcg::vertex_traits<vertex_type>::edges_const_iterator et,</vertex_type>
Shinya Kitaoka 120a6e
          eEnd = vx0.edgesEnd();
Shinya Kitaoka 120a6e
      for (et = vx0.edgesBegin(); et != eEnd; ++et) {
Shinya Kitaoka 120a6e
        int otherV = m_mesh.edge(*et).otherVertex(v0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        if (m_mesh.edgeInciding(v1, otherV) >= 0) {
Shinya Kitaoka 120a6e
          // Adjacent vertex - must be found in the allowed list
Shinya Kitaoka 120a6e
          if (std::find(allowedV, avEnd, otherV) == avEnd) return false;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      return true;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } locals = {mesh, e, mesh.edge(e)};
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return (locals.testTrianglesCount() && locals.testBoundary() &&
Shinya Kitaoka 120a6e
          locals.testAdjacency());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    PlasticToolLocals  stuff
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace PlasticToolLocals {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct Closer {
Shinya Kitaoka 120a6e
  const TTextureMesh &m_mesh;
Shinya Kitaoka 120a6e
  TPointD m_pos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double dist2(const TTextureMesh::vertex_type &a) {
Shinya Kitaoka 120a6e
    return tcg::point_ops::dist2<tpointd>(a.P(), m_pos);</tpointd>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double dist2(const TTextureMesh::edge_type &a) {
Shinya Kitaoka 120a6e
    const TTextureMesh::vertex_type &avx0 = m_mesh.vertex(a.vertex(0)),
Shinya Kitaoka 120a6e
                                    &avx1 = m_mesh.vertex(a.vertex(1));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    return sq(tcg::point_ops::segDist<tpointd>(avx0.P(), avx1.P(), m_pos));</tpointd>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool operator()(const TTextureMesh::vertex_type &a,
Shinya Kitaoka 120a6e
                  const TTextureMesh::vertex_type &b) {
Shinya Kitaoka 120a6e
    return (dist2(a) < dist2(b));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool operator()(const TTextureMesh::edge_type &a,
Shinya Kitaoka 120a6e
                  const TTextureMesh::edge_type &b) {
Shinya Kitaoka 120a6e
    return (dist2(a) < dist2(b));
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==============================================================================
Toshihiro Shimizu 890ddd
Campbell Barton 8c6c57
static std::pair<double, int=""> closestVertex(const TTextureMesh &mesh,</double,>
Campbell Barton 8c6c57
                                            const TPointD &pos) {
Shinya Kitaoka 120a6e
  Closer closer = {mesh, pos};
Shinya Kitaoka 120a6e
  int vIdx      = int(
Shinya Kitaoka 120a6e
      std::min_element(mesh.vertices().begin(), mesh.vertices().end(), closer)
Shinya Kitaoka 120a6e
          .index());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return std::make_pair(closer.dist2(mesh.vertex(vIdx)), vIdx);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Campbell Barton 8c6c57
static std::pair<double, int=""> closestEdge(const TTextureMesh &mesh,</double,>
Campbell Barton 8c6c57
                                          const TPointD &pos) {
Shinya Kitaoka 120a6e
  Closer closer = {mesh, pos};
Shinya Kitaoka 120a6e
  int eIdx =
Shinya Kitaoka 120a6e
      int(std::min_element(mesh.edges().begin(), mesh.edges().end(), closer)
Shinya Kitaoka 120a6e
              .index());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return std::make_pair(closer.dist2(mesh.edge(eIdx)), eIdx);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
std::pair<double, meshindex=""> closestVertex(const TMeshImage &mi,</double,>
Shinya Kitaoka 120a6e
                                           const TPointD &pos) {
Shinya Kitaoka 120a6e
  std::pair<double, meshindex=""> closest((std::numeric_limits<double>::max)(),</double></double,>
Shinya Kitaoka 120a6e
                                       MeshIndex());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TMeshImage::meshes_container &meshes = mi.meshes();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TMeshImage::meshes_container::const_iterator mt, mEnd = meshes.end();
Shinya Kitaoka 120a6e
  for (mt = meshes.begin(); mt != mEnd; ++mt) {
Shinya Kitaoka 120a6e
    const std::pair<double, int=""> &candidateIdx = closestVertex(**mt, pos);</double,>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    std::pair<double, meshindex=""> candidate(</double,>
Shinya Kitaoka 120a6e
        candidateIdx.first,
Shinya Kitaoka 120a6e
        MeshIndex(mt - meshes.begin(), candidateIdx.second));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (candidate < closest) closest = candidate;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return closest;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
std::pair<double, meshindex=""> closestEdge(const TMeshImage &mi,</double,>
Shinya Kitaoka 120a6e
                                         const TPointD &pos) {
Shinya Kitaoka 120a6e
  std::pair<double, meshindex=""> closest((std::numeric_limits<double>::max)(),</double></double,>
Shinya Kitaoka 120a6e
                                       MeshIndex());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TMeshImage::meshes_container &meshes = mi.meshes();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TMeshImage::meshes_container::const_iterator mt, mEnd = meshes.end();
Shinya Kitaoka 120a6e
  for (mt = meshes.begin(); mt != mEnd; ++mt) {
Shinya Kitaoka 120a6e
    const std::pair<double, int=""> &candidateIdx = closestEdge(**mt, pos);</double,>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    std::pair<double, meshindex=""> candidate(</double,>
Shinya Kitaoka 120a6e
        candidateIdx.first,
Shinya Kitaoka 120a6e
        MeshIndex(mt - meshes.begin(), candidateIdx.second));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (candidate < closest) closest = candidate;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return closest;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    Cut Mesh  operation
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct EdgeCut {
Shinya Kitaoka 120a6e
  int m_vIdx;  //!< Vertex index to cut from.
Shinya Kitaoka 120a6e
  int m_eIdx;  //!< Edge index to cut.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  EdgeCut(int vIdx, int eIdx) : m_vIdx(vIdx), m_eIdx(eIdx) {}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
struct VertexOccurrence {
Shinya Kitaoka 120a6e
  int m_count;               //!< Number of times a vertex occurs.
Shinya Kitaoka 120a6e
  int m_adjacentEdgeIdx[2];  //!< Edge indexes of which a vertex is endpoint.
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool buildEdgeCuts(const TMeshImage &mi,
Shinya Kitaoka 120a6e
                   const PlasticTool::MeshSelection &edgesSelection,
Shinya Kitaoka 120a6e
                   int &meshIdx, std::vector<edgecut> &edgeCuts) {</edgecut>
Shinya Kitaoka 120a6e
  typedef PlasticTool::MeshSelection::objects_container edges_container;
Shinya Kitaoka 120a6e
  typedef PlasticTool::MeshIndex MeshIndex;
Shinya Kitaoka 120a6e
  typedef boost::unordered_map<int, vertexoccurrence=""> VertexOccurrencesMap;</int,>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static bool differentMesh(const MeshIndex &a, const MeshIndex &b) {
Shinya Kitaoka 120a6e
      return (a.m_meshIdx != b.m_meshIdx);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    static int testSingleMesh(const edges_container &edges) {
Shinya Kitaoka 120a6e
      assert(!edges.empty());
Shinya Kitaoka 120a6e
      return (std::find_if(edges.begin(), edges.end(),
Shinya Kitaoka 120a6e
                           tcg::bind2nd(&differentMesh, edges.front())) ==
Shinya Kitaoka 120a6e
              edges.end())
Shinya Kitaoka 120a6e
                 ? edges.front().m_meshIdx
Shinya Kitaoka 120a6e
                 : -1;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    static bool testNoBoundaryEdge(const TTextureMesh &mesh,
Shinya Kitaoka 120a6e
                                   const edges_container &edges) {
Shinya Kitaoka 120a6e
      edges_container::const_iterator et, eEnd = edges.end();
Shinya Kitaoka 120a6e
      for (et = edges.begin(); et != eEnd; ++et)
Shinya Kitaoka 120a6e
        if (::borderEdge(mesh, et->m_idx)) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      return true;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    static bool buildVertexOccurrences(
Shinya Kitaoka 120a6e
        const TTextureMesh &mesh, const edges_container &edges,
Shinya Kitaoka 120a6e
        VertexOccurrencesMap &vertexOccurrences) {
Shinya Kitaoka 120a6e
      // Calculate vertex occurrences as edge endpoints
Shinya Kitaoka 120a6e
      edges_container::const_iterator et, eEnd = edges.end();
Shinya Kitaoka 120a6e
      for (et = edges.begin(); et != eEnd; ++et) {
Shinya Kitaoka 120a6e
        const edge_type &ed = mesh.edge(et->m_idx);
Shinya Kitaoka 120a6e
        int v0 = ed.vertex(0), v1 = ed.vertex(1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        VertexOccurrence &vo0 = vertexOccurrences[v0],
Shinya Kitaoka 120a6e
                         &vo1 = vertexOccurrences[v1];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        if (vo0.m_count > 1 || vo1.m_count > 1) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        vo0.m_adjacentEdgeIdx[vo0.m_count++] =
Shinya Kitaoka 120a6e
            vo1.m_adjacentEdgeIdx[vo1.m_count++] = et->m_idx;
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      return true;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    static bool buildEdgeCuts(const TTextureMesh &mesh,
Shinya Kitaoka 120a6e
                              const edges_container &edges,
Shinya Kitaoka 120a6e
                              std::vector<edgecut> &edgeCuts) {</edgecut>
Shinya Kitaoka 120a6e
      VertexOccurrencesMap vertexOccurrences;
Shinya Kitaoka 120a6e
      if (!buildVertexOccurrences(mesh, edges, vertexOccurrences)) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // Build endpoints (exactly 2)
Shinya Kitaoka 120a6e
      int endPoints[2];
Shinya Kitaoka 120a6e
      int epCount = 0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      VertexOccurrencesMap::iterator ot, oEnd = vertexOccurrences.end();
Shinya Kitaoka 120a6e
      for (ot = vertexOccurrences.begin(); ot != oEnd; ++ot) {
Shinya Kitaoka 120a6e
        if (ot->second.m_count == 1) {
Shinya Kitaoka 120a6e
          if (epCount > 1) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          endPoints[epCount++] = ot->first;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      if (epCount != 2) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // Pick the first endpoint on the boundary, if any (otherwise, just pick
Shinya Kitaoka 120a6e
      // one)
Shinya Kitaoka 120a6e
      int *ept, *epEnd = endPoints + 2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      ept = std::find_if(endPoints, epEnd, tcg::bind1st(&borderVertex, mesh));
Shinya Kitaoka 120a6e
      if (ept == epEnd) {
Shinya Kitaoka 120a6e
        // There is no boundary endpoint
Shinya Kitaoka 120a6e
        if (edges.size() < 2)  // We should not cut the mesh on a
Shinya Kitaoka 120a6e
          return false;        // single edge - no vertex to duplicate!
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        ept = endPoints;
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      // Build the edge cuts list, expanding the edges selection from
Shinya Kitaoka 120a6e
      // the chosen endpoint
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      edgeCuts.push_back(EdgeCut(  // Build the first EdgeCut separately
Shinya Kitaoka 120a6e
          *ept, vertexOccurrences[*ept].m_adjacentEdgeIdx[0]));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      int e, eCount = int(edges.size());  // Build the remaining ones
Shinya Kitaoka 120a6e
      for (e = 1; e != eCount; ++e) {
Shinya Kitaoka 120a6e
        const EdgeCut &lastCut = edgeCuts.back();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        int vIdx = mesh.edge(lastCut.m_eIdx).otherVertex(lastCut.m_vIdx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        const int(&adjEdges)[2] = vertexOccurrences[vIdx].m_adjacentEdgeIdx;
Shinya Kitaoka 120a6e
        int eIdx = (adjEdges[0] == lastCut.m_eIdx) ? adjEdges[1] : adjEdges[0];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        edgeCuts.push_back(EdgeCut(vIdx, eIdx));
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      return true;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  };
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const edges_container &edges = edgesSelection.objects();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Trivial early bailouts
Shinya Kitaoka 120a6e
  if (edges.empty()) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Selected edges must lie on the same mesh
Shinya Kitaoka 120a6e
  meshIdx = locals::testSingleMesh(edges);
Shinya Kitaoka 120a6e
  if (meshIdx < 0) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TTextureMesh &mesh = *mi.meshes()[meshIdx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // No selected edge must be on the boundary
Shinya Kitaoka 120a6e
  return (locals::testNoBoundaryEdge(mesh, edges) &&
Shinya Kitaoka 120a6e
          locals::buildEdgeCuts(mesh, edges, edgeCuts));
Shinya Kitaoka 120a6e
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
//------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
inline bool testCutMesh(const TMeshImage &mi,
Shinya Kitaoka 120a6e
                        const PlasticTool::MeshSelection &edgesSelection) {
Shinya Kitaoka 120a6e
  std::vector<edgecut> edgeCuts;</edgecut>
Shinya Kitaoka 120a6e
  int meshIdx;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return buildEdgeCuts(mi, edgesSelection, meshIdx, edgeCuts);
Shinya Kitaoka 120a6e
}
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
//------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
void slitMesh(TTextureMesh &mesh,
Shinya Kitaoka 120a6e
              int e)  //! Opens a slit along the specified edge index.
Shinya Kitaoka 120a6e
{
Shinya Kitaoka 120a6e
  TTextureMesh::edge_type &ed = mesh.edge(e);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  assert(ed.facesCount() == 2);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Duplicate the edge and pass one face to the duplicate
Shinya Kitaoka 120a6e
  TTextureMesh::edge_type edDup(ed.vertex(0), ed.vertex(1));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int f = ed.face(1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  edDup.addFace(f);
Shinya Kitaoka 120a6e
  ed.eraseFace(ed.facesBegin() + 1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int eDup = mesh.addEdge(edDup);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Alter the face to host the duplicate
Shinya Kitaoka 120a6e
  TTextureMesh::face_type &fc = mesh.face(f);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  (fc.edge(0) == e)
Shinya Kitaoka 120a6e
      ? fc.setEdge(0, eDup)
Shinya Kitaoka 120a6e
      : (fc.edge(1) == e) ? fc.setEdge(1, eDup) : fc.setEdge(2, eDup);
Shinya Kitaoka 120a6e
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
//------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
/*!
Shinya Kitaoka 120a6e
  \brief    Duplicates a mesh edge-vertex pair (the 'cut') and separates their
Shinya Kitaoka 120a6e
            connections to adjacent mesh primitives.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  \remark   The starting vertex is supposed to be on the mesh boundary.
Shinya Kitaoka 120a6e
  \remark   Edges with a single neighbouring face can be duplicated, too.
Shinya Kitaoka 120a6e
*/
Shinya Kitaoka 120a6e
void cutEdge(TTextureMesh &mesh, const EdgeCut &edgeCut) {
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static void transferEdge(TTextureMesh &mesh, int e, int vFrom, int vTo) {
Shinya Kitaoka 120a6e
      edge_type &ed = mesh.edge(e);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      vertex_type &vxFrom = mesh.vertex(vFrom), &vxTo = mesh.vertex(vTo);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      (ed.vertex(0) == vFrom) ? ed.setVertex(0, vTo) : ed.setVertex(1, vTo);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      vxTo.addEdge(e);
Shinya Kitaoka 120a6e
      vxFrom.eraseEdge(
Shinya Kitaoka 120a6e
          std::find(vxFrom.edges().begin(), vxFrom.edges().end(), e));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    static void transferFace(TTextureMesh &mesh, int eFrom, int eTo) {
Shinya Kitaoka 120a6e
      edge_type &edFrom = mesh.edge(eFrom), &edTo = mesh.edge(eTo);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      int f = mesh.edge(eFrom).face(1);
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        face_type &fc = mesh.face(f);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        (fc.edge(0) == eFrom)
Shinya Kitaoka 120a6e
            ? fc.setEdge(0, eTo)
Shinya Kitaoka 120a6e
            : (fc.edge(1) == eFrom) ? fc.setEdge(1, eTo) : fc.setEdge(2, eTo);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        edTo.addFace(f);
Shinya Kitaoka 120a6e
        edFrom.eraseFace(edFrom.facesBegin() + 1);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  };  // locals
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int vOrig = edgeCut.m_vIdx, eOrig = edgeCut.m_eIdx;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Create a new vertex at the same position of the original
Shinya Kitaoka 120a6e
  int vDup = mesh.addVertex(vertex_type(mesh.vertex(vOrig).P()));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int e = eOrig;
Shinya Kitaoka 120a6e
  if (mesh.edge(e).facesCount() == 2) {
Shinya Kitaoka 120a6e
    // Duplicate the cut edge
Shinya Kitaoka 120a6e
    e = mesh.addEdge(edge_type(vDup, mesh.edge(eOrig).otherVertex(vOrig)));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Transfer one face from the original to the duplicate
Shinya Kitaoka 120a6e
    locals::transferFace(mesh, eOrig, e);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    // Transfer the original edge to the duplicate vertex
Shinya Kitaoka 120a6e
    locals::transferEdge(mesh, eOrig, vOrig, vDup);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Edges adjacent to the original vertex that are also adjacent
Shinya Kitaoka 120a6e
  // to the transferred face above must be transferred too
Shinya Kitaoka 120a6e
  int f = mesh.edge(e).face(0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  while (f >= 0) {
Shinya Kitaoka 120a6e
    // Retrieve the next edge to transfer
Shinya Kitaoka 120a6e
    int otherE = mesh.otherFaceEdge(f, mesh.edge(e).otherVertex(vDup));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // NOTE: Not "mesh.edgeInciding(vOrig, mesh.otherFaceVertex(f, e))" in the
Shinya Kitaoka 120a6e
    // calculation
Shinya Kitaoka 120a6e
    //       of otherE. This is required since by transferring each edge at a
Shinya Kitaoka 120a6e
    //       time,
Shinya Kitaoka 120a6e
    //       we're 'breaking' faces up - f is adjacent to both vOrig AND vDup!
Shinya Kitaoka 120a6e
    //
Shinya Kitaoka 120a6e
    //       The chosen calculation, instead, just asks for the one edge which
Shinya Kitaoka 120a6e
    //       does
Shinya Kitaoka 120a6e
    //       not have a specific vertex in common to the 2 other edges in the
Shinya Kitaoka 120a6e
    //       face.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    locals::transferEdge(mesh, otherE, vOrig, vDup);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Update e and f
Shinya Kitaoka 120a6e
    e = otherE;
Shinya Kitaoka 120a6e
    f = mesh.edge(otherE).otherFace(f);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
}
Shinya Kitaoka 120a6e
namespace locals_ {      // Need to use a named namespace due to
Shinya Kitaoka 120a6e
                         // a known gcc 4.2 bug with compiler-generated
Shinya Kitaoka 120a6e
struct VertexesRecorder  // copy constructors.
Shinya Kitaoka 120a6e
{
Shinya Kitaoka 120a6e
  boost::unordered_set<int> &m_examinedVertexes;</int>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  typedef boost::on_examine_vertex event_filter;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  VertexesRecorder(boost::unordered_set<int> &examinedVertexes)</int>
Shinya Kitaoka 120a6e
      : m_examinedVertexes(examinedVertexes) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void operator()(int v, const TTextureMesh &) { m_examinedVertexes.insert(v); }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
}
Shinya Kitaoka 120a6e
namespace {  //
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void splitUnconnectedMesh(TMeshImage &mi, int meshIdx) {
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static void buildConnectedComponent(const TTextureMesh &mesh,
Shinya Kitaoka 120a6e
                                        boost::unordered_set<int> &vertexes) {</int>
Shinya Kitaoka 120a6e
      // Prepare BFS algorithm
Shinya Kitaoka 120a6e
      tcg::unique_ptr<uchar, tcg::freer=""> colorMapP(</uchar,>
Shinya Kitaoka 120a6e
          (UCHAR *)calloc(mesh.vertices().nodesCount(), sizeof(UCHAR)));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      locals_::VertexesRecorder vertexesRecorder(vertexes);
Shinya Kitaoka 120a6e
      std::stack<int> verticesQueue;</int>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Launch it
Shinya Kitaoka 120a6e
      boost::breadth_first_visit(
Shinya Kitaoka 120a6e
          mesh, int(mesh.vertices().begin().index()), verticesQueue,
Shinya Kitaoka 120a6e
          boost::make_bfs_visitor(vertexesRecorder), colorMapP.get());
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  };  // locals
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Retrieve the list of vertexes in the first connected component
Shinya Kitaoka 120a6e
  TTextureMesh &origMesh = *mi.meshes()[meshIdx];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  boost::unordered_set<int> firstComponent;</int>
Shinya Kitaoka 120a6e
  locals::buildConnectedComponent(origMesh, firstComponent);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (firstComponent.size() == origMesh.verticesCount()) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // There are (exactly) 2 connected components. Just duplicate the mesh
Shinya Kitaoka 120a6e
  // and keep/delete found vertexes.
Shinya Kitaoka 120a6e
  TTextureMeshP dupMeshPtr(new TTextureMesh(origMesh));
Shinya Kitaoka 120a6e
  TTextureMesh &dupMesh = *dupMeshPtr;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TTextureMesh::vertices_container &vertices = origMesh.vertices();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TTextureMesh::vertices_container::iterator vt, vEnd = vertices.end();
Shinya Kitaoka 120a6e
  for (vt = vertices.begin(); vt != vEnd;) {
Shinya Kitaoka 120a6e
    int v = int(vt.index());
Shinya Kitaoka 120a6e
    ++vt;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (firstComponent.count(v))
Shinya Kitaoka 120a6e
      dupMesh.removeVertex(v);
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      origMesh.removeVertex(v);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  dupMesh.squeeze();
Shinya Kitaoka 120a6e
  origMesh.squeeze();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  mi.meshes().push_back(dupMeshPtr);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void splitMesh(TMeshImage &mi, int meshIdx, int lastBoundaryVertex) {
Shinya Kitaoka 120a6e
  // Retrieve a cutting edge with a single adjacent face - cutting that
Shinya Kitaoka 120a6e
  // will just duplicate the vertex and separate the mesh in 2 connected
Shinya Kitaoka 120a6e
  // components
Shinya Kitaoka 120a6e
  TTextureMesh &mesh = *mi.meshes()[meshIdx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int e;
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    const vertex_type &lbVx = mesh.vertex(lastBoundaryVertex);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    vertex_type::edges_const_iterator et = std::find_if(
Shinya Kitaoka 120a6e
        lbVx.edgesBegin(), lbVx.edgesEnd(), tcg::bind1st(&borderEdge, mesh));
Shinya Kitaoka 120a6e
    assert(et != lbVx.edgesEnd());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    e = *et;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cutEdge(mesh, EdgeCut(lastBoundaryVertex, e));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // At this point, separate the 2 resulting connected components
Shinya Kitaoka 120a6e
  // in 2 separate meshes (if necessary)
Shinya Kitaoka 120a6e
  splitUnconnectedMesh(mi, meshIdx);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool cutMesh(TMeshImage &mi, const PlasticTool::MeshSelection &edgesSelection) {
Shinya Kitaoka 120a6e
  struct locals {
Shinya Kitaoka 120a6e
    static int lastVertex(const TTextureMesh &mesh,
Shinya Kitaoka 120a6e
                          const std::vector<edgecut> &edgeCuts) {</edgecut>
Shinya Kitaoka 120a6e
      return mesh.edge(edgeCuts.back().m_eIdx)
Shinya Kitaoka 120a6e
          .otherVertex(edgeCuts.back().m_vIdx);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    static int lastBoundaryVertex(const TTextureMesh &mesh,
Shinya Kitaoka 120a6e
                                  const std::vector<edgecut> &edgeCuts) {</edgecut>
Shinya Kitaoka 120a6e
      int v = lastVertex(mesh, edgeCuts);
Shinya Kitaoka 120a6e
      return ::borderVertex(mesh, v) ? v : -1;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  };  // locals
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<edgecut> edgeCuts;</edgecut>
Shinya Kitaoka 120a6e
  int meshIdx;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!::buildEdgeCuts(mi, edgesSelection, meshIdx, edgeCuts)) return false;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TTextureMesh &mesh = *mi.meshes()[meshIdx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int lastBoundaryVertex = locals::lastBoundaryVertex(mesh, edgeCuts);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Slit the mesh on the first edge, in case the cuts do not start
Shinya Kitaoka 120a6e
  // on the mesh boundary
Shinya Kitaoka 120a6e
  std::vector<edgecut>::iterator ecBegin = edgeCuts.begin();</edgecut>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!::borderVertex(mesh, ecBegin->m_vIdx)) {
Shinya Kitaoka 120a6e
    ::slitMesh(mesh, ecBegin->m_eIdx);
Shinya Kitaoka 120a6e
    ++ecBegin;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Cut edges, in the order specified by edgeCuts
Shinya Kitaoka 120a6e
  std::for_each(ecBegin, edgeCuts.end(), tcg::bind1st(&cutEdge, mesh));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Finally, the mesh could have been split in 2 - we need to separate
Shinya Kitaoka 120a6e
  // the pieces if needed
Shinya Kitaoka 120a6e
  if (lastBoundaryVertex >= 0) splitMesh(mi, meshIdx, lastBoundaryVertex);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    Undo  definitions
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class MoveVertexUndo_Mesh final : public TUndo {
Shinya Kitaoka 120a6e
  int m_row, m_col;  //!< Xsheet coordinates
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<meshindex> m_vIdxs;     //!< Moved vertices</meshindex>
Shinya Kitaoka 120a6e
  std::vector<tpointd> m_origVxsPos;  //!< Original vertex positions</tpointd>
Shinya Kitaoka 120a6e
  TPointD m_posShift;                 //!< Vertex positions shift
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  MoveVertexUndo_Mesh(const std::vector<meshindex> &vIdxs,</meshindex>
Shinya Kitaoka 120a6e
                      const std::vector<tpointd> &origVxsPos,</tpointd>
Shinya Kitaoka 120a6e
                      const TPointD &posShift)
Shinya Kitaoka 120a6e
      : m_row(::row())
Shinya Kitaoka 120a6e
      , m_col(::column())
Shinya Kitaoka 120a6e
      , m_vIdxs(vIdxs)
Shinya Kitaoka 120a6e
      , m_origVxsPos(origVxsPos)
Shinya Kitaoka 120a6e
      , m_posShift(posShift) {
Shinya Kitaoka 120a6e
    assert(m_vIdxs.size() == m_origVxsPos.size());
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  int getSize() const override {
Shinya Kitaoka 120a6e
    return int(sizeof(*this) +
Shinya Kitaoka 120a6e
               m_vIdxs.size() * (sizeof(int) + 2 * sizeof(TPointD)));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void redo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.setMeshVertexesSelection(m_vIdxs);
Shinya Kitaoka 120a6e
    l_plasticTool.moveVertex_mesh(m_origVxsPos, m_posShift);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool
Shinya Kitaoka 120a6e
        .notifyImageChanged();  // IMPORTANT: In particular, sets the level's
Shinya Kitaoka 120a6e
  }                             //            dirty flag, so Toonz knows it has
Shinya Kitaoka 120a6e
                                //            to be saved!
Shinya Kitaoka 473e70
  void undo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.setMeshVertexesSelection(m_vIdxs);
Shinya Kitaoka 120a6e
    l_plasticTool.moveVertex_mesh(m_origVxsPos, TPointD());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class SwapEdgeUndo final : public TUndo {
Shinya Kitaoka 120a6e
  int m_row, m_col;             //!< Xsheet coordinates
Shinya Kitaoka 120a6e
  mutable MeshIndex m_edgeIdx;  //!< Edge index
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  SwapEdgeUndo(const MeshIndex &edgeIdx)
Shinya Kitaoka 120a6e
      : m_row(::row()), m_col(::column()), m_edgeIdx(edgeIdx) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  int getSize() const override { return sizeof(*this); }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void redo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TMeshImageP &mi = TMeshImageP(TTool::getImage(true));
Shinya Kitaoka 120a6e
    assert(mi);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Perform swap
Shinya Kitaoka 120a6e
    TTextureMesh &mesh = *mi->meshes()[m_edgeIdx.m_meshIdx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_edgeIdx.m_idx = mesh.swapEdge(m_edgeIdx.m_idx);
Shinya Kitaoka 120a6e
    assert(m_edgeIdx.m_idx >= 0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Invalidate any deformer associated with mi
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Update tool selection
Shinya Kitaoka 120a6e
    l_plasticTool.setMeshEdgesSelection(m_edgeIdx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void undo() const override { redo(); }  // Operation is idempotent (indices
Shinya Kitaoka 38fd86
                                          // are perfectly restored, too)
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class TTextureMeshUndo : public TUndo {
Toshihiro Shimizu 890ddd
protected:
Shinya Kitaoka 120a6e
  int m_row, m_col;  //!< Xsheet coordinates
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int m_meshIdx;  //!< Mesh index in the image at stored xsheet coords
Shinya Kitaoka 120a6e
  mutable TTextureMesh m_origMesh;  //!< Copy of the original mesh
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  TTextureMeshUndo(int meshIdx)
Shinya Kitaoka 120a6e
      : m_row(::row()), m_col(::column()), m_meshIdx(meshIdx) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Let's say 1MB each - storing the mesh is costly
Shinya Kitaoka 473e70
  int getSize() const override { return 1 << 20; }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TMeshImageP getMeshImage() const {
Shinya Kitaoka 120a6e
    const TMeshImageP &mi = TMeshImageP(TTool::getImage(true));
Shinya Kitaoka 120a6e
    assert(mi);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    return mi;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class CollapseEdgeUndo final : public TTextureMeshUndo {
Shinya Kitaoka 120a6e
  int m_eIdx;  //!< Collapsed edge index
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  CollapseEdgeUndo(const MeshIndex &edgeIdx)
Shinya Kitaoka 120a6e
      : TTextureMeshUndo(edgeIdx.m_meshIdx), m_eIdx(edgeIdx.m_idx) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void redo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TMeshImageP &mi = getMeshImage();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Store the original mesh
Shinya Kitaoka 120a6e
    TTextureMesh &mesh = *mi->meshes()[m_meshIdx];
Shinya Kitaoka 120a6e
    m_origMesh         = mesh;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Collapse
Shinya Kitaoka 120a6e
    mesh.collapseEdge(m_eIdx);
Shinya Kitaoka 120a6e
    mesh.squeeze();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Invalidate any cached deformer associated with the modified mesh image
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Refresh the tool
Shinya Kitaoka 120a6e
    l_plasticTool.clearMeshSelections();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void undo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TMeshImageP &mi = getMeshImage();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore the original mesh
Shinya Kitaoka 120a6e
    TTextureMesh &mesh = *mi->meshes()[m_meshIdx];
Shinya Kitaoka 120a6e
    mesh               = m_origMesh;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore selection
Shinya Kitaoka 120a6e
    l_plasticTool.setMeshEdgesSelection(MeshIndex(m_meshIdx, m_eIdx));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class SplitEdgeUndo final : public TTextureMeshUndo {
Shinya Kitaoka 120a6e
  int m_eIdx;  //!< Split edge index
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  SplitEdgeUndo(const MeshIndex &edgeIdx)
Shinya Kitaoka 120a6e
      : TTextureMeshUndo(edgeIdx.m_meshIdx), m_eIdx(edgeIdx.m_idx) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void redo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TMeshImageP &mi = getMeshImage();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Store the original mesh
Shinya Kitaoka 120a6e
    TTextureMesh &mesh = *mi->meshes()[m_meshIdx];
Shinya Kitaoka 120a6e
    m_origMesh         = mesh;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Split
Shinya Kitaoka 120a6e
    mesh.splitEdge(m_eIdx);
Shinya Kitaoka 120a6e
    // mesh.squeeze();                                                     //
Shinya Kitaoka 120a6e
    // There should be no need to squeeze
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    assert(mesh.vertices().size() == mesh.vertices().nodesCount());  //
Shinya Kitaoka 120a6e
    assert(mesh.edges().size() == mesh.edges().nodesCount());        //
Shinya Kitaoka 120a6e
    assert(mesh.faces().size() == mesh.faces().nodesCount());        //
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.clearMeshSelections();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void undo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    const TMeshImageP &mi = getMeshImage();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore the original mesh
Shinya Kitaoka 120a6e
    TTextureMesh &mesh = *mi->meshes()[m_meshIdx];
Shinya Kitaoka 120a6e
    mesh               = m_origMesh;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore selection
Shinya Kitaoka 120a6e
    l_plasticTool.setMeshEdgesSelection(MeshIndex(m_meshIdx, m_eIdx));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==============================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class CutEdgesUndo final : public TUndo {
Shinya Kitaoka 120a6e
  int m_row, m_col;         //!< Xsheet coordinates
Shinya Kitaoka 120a6e
  TMeshImageP m_origImage;  //!< Clone of the original image
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  PlasticTool::MeshSelection m_edgesSelection;  //!< Selection to operate on
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  CutEdgesUndo(const PlasticTool::MeshSelection &edgesSelection)
Shinya Kitaoka 120a6e
      : m_row(::row())
Shinya Kitaoka 120a6e
      , m_col(::column())
Shinya Kitaoka 120a6e
      , m_origImage(TTool::getImage(false)->cloneImage())
Shinya Kitaoka 120a6e
      , m_edgesSelection(edgesSelection) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  int getSize() const override { return 1 << 20; }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  bool do_() const {
Shinya Kitaoka 120a6e
    TMeshImageP mi = TTool::getImage(true);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (::cutMesh(*mi, m_edgesSelection)) {
Shinya Kitaoka 120a6e
      PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      l_plasticTool.clearMeshSelections();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
      l_plasticTool.notifyImageChanged();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      return true;
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    return false;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void redo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    bool ret = do_();
Shinya Kitaoka 120a6e
    (void)ret;
Shinya Kitaoka 120a6e
    assert(ret);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void undo() const override {
Shinya Kitaoka 120a6e
    PlasticTool::TemporaryActivation tempActivate(m_row, m_col);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TMeshImageP mi = TTool::getImage(true);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore the original image
Shinya Kitaoka 120a6e
    *mi = *m_origImage;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    PlasticDeformerStorage::instance()->releaseMeshData(mi.getPointer());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Restore selection
Shinya Kitaoka 120a6e
    l_plasticTool.setMeshEdgesSelection(m_edgesSelection);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    l_plasticTool.invalidate();
Shinya Kitaoka 120a6e
    l_plasticTool.notifyImageChanged();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
//    PlasticTool  functions
Toshihiro Shimizu 890ddd
//****************************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::storeMeshImage() {
Shinya Kitaoka 120a6e
  TMeshImageP mi = getImage(false);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (mi != m_mi) {
Shinya Kitaoka 120a6e
    m_mi = mi;
Shinya Kitaoka 120a6e
    clearMeshSelections();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::setMeshSelection(MeshSelection &target,
Shinya Kitaoka 120a6e
                                   const MeshSelection &newSel) {
Shinya Kitaoka 120a6e
  if (newSel.isEmpty()) {
Shinya Kitaoka 120a6e
    target.selectNone();
Shinya Kitaoka 120a6e
    target.makeNotCurrent();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  target.setObjects(newSel.objects());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  target.notifyView();
Shinya Kitaoka 120a6e
  target.makeCurrent();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::toggleMeshSelection(MeshSelection &target,
Shinya Kitaoka 120a6e
                                      const MeshSelection &addition) {
Shinya Kitaoka 120a6e
  typedef MeshSelection::objects_container objects_container;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const objects_container &storedIdxs = target.objects();
Shinya Kitaoka 120a6e
  const objects_container &addedIdxs  = addition.objects();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build new selection
Shinya Kitaoka 120a6e
  objects_container selectedIdxs;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (target.contains(addition)) {
Shinya Kitaoka 120a6e
    std::set_difference(storedIdxs.begin(), storedIdxs.end(), addedIdxs.begin(),
Shinya Kitaoka 120a6e
                        addedIdxs.end(), std::back_inserter(selectedIdxs));
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    std::set_union(storedIdxs.begin(), storedIdxs.end(), addedIdxs.begin(),
Shinya Kitaoka 120a6e
                   addedIdxs.end(), std::back_inserter(selectedIdxs));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  setMeshSelection(target, selectedIdxs);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::clearMeshSelections() {
Shinya Kitaoka 120a6e
  m_mvHigh = m_meHigh = MeshIndex();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_mvSel.selectNone();
Shinya Kitaoka 120a6e
  m_mvSel.makeNotCurrent();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_meSel.selectNone();
Shinya Kitaoka 120a6e
  m_meSel.makeNotCurrent();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::setMeshVertexesSelection(const MeshSelection &vSel) {
Shinya Kitaoka 120a6e
  setMeshSelection(m_meSel, MeshSelection()), setMeshSelection(m_mvSel, vSel);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::toggleMeshVertexesSelection(const MeshSelection &vSel) {
Shinya Kitaoka 120a6e
  setMeshSelection(m_meSel, MeshSelection()),
Shinya Kitaoka 120a6e
      toggleMeshSelection(m_mvSel, vSel);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::setMeshEdgesSelection(const MeshSelection &eSel) {
Shinya Kitaoka 120a6e
  setMeshSelection(m_meSel, eSel), setMeshSelection(m_mvSel, MeshSelection());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::toggleMeshEdgesSelection(const MeshSelection &eSel) {
Shinya Kitaoka 120a6e
  toggleMeshSelection(m_meSel, eSel),
Shinya Kitaoka 120a6e
      setMeshSelection(m_mvSel, MeshSelection());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::mouseMove_mesh(const TPointD &pos, const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  m_pos = pos;  // Needs to be done now - ensures m_pos is valid
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_mvHigh = MeshIndex();  // Reset highlighted primitives
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (m_mi) {
Shinya Kitaoka 120a6e
    // Look for nearest primitive
Shinya Kitaoka 120a6e
    std::pair<double, meshindex=""> closestVertex = ::closestVertex(*m_mi, pos),</double,>
Shinya Kitaoka 120a6e
                                 closestEdge = ::closestEdge(*m_mi, pos);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Discriminate on fixed metric
Shinya Kitaoka 120a6e
    const double hDistSq = sq(getPixelSize() * MESH_HIGHLIGHT_DISTANCE);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    m_mvHigh = m_meHigh = MeshIndex();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (closestEdge.first < hDistSq) m_meHigh = closestEdge.second;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (closestVertex.first < hDistSq)
Shinya Kitaoka 120a6e
      m_mvHigh = closestVertex.second, m_meHigh = MeshIndex();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(!(m_mvHigh &&
Shinya Kitaoka 120a6e
           m_meHigh));  // Vertex and edge highlights are mutually exclusive
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::leftButtonDown_mesh(const TPointD &pos,
Shinya Kitaoka 120a6e
                                      const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  struct Locals {
Shinya Kitaoka 120a6e
    PlasticTool *m_this;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    void updateSelection(MeshSelection &sel, const MeshIndex &idx,
Shinya Kitaoka 120a6e
                         const TMouseEvent &me) {
Shinya Kitaoka 120a6e
      if (idx) {
Shinya Kitaoka 120a6e
        MeshSelection newSel(idx);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        if (me.isCtrlPressed())
Shinya Kitaoka 120a6e
          m_this->toggleMeshSelection(sel, newSel);
Shinya Kitaoka 120a6e
        else if (!sel.contains(newSel))
Shinya Kitaoka 120a6e
          m_this->setMeshSelection(sel, newSel);
Shinya Kitaoka 120a6e
      } else
Shinya Kitaoka 120a6e
        m_this->setMeshSelection(sel, MeshSelection());
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    static TPointD vertexPos(const TMeshImage &mi, const MeshIndex &meshIdx) {
Shinya Kitaoka 120a6e
      return mi.meshes()[meshIdx.m_meshIdx]->vertex(meshIdx.m_idx).P();
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  } locals = {this};
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  m_pressedPos = m_pos = pos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Update selection
Shinya Kitaoka 120a6e
  locals.updateSelection(m_mvSel, m_mvHigh, me);
Shinya Kitaoka 120a6e
  locals.updateSelection(m_meSel, m_meHigh, me);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Store original vertex positions
Shinya Kitaoka 120a6e
  if (!m_mvSel.isEmpty()) {
Shinya Kitaoka 120a6e
    m_pressedVxsPos = std::vector<tpointd>(</tpointd>
Shinya Kitaoka 120a6e
        tcg::make_cast_it(m_mvSel.objects().begin(),
Shinya Kitaoka 120a6e
                          tcg::bind1st(&Locals::vertexPos, *m_mi)),
Shinya Kitaoka 120a6e
        tcg::make_cast_it(m_mvSel.objects().end(),
Shinya Kitaoka 120a6e
                          tcg::bind1st(&Locals::vertexPos, *m_mi)));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Redraw selections
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::leftButtonDrag_mesh(const TPointD &pos,
Shinya Kitaoka 120a6e
                                      const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  m_pos = pos;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (!m_mvSel.isEmpty()) {
Shinya Kitaoka 120a6e
    moveVertex_mesh(m_pressedVxsPos, pos - m_pressedPos);
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::leftButtonUp_mesh(const TPointD &pos, const TMouseEvent &me) {
Shinya Kitaoka 120a6e
  // Track mouse position
Shinya Kitaoka 120a6e
  m_pos = pos;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (m_dragged && !m_mvSel.isEmpty()) {
Shinya Kitaoka 120a6e
    TUndoManager::manager()->add(new MoveVertexUndo_Mesh(
Shinya Kitaoka 120a6e
        m_mvSel.objects(), m_pressedVxsPos, pos - m_pressedPos));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
    notifyImageChanged();  // Sets the level's dirty flag      -.-'
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::addContextMenuActions_mesh(QMenu *menu) {
Shinya Kitaoka 120a6e
  bool ret = true;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!m_meSel.isEmpty()) {
Shinya Kitaoka 120a6e
    if (m_meSel.hasSingleObject()) {
Shinya Kitaoka 120a6e
      const MeshIndex &mIdx    = m_meSel.objects().front();
Shinya Kitaoka 120a6e
      const TTextureMesh &mesh = *m_mi->meshes()[mIdx.m_meshIdx];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (::testSwapEdge(mesh, mIdx.m_idx)) {
Shinya Kitaoka 120a6e
        QAction *swapEdge = menu->addAction(tr("Swap Edge"));
Shinya Kitaoka 120a6e
        ret = ret && connect(swapEdge, SIGNAL(triggered()), &l_plasticTool,
Shinya Kitaoka 120a6e
                             SLOT(swapEdge_mesh_undo()));
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (::testCollapseEdge(mesh, mIdx.m_idx)) {
Shinya Kitaoka 120a6e
        QAction *collapseEdge = menu->addAction(tr("Collapse Edge"));
Shinya Kitaoka 120a6e
        ret = ret && connect(collapseEdge, SIGNAL(triggered()), &l_plasticTool,
Shinya Kitaoka 120a6e
                             SLOT(collapseEdge_mesh_undo()));
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      QAction *splitEdge = menu->addAction(tr("Split Edge"));
Shinya Kitaoka 120a6e
      ret = ret && connect(splitEdge, SIGNAL(triggered()), &l_plasticTool,
Shinya Kitaoka 120a6e
                           SLOT(splitEdge_mesh_undo()));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (::testCutMesh(*m_mi, m_meSel)) {
Shinya Kitaoka 120a6e
      QAction *cutEdges = menu->addAction(tr("Cut Mesh"));
Shinya Kitaoka 120a6e
      ret = ret && connect(cutEdges, SIGNAL(triggered()), &l_plasticTool,
Shinya Kitaoka 120a6e
                           SLOT(cutEdges_mesh_undo()));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    menu->addSeparator();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  assert(ret);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::moveVertex_mesh(const std::vector<tpointd> &origVxsPos,</tpointd>
Shinya Kitaoka 120a6e
                                  const TPointD &posShift) {
Shinya Kitaoka 120a6e
  if (m_mvSel.isEmpty() || !m_mi) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(origVxsPos.size() == m_mvSel.objects().size());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Move selected vertices
Shinya Kitaoka 120a6e
  TMeshImageP mi = getImage(true);
Shinya Kitaoka 120a6e
  assert(m_mi == mi);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int v, vCount = int(m_mvSel.objects().size());
Shinya Kitaoka 120a6e
  for (v = 0; v != vCount; ++v) {
Shinya Kitaoka 120a6e
    const MeshIndex &meshIndex = m_mvSel.objects()[v];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    TTextureMesh &mesh               = *mi->meshes()[meshIndex.m_meshIdx];
Shinya Kitaoka 120a6e
    mesh.vertex(meshIndex.m_idx).P() = origVxsPos[v] + posShift;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Mesh must be recompiled
Shinya Kitaoka 120a6e
  PlasticDeformerStorage::instance()->invalidateMeshImage(
Shinya Kitaoka 120a6e
      mi.getPointer(), PlasticDeformerStorage::MESH);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::swapEdge_mesh_undo() {
Shinya Kitaoka 120a6e
  if (!(m_mi && m_meSel.hasSingleObject())) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Test current edge swapability
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    const MeshIndex &eIdx    = m_meSel.objects().front();
Shinya Kitaoka 120a6e
    const TTextureMesh &mesh = *m_mi->meshes()[eIdx.m_meshIdx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (!::testSwapEdge(mesh, eIdx.m_idx)) return;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Perform operation
Shinya Kitaoka 2a7129
  std::unique_ptr<tundo> undo(new SwapEdgeUndo(m_meSel.objects().front()));</tundo>
Shinya Kitaoka 120a6e
  undo->redo();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TUndoManager::manager()->add(undo.release());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::collapseEdge_mesh_undo() {
Shinya Kitaoka 120a6e
  if (!(m_mi && m_meSel.hasSingleObject())) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Test collapsibility of current edge
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    const MeshIndex &eIdx    = m_meSel.objects().front();
Shinya Kitaoka 120a6e
    const TTextureMesh &mesh = *m_mi->meshes()[eIdx.m_meshIdx];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (!::testCollapseEdge(mesh, eIdx.m_idx)) return;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Perform operation
Shinya Kitaoka 2a7129
  std::unique_ptr<tundo> undo(new CollapseEdgeUndo(m_meSel.objects().front()));</tundo>
Shinya Kitaoka 120a6e
  undo->redo();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TUndoManager::manager()->add(undo.release());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::splitEdge_mesh_undo() {
Shinya Kitaoka 120a6e
  if (!(m_mi && m_meSel.hasSingleObject())) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 2a7129
  std::unique_ptr<tundo> undo(new SplitEdgeUndo(m_meSel.objects().front()));</tundo>
Shinya Kitaoka 120a6e
  undo->redo();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TUndoManager::manager()->add(undo.release());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::cutEdges_mesh_undo() {
Shinya Kitaoka 120a6e
  if (!m_mi) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 2a7129
  std::unique_ptr<cutedgesundo> undo(new CutEdgesUndo(m_meSel.objects()));</cutedgesundo>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (undo->do_()) TUndoManager::manager()->add(undo.release());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void PlasticTool::draw_mesh() {
Shinya Kitaoka 120a6e
  struct Locals {
Shinya Kitaoka 120a6e
    PlasticTool *m_this;
Shinya Kitaoka 120a6e
    double m_pixelSize;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    void drawLine(const TPointD &a, const TPointD &b) {
Shinya Kitaoka 120a6e
      glVertex2d(a.x, a.y);
Shinya Kitaoka 120a6e
      glVertex2d(b.x, b.y);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    void drawVertexSelections() {
Shinya Kitaoka 120a6e
      typedef MeshSelection::objects_container objects_container;
Shinya Kitaoka 120a6e
      const objects_container &objects = m_this->m_mvSel.objects();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      glColor3ub(255, 0, 0);  // Red
Shinya Kitaoka 120a6e
      glLineWidth(1.0f);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      const double hSize = MESH_SELECTED_HANDLE_SIZE * m_pixelSize;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      objects_container::const_iterator vt, vEnd = objects.end();
Shinya Kitaoka 120a6e
      for (vt = objects.begin(); vt != vEnd; ++vt) {
Shinya Kitaoka 120a6e
        const TTextureVertex &vx =
Shinya Kitaoka 120a6e
            m_this->m_mi->meshes()[vt->m_meshIdx]->vertex(vt->m_idx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        ::drawFullSquare(vx.P(), hSize);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    void drawEdgeSelections() {
Shinya Kitaoka 120a6e
      typedef MeshSelection::objects_container objects_container;
Shinya Kitaoka 120a6e
      const objects_container &objects = m_this->m_meSel.objects();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      glColor3ub(0, 0, 255);  // Blue
Shinya Kitaoka 120a6e
      glLineWidth(2.0f);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      glBegin(GL_LINES);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      objects_container::const_iterator et, eEnd = objects.end();
Shinya Kitaoka 120a6e
      for (et = objects.begin(); et != eEnd; ++et) {
Shinya Kitaoka 120a6e
        const TTextureVertex
Shinya Kitaoka 120a6e
            &vx0 =
Shinya Kitaoka 120a6e
                m_this->m_mi->meshes()[et->m_meshIdx]->edgeVertex(et->m_idx, 0),
Shinya Kitaoka 120a6e
            &vx1 =
Shinya Kitaoka 120a6e
                m_this->m_mi->meshes()[et->m_meshIdx]->edgeVertex(et->m_idx, 1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        drawLine(vx0.P(), vx1.P());
Shinya Kitaoka 120a6e
      }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      glEnd();
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    void drawVertexHighlights() {
Shinya Kitaoka 120a6e
      if (m_this->m_mvHigh) {
Shinya Kitaoka 120a6e
        const MeshIndex &vHigh = m_this->m_mvHigh;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        const TTextureMesh::vertex_type &vx =
Shinya Kitaoka 120a6e
            m_this->m_mi->meshes()[vHigh.m_meshIdx]->vertex(vHigh.m_idx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        glColor3ub(255, 0, 0);  // Red
Shinya Kitaoka 120a6e
        glLineWidth(1.0f);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        const double hSize = MESH_HIGHLIGHTED_HANDLE_SIZE * m_pixelSize;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        ::drawSquare(vx.P(), hSize);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    void drawEdgeHighlights() {
Shinya Kitaoka 120a6e
      if (m_this->m_meHigh) {
Shinya Kitaoka 120a6e
        const MeshIndex &eHigh = m_this->m_meHigh;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        const TTextureMesh::vertex_type
Shinya Kitaoka 120a6e
            &vx0 = m_this->m_mi->meshes()[eHigh.m_meshIdx]->edgeVertex(
Shinya Kitaoka 120a6e
                eHigh.m_idx, 0),
Shinya Kitaoka 120a6e
            &vx1 = m_this->m_mi->meshes()[eHigh.m_meshIdx]->edgeVertex(
Shinya Kitaoka 120a6e
                eHigh.m_idx, 1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
        {
Shinya Kitaoka 120a6e
          glPushAttrib(GL_LINE_BIT);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          glEnable(GL_LINE_STIPPLE);
Shinya Kitaoka 120a6e
          glLineStipple(1, 0xCCCC);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          glColor3ub(0, 0, 255);  // Blue
Shinya Kitaoka 120a6e
          glLineWidth(1.0f);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          glBegin(GL_LINES);
Shinya Kitaoka 120a6e
          drawLine(vx0.P(), vx1.P());
Shinya Kitaoka 120a6e
          glEnd();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
          glPopAttrib();
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  } locals = {this, getPixelSize()};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // The selected mesh image is already drawn by the stage
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Draw additional overlays
Shinya Kitaoka 120a6e
  if (m_mi) {
Shinya Kitaoka 120a6e
    locals.drawVertexSelections();
Shinya Kitaoka 120a6e
    locals.drawEdgeSelections();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    locals.drawVertexHighlights();
Shinya Kitaoka 120a6e
    locals.drawEdgeHighlights();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}