Path: blob/master/thirdparty/manifold/src/face_op.cpp
9903 views
// Copyright 2021 The Manifold Authors.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#include <unordered_set>1516#include "impl.h"17#include "manifold/common.h"18#include "manifold/polygon.h"19#include "parallel.h"20#include "shared.h"2122#if (MANIFOLD_PAR == 1) && __has_include(<tbb/concurrent_map.h>)23#include <tbb/tbb.h>24#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 125#include <tbb/concurrent_map.h>26#endif2728namespace {29using namespace manifold;3031/**32* Returns an assembled set of vertex index loops of the input list of33* Halfedges, where each vert must be referenced the same number of times as a34* startVert and endVert. If startHalfedgeIdx is given, instead of putting35* vertex indices into the returned polygons structure, it will use the halfedge36* indices instead.37*/38std::vector<std::vector<int>> AssembleHalfedges(VecView<Halfedge>::IterC start,39VecView<Halfedge>::IterC end,40const int startHalfedgeIdx) {41std::multimap<int, int> vert_edge;42for (auto edge = start; edge != end; ++edge) {43vert_edge.emplace(44std::make_pair(edge->startVert, static_cast<int>(edge - start)));45}4647std::vector<std::vector<int>> polys;48int startEdge = 0;49int thisEdge = startEdge;50while (1) {51if (thisEdge == startEdge) {52if (vert_edge.empty()) break;53startEdge = vert_edge.begin()->second;54thisEdge = startEdge;55polys.push_back({});56}57polys.back().push_back(startHalfedgeIdx + thisEdge);58const auto result = vert_edge.find((start + thisEdge)->endVert);59DEBUG_ASSERT(result != vert_edge.end(), topologyErr, "non-manifold edge");60thisEdge = result->second;61vert_edge.erase(result);62}63return polys;64}6566/**67* Add the vertex position projection to the indexed polygons.68*/69PolygonsIdx ProjectPolygons(const std::vector<std::vector<int>>& polys,70const Vec<Halfedge>& halfedge,71const Vec<vec3>& vertPos, mat2x3 projection) {72PolygonsIdx polygons;73for (const auto& poly : polys) {74polygons.push_back({});75for (const auto& edge : poly) {76polygons.back().push_back(77{projection * vertPos[halfedge[edge].startVert], edge});78} // for vert79} // for poly80return polygons;81}82} // namespace8384namespace manifold {8586using GeneralTriangulation = std::function<std::vector<ivec3>(int)>;87using AddTriangle = std::function<void(int, ivec3, vec3, TriRef)>;8889/**90* Triangulates the faces. In this case, the halfedge_ vector is not yet a set91* of triangles as required by this data structure, but is instead a set of92* general faces with the input faceEdge vector having length of the number of93* faces + 1. The values are indicies into the halfedge_ vector for the first94* edge of each face, with the final value being the length of the halfedge_95* vector itself. Upon return, halfedge_ has been lengthened and properly96* represents the mesh as a set of triangles as usual. In this process the97* faceNormal_ values are retained, repeated as necessary.98*/99void Manifold::Impl::Face2Tri(const Vec<int>& faceEdge,100const Vec<TriRef>& halfedgeRef,101bool allowConvex) {102ZoneScoped;103Vec<ivec3> triVerts;104Vec<vec3> triNormal;105Vec<ivec3> triProp;106Vec<TriRef>& triRef = meshRelation_.triRef;107triRef.clear();108auto processFace = [&](GeneralTriangulation general, AddTriangle addTri,109int face) {110const int firstEdge = faceEdge[face];111const int lastEdge = faceEdge[face + 1];112const int numEdge = lastEdge - firstEdge;113if (numEdge == 0) return;114DEBUG_ASSERT(numEdge >= 3, topologyErr, "face has less than three edges.");115const vec3 normal = faceNormal_[face];116117if (numEdge == 3) { // Single triangle118ivec3 triEdge(firstEdge, firstEdge + 1, firstEdge + 2);119ivec3 tri(halfedge_[firstEdge].startVert,120halfedge_[firstEdge + 1].startVert,121halfedge_[firstEdge + 2].startVert);122ivec3 ends(halfedge_[firstEdge].endVert, halfedge_[firstEdge + 1].endVert,123halfedge_[firstEdge + 2].endVert);124if (ends[0] == tri[2]) {125std::swap(triEdge[1], triEdge[2]);126std::swap(tri[1], tri[2]);127std::swap(ends[1], ends[2]);128}129DEBUG_ASSERT(ends[0] == tri[1] && ends[1] == tri[2] && ends[2] == tri[0],130topologyErr, "These 3 edges do not form a triangle!");131132addTri(face, triEdge, normal, halfedgeRef[firstEdge]);133} else if (numEdge == 4) { // Pair of triangles134const mat2x3 projection = GetAxisAlignedProjection(normal);135auto triCCW = [&projection, this](const ivec3 tri) {136return CCW(projection * this->vertPos_[halfedge_[tri[0]].startVert],137projection * this->vertPos_[halfedge_[tri[1]].startVert],138projection * this->vertPos_[halfedge_[tri[2]].startVert],139epsilon_) >= 0;140};141142std::vector<int> quad = AssembleHalfedges(143halfedge_.cbegin() + faceEdge[face],144halfedge_.cbegin() + faceEdge[face + 1], faceEdge[face])[0];145146const la::mat<int, 3, 2> tris[2] = {147{{quad[0], quad[1], quad[2]}, {quad[0], quad[2], quad[3]}},148{{quad[1], quad[2], quad[3]}, {quad[0], quad[1], quad[3]}}};149150int choice = 0;151152if (!(triCCW(tris[0][0]) && triCCW(tris[0][1]))) {153choice = 1;154} else if (triCCW(tris[1][0]) && triCCW(tris[1][1])) {155vec3 diag0 = vertPos_[halfedge_[quad[0]].startVert] -156vertPos_[halfedge_[quad[2]].startVert];157vec3 diag1 = vertPos_[halfedge_[quad[1]].startVert] -158vertPos_[halfedge_[quad[3]].startVert];159if (la::length2(diag0) > la::length2(diag1)) {160choice = 1;161}162}163164for (const auto& tri : tris[choice]) {165addTri(face, tri, normal, halfedgeRef[firstEdge]);166}167} else { // General triangulation168for (const auto& tri : general(face)) {169addTri(face, tri, normal, halfedgeRef[firstEdge]);170}171}172};173auto generalTriangulation = [&](int face) {174const vec3 normal = faceNormal_[face];175const mat2x3 projection = GetAxisAlignedProjection(normal);176const PolygonsIdx polys = ProjectPolygons(177AssembleHalfedges(halfedge_.cbegin() + faceEdge[face],178halfedge_.cbegin() + faceEdge[face + 1],179faceEdge[face]),180halfedge_, vertPos_, projection);181return TriangulateIdx(polys, epsilon_, allowConvex);182};183#if (MANIFOLD_PAR == 1) && __has_include(<tbb/tbb.h>)184tbb::task_group group;185// map from face to triangle186tbb::concurrent_unordered_map<int, std::vector<ivec3>> results;187Vec<size_t> triCount(faceEdge.size());188triCount.back() = 0;189// precompute number of triangles per face, and launch async tasks to190// triangulate complex faces191for_each(autoPolicy(faceEdge.size(), 1e5), countAt(0_uz),192countAt(faceEdge.size() - 1), [&](size_t face) {193triCount[face] = faceEdge[face + 1] - faceEdge[face] - 2;194DEBUG_ASSERT(triCount[face] >= 1, topologyErr,195"face has less than three edges.");196if (triCount[face] > 2)197group.run([&, face] {198std::vector<ivec3> newTris = generalTriangulation(face);199triCount[face] = newTris.size();200results[face] = std::move(newTris);201});202});203group.wait();204// prefix sum computation (assign unique index to each face) and preallocation205exclusive_scan(triCount.begin(), triCount.end(), triCount.begin(), 0_uz);206triVerts.resize(triCount.back());207triProp.resize(triCount.back());208triNormal.resize(triCount.back());209triRef.resize(triCount.back());210211auto processFace2 = std::bind(212processFace, [&](size_t face) { return std::move(results[face]); },213[&](size_t face, ivec3 tri, vec3 normal, TriRef r) {214for (const int i : {0, 1, 2}) {215triVerts[triCount[face]][i] = halfedge_[tri[i]].startVert;216triProp[triCount[face]][i] = halfedge_[tri[i]].propVert;217}218triNormal[triCount[face]] = normal;219triRef[triCount[face]] = r;220triCount[face]++;221},222std::placeholders::_1);223// set triangles in parallel224for_each(autoPolicy(faceEdge.size(), 1e4), countAt(0_uz),225countAt(faceEdge.size() - 1), processFace2);226#else227triVerts.reserve(faceEdge.size());228triNormal.reserve(faceEdge.size());229triRef.reserve(faceEdge.size());230auto processFace2 = std::bind(231processFace, generalTriangulation,232[&](size_t, ivec3 tri, vec3 normal, TriRef r) {233ivec3 verts;234ivec3 props;235for (const int i : {0, 1, 2}) {236verts[i] = halfedge_[tri[i]].startVert;237props[i] = halfedge_[tri[i]].propVert;238}239triVerts.push_back(verts);240triProp.push_back(props);241triNormal.push_back(normal);242triRef.push_back(r);243},244std::placeholders::_1);245for (size_t face = 0; face < faceEdge.size() - 1; ++face) {246processFace2(face);247}248#endif249250faceNormal_ = std::move(triNormal);251CreateHalfedges(triProp, triVerts);252}253254Polygons Manifold::Impl::Slice(double height) const {255Box plane = bBox_;256plane.min.z = plane.max.z = height;257Vec<Box> query;258query.push_back(plane);259260std::unordered_set<int> tris;261auto recordCollision = [&](int, int tri) {262double min = std::numeric_limits<double>::infinity();263double max = -std::numeric_limits<double>::infinity();264for (const int j : {0, 1, 2}) {265const double z = vertPos_[halfedge_[3 * tri + j].startVert].z;266min = std::min(min, z);267max = std::max(max, z);268}269270if (min <= height && max > height) {271tris.insert(tri);272}273};274275auto recorder = MakeSimpleRecorder(recordCollision);276collider_.Collisions<false>(query.cview(), recorder, false);277278Polygons polys;279while (!tris.empty()) {280const int startTri = *tris.begin();281SimplePolygon poly;282283int k = 0;284for (const int j : {0, 1, 2}) {285if (vertPos_[halfedge_[3 * startTri + j].startVert].z > height &&286vertPos_[halfedge_[3 * startTri + Next3(j)].startVert].z <= height) {287k = Next3(j);288break;289}290}291292int tri = startTri;293do {294tris.erase(tris.find(tri));295if (vertPos_[halfedge_[3 * tri + k].endVert].z <= height) {296k = Next3(k);297}298299Halfedge up = halfedge_[3 * tri + k];300const vec3 below = vertPos_[up.startVert];301const vec3 above = vertPos_[up.endVert];302const double a = (height - below.z) / (above.z - below.z);303poly.push_back(vec2(la::lerp(below, above, a)));304305const int pair = up.pairedHalfedge;306tri = pair / 3;307k = Next3(pair % 3);308} while (tri != startTri);309310polys.push_back(poly);311}312313return polys;314}315316Polygons Manifold::Impl::Project() const {317const mat2x3 projection = GetAxisAlignedProjection({0, 0, 1});318Vec<Halfedge> cusps(NumEdge());319cusps.resize(320copy_if(321halfedge_.cbegin(), halfedge_.cend(), cusps.begin(),322[&](Halfedge edge) {323return faceNormal_[halfedge_[edge.pairedHalfedge].pairedHalfedge /3243]325.z >= 0 &&326faceNormal_[edge.pairedHalfedge / 3].z < 0;327}) -328cusps.begin());329330PolygonsIdx polysIndexed =331ProjectPolygons(AssembleHalfedges(cusps.cbegin(), cusps.cend(), 0), cusps,332vertPos_, projection);333334Polygons polys;335for (const auto& poly : polysIndexed) {336SimplePolygon simple;337for (const PolyVert& polyVert : poly) {338simple.push_back(polyVert.pos);339}340polys.push_back(simple);341}342343return polys;344}345} // namespace manifold346347348