Path: blob/master/thirdparty/manifold/src/boolean3.cpp
9902 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 "boolean3.h"1516#include <limits>17#include <unordered_set>1819#include "disjoint_sets.h"20#include "parallel.h"2122#if (MANIFOLD_PAR == 1)23#include <tbb/combinable.h>24#endif2526using namespace manifold;2728namespace {2930// These two functions (Interpolate and Intersect) are the only places where31// floating-point operations take place in the whole Boolean function. These32// are carefully designed to minimize rounding error and to eliminate it at edge33// cases to ensure consistency.3435vec2 Interpolate(vec3 pL, vec3 pR, double x) {36const double dxL = x - pL.x;37const double dxR = x - pR.x;38DEBUG_ASSERT(dxL * dxR <= 0, logicErr,39"Boolean manifold error: not in domain");40const bool useL = fabs(dxL) < fabs(dxR);41const vec3 dLR = pR - pL;42const double lambda = (useL ? dxL : dxR) / dLR.x;43if (!std::isfinite(lambda) || !std::isfinite(dLR.y) || !std::isfinite(dLR.z))44return vec2(pL.y, pL.z);45vec2 yz;46yz[0] = lambda * dLR.y + (useL ? pL.y : pR.y);47yz[1] = lambda * dLR.z + (useL ? pL.z : pR.z);48return yz;49}5051vec4 Intersect(const vec3& pL, const vec3& pR, const vec3& qL, const vec3& qR) {52const double dyL = qL.y - pL.y;53const double dyR = qR.y - pR.y;54DEBUG_ASSERT(dyL * dyR <= 0, logicErr,55"Boolean manifold error: no intersection");56const bool useL = fabs(dyL) < fabs(dyR);57const double dx = pR.x - pL.x;58double lambda = (useL ? dyL : dyR) / (dyL - dyR);59if (!std::isfinite(lambda)) lambda = 0.0;60vec4 xyzz;61xyzz.x = lambda * dx + (useL ? pL.x : pR.x);62const double pDy = pR.y - pL.y;63const double qDy = qR.y - qL.y;64const bool useP = fabs(pDy) < fabs(qDy);65xyzz.y = lambda * (useP ? pDy : qDy) +66(useL ? (useP ? pL.y : qL.y) : (useP ? pR.y : qR.y));67xyzz.z = lambda * (pR.z - pL.z) + (useL ? pL.z : pR.z);68xyzz.w = lambda * (qR.z - qL.z) + (useL ? qL.z : qR.z);69return xyzz;70}7172inline bool Shadows(double p, double q, double dir) {73return p == q ? dir < 0 : p < q;74}7576inline std::pair<int, vec2> Shadow01(77const int p0, const int q1, VecView<const vec3> vertPosP,78VecView<const vec3> vertPosQ, VecView<const Halfedge> halfedgeQ,79const double expandP, VecView<const vec3> normal, const bool reverse) {80const int q1s = halfedgeQ[q1].startVert;81const int q1e = halfedgeQ[q1].endVert;82const double p0x = vertPosP[p0].x;83const double q1sx = vertPosQ[q1s].x;84const double q1ex = vertPosQ[q1e].x;85int s01 = reverse ? Shadows(q1sx, p0x, expandP * normal[q1s].x) -86Shadows(q1ex, p0x, expandP * normal[q1e].x)87: Shadows(p0x, q1ex, expandP * normal[p0].x) -88Shadows(p0x, q1sx, expandP * normal[p0].x);89vec2 yz01(NAN);9091if (s01 != 0) {92yz01 = Interpolate(vertPosQ[q1s], vertPosQ[q1e], vertPosP[p0].x);93if (reverse) {94vec3 diff = vertPosQ[q1s] - vertPosP[p0];95const double start2 = la::dot(diff, diff);96diff = vertPosQ[q1e] - vertPosP[p0];97const double end2 = la::dot(diff, diff);98const double dir = start2 < end2 ? normal[q1s].y : normal[q1e].y;99if (!Shadows(yz01[0], vertPosP[p0].y, expandP * dir)) s01 = 0;100} else {101if (!Shadows(vertPosP[p0].y, yz01[0], expandP * normal[p0].y)) s01 = 0;102}103}104return std::make_pair(s01, yz01);105}106107struct Kernel11 {108VecView<const vec3> vertPosP;109VecView<const vec3> vertPosQ;110VecView<const Halfedge> halfedgeP;111VecView<const Halfedge> halfedgeQ;112const double expandP;113VecView<const vec3> normalP;114115std::pair<int, vec4> operator()(int p1, int q1) {116vec4 xyzz11 = vec4(NAN);117int s11 = 0;118119// For pRL[k], qRL[k], k==0 is the left and k==1 is the right.120int k = 0;121vec3 pRL[2], qRL[2];122// Either the left or right must shadow, but not both. This ensures the123// intersection is between the left and right.124bool shadows = false;125s11 = 0;126127const int p0[2] = {halfedgeP[p1].startVert, halfedgeP[p1].endVert};128for (int i : {0, 1}) {129const auto [s01, yz01] = Shadow01(p0[i], q1, vertPosP, vertPosQ,130halfedgeQ, expandP, normalP, false);131// If the value is NaN, then these do not overlap.132if (std::isfinite(yz01[0])) {133s11 += s01 * (i == 0 ? -1 : 1);134if (k < 2 && (k == 0 || (s01 != 0) != shadows)) {135shadows = s01 != 0;136pRL[k] = vertPosP[p0[i]];137qRL[k] = vec3(pRL[k].x, yz01.x, yz01.y);138++k;139}140}141}142143const int q0[2] = {halfedgeQ[q1].startVert, halfedgeQ[q1].endVert};144for (int i : {0, 1}) {145const auto [s10, yz10] = Shadow01(q0[i], p1, vertPosQ, vertPosP,146halfedgeP, expandP, normalP, true);147// If the value is NaN, then these do not overlap.148if (std::isfinite(yz10[0])) {149s11 += s10 * (i == 0 ? -1 : 1);150if (k < 2 && (k == 0 || (s10 != 0) != shadows)) {151shadows = s10 != 0;152qRL[k] = vertPosQ[q0[i]];153pRL[k] = vec3(qRL[k].x, yz10.x, yz10.y);154++k;155}156}157}158159if (s11 == 0) { // No intersection160xyzz11 = vec4(NAN);161} else {162DEBUG_ASSERT(k == 2, logicErr, "Boolean manifold error: s11");163xyzz11 = Intersect(pRL[0], pRL[1], qRL[0], qRL[1]);164165const int p1s = halfedgeP[p1].startVert;166const int p1e = halfedgeP[p1].endVert;167vec3 diff = vertPosP[p1s] - vec3(xyzz11);168const double start2 = la::dot(diff, diff);169diff = vertPosP[p1e] - vec3(xyzz11);170const double end2 = la::dot(diff, diff);171const double dir = start2 < end2 ? normalP[p1s].z : normalP[p1e].z;172173if (!Shadows(xyzz11.z, xyzz11.w, expandP * dir)) s11 = 0;174}175176return std::make_pair(s11, xyzz11);177}178};179180struct Kernel02 {181VecView<const vec3> vertPosP;182VecView<const Halfedge> halfedgeQ;183VecView<const vec3> vertPosQ;184const double expandP;185VecView<const vec3> vertNormalP;186const bool forward;187188std::pair<int, double> operator()(int p0, int q2) {189int s02 = 0;190double z02 = 0.0;191192// For yzzLR[k], k==0 is the left and k==1 is the right.193int k = 0;194vec3 yzzRL[2];195// Either the left or right must shadow, but not both. This ensures the196// intersection is between the left and right.197bool shadows = false;198int closestVert = -1;199double minMetric = std::numeric_limits<double>::infinity();200s02 = 0;201202const vec3 posP = vertPosP[p0];203for (const int i : {0, 1, 2}) {204const int q1 = 3 * q2 + i;205const Halfedge edge = halfedgeQ[q1];206const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge;207208if (!forward) {209const int qVert = halfedgeQ[q1F].startVert;210const vec3 diff = posP - vertPosQ[qVert];211const double metric = la::dot(diff, diff);212if (metric < minMetric) {213minMetric = metric;214closestVert = qVert;215}216}217218const auto syz01 = Shadow01(p0, q1F, vertPosP, vertPosQ, halfedgeQ,219expandP, vertNormalP, !forward);220const int s01 = syz01.first;221const vec2 yz01 = syz01.second;222// If the value is NaN, then these do not overlap.223if (std::isfinite(yz01[0])) {224s02 += s01 * (forward == edge.IsForward() ? -1 : 1);225if (k < 2 && (k == 0 || (s01 != 0) != shadows)) {226shadows = s01 != 0;227yzzRL[k++] = vec3(yz01[0], yz01[1], yz01[1]);228}229}230}231232if (s02 == 0) { // No intersection233z02 = NAN;234} else {235DEBUG_ASSERT(k == 2, logicErr, "Boolean manifold error: s02");236vec3 vertPos = vertPosP[p0];237z02 = Interpolate(yzzRL[0], yzzRL[1], vertPos.y)[1];238if (forward) {239if (!Shadows(vertPos.z, z02, expandP * vertNormalP[p0].z)) s02 = 0;240} else {241// DEBUG_ASSERT(closestVert != -1, topologyErr, "No closest vert");242if (!Shadows(z02, vertPos.z, expandP * vertNormalP[closestVert].z))243s02 = 0;244}245}246return std::make_pair(s02, z02);247}248};249250struct Kernel12 {251VecView<const Halfedge> halfedgesP;252VecView<const Halfedge> halfedgesQ;253VecView<const vec3> vertPosP;254const bool forward;255Kernel02 k02;256Kernel11 k11;257258std::pair<int, vec3> operator()(int p1, int q2) {259int x12 = 0;260vec3 v12 = vec3(NAN);261262// For xzyLR-[k], k==0 is the left and k==1 is the right.263int k = 0;264vec3 xzyLR0[2];265vec3 xzyLR1[2];266// Either the left or right must shadow, but not both. This ensures the267// intersection is between the left and right.268bool shadows = false;269x12 = 0;270271const Halfedge edge = halfedgesP[p1];272273for (int vert : {edge.startVert, edge.endVert}) {274const auto [s, z] = k02(vert, q2);275if (std::isfinite(z)) {276x12 += s * ((vert == edge.startVert) == forward ? 1 : -1);277if (k < 2 && (k == 0 || (s != 0) != shadows)) {278shadows = s != 0;279xzyLR0[k] = vertPosP[vert];280std::swap(xzyLR0[k].y, xzyLR0[k].z);281xzyLR1[k] = xzyLR0[k];282xzyLR1[k][1] = z;283k++;284}285}286}287288for (const int i : {0, 1, 2}) {289const int q1 = 3 * q2 + i;290const Halfedge edge = halfedgesQ[q1];291const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge;292const auto [s, xyzz] = forward ? k11(p1, q1F) : k11(q1F, p1);293if (std::isfinite(xyzz[0])) {294x12 -= s * (edge.IsForward() ? 1 : -1);295if (k < 2 && (k == 0 || (s != 0) != shadows)) {296shadows = s != 0;297xzyLR0[k][0] = xyzz.x;298xzyLR0[k][1] = xyzz.z;299xzyLR0[k][2] = xyzz.y;300xzyLR1[k] = xzyLR0[k];301xzyLR1[k][1] = xyzz.w;302if (!forward) std::swap(xzyLR0[k][1], xzyLR1[k][1]);303k++;304}305}306}307308if (x12 == 0) { // No intersection309v12 = vec3(NAN);310} else {311DEBUG_ASSERT(k == 2, logicErr, "Boolean manifold error: v12");312const vec4 xzyy = Intersect(xzyLR0[0], xzyLR0[1], xzyLR1[0], xzyLR1[1]);313v12.x = xzyy[0];314v12.y = xzyy[2];315v12.z = xzyy[1];316}317return std::make_pair(x12, v12);318}319};320321struct Kernel12Tmp {322Vec<std::array<int, 2>> p1q2_;323Vec<int> x12_;324Vec<vec3> v12_;325};326327struct Kernel12Recorder {328using Local = Kernel12Tmp;329Kernel12& k12;330bool forward;331332#if MANIFOLD_PAR == 1333tbb::combinable<Kernel12Tmp> store;334Local& local() { return store.local(); }335#else336Kernel12Tmp localStore;337Local& local() { return localStore; }338#endif339340void record(int queryIdx, int leafIdx, Local& tmp) {341const auto [x12, v12] = k12(queryIdx, leafIdx);342if (std::isfinite(v12[0])) {343if (forward)344tmp.p1q2_.push_back({queryIdx, leafIdx});345else346tmp.p1q2_.push_back({leafIdx, queryIdx});347tmp.x12_.push_back(x12);348tmp.v12_.push_back(v12);349}350}351352Kernel12Tmp get() {353#if MANIFOLD_PAR == 1354Kernel12Tmp result;355std::vector<Kernel12Tmp> tmps;356store.combine_each(357[&](Kernel12Tmp& data) { tmps.emplace_back(std::move(data)); });358std::vector<size_t> sizes;359size_t total_size = 0;360for (const auto& tmp : tmps) {361sizes.push_back(total_size);362total_size += tmp.x12_.size();363}364result.p1q2_.resize(total_size);365result.x12_.resize(total_size);366result.v12_.resize(total_size);367for_each_n(ExecutionPolicy::Seq, countAt(0), tmps.size(), [&](size_t i) {368std::copy(tmps[i].p1q2_.begin(), tmps[i].p1q2_.end(),369result.p1q2_.begin() + sizes[i]);370std::copy(tmps[i].x12_.begin(), tmps[i].x12_.end(),371result.x12_.begin() + sizes[i]);372std::copy(tmps[i].v12_.begin(), tmps[i].v12_.end(),373result.v12_.begin() + sizes[i]);374});375return result;376#else377return localStore;378#endif379}380};381382std::tuple<Vec<int>, Vec<vec3>> Intersect12(const Manifold::Impl& inP,383const Manifold::Impl& inQ,384Vec<std::array<int, 2>>& p1q2,385double expandP, bool forward) {386ZoneScoped;387// a: 1 (edge), b: 2 (face)388const Manifold::Impl& a = forward ? inP : inQ;389const Manifold::Impl& b = forward ? inQ : inP;390391Kernel02 k02{a.vertPos_, b.halfedge_, b.vertPos_,392expandP, inP.vertNormal_, forward};393Kernel11 k11{inP.vertPos_, inQ.vertPos_, inP.halfedge_,394inQ.halfedge_, expandP, inP.vertNormal_};395396Kernel12 k12{a.halfedge_, b.halfedge_, a.vertPos_, forward, k02, k11};397Kernel12Recorder recorder{k12, forward, {}};398auto f = [&a](int i) {399return a.halfedge_[i].IsForward()400? Box(a.vertPos_[a.halfedge_[i].startVert],401a.vertPos_[a.halfedge_[i].endVert])402: Box();403};404b.collider_.Collisions<false, decltype(f), Kernel12Recorder>(405f, a.halfedge_.size(), recorder);406407Kernel12Tmp result = recorder.get();408p1q2 = std::move(result.p1q2_);409auto x12 = std::move(result.x12_);410auto v12 = std::move(result.v12_);411// sort p1q2 according to edges412Vec<size_t> i12(p1q2.size());413sequence(i12.begin(), i12.end());414415int index = forward ? 0 : 1;416stable_sort(i12.begin(), i12.end(), [&](int a, int b) {417return p1q2[a][index] < p1q2[b][index] ||418(p1q2[a][index] == p1q2[b][index] &&419p1q2[a][1 - index] < p1q2[b][1 - index]);420});421Permute(p1q2, i12);422Permute(x12, i12);423Permute(v12, i12);424return std::make_tuple(x12, v12);425};426427Vec<int> Winding03(const Manifold::Impl& inP, const Manifold::Impl& inQ,428const VecView<std::array<int, 2>> p1q2, double expandP,429bool forward) {430ZoneScoped;431const Manifold::Impl& a = forward ? inP : inQ;432const Manifold::Impl& b = forward ? inQ : inP;433Vec<int> brokenHalfedges;434int index = forward ? 0 : 1;435436DisjointSets uA(a.vertPos_.size());437for_each(autoPolicy(a.halfedge_.size()), countAt(0),438countAt(a.halfedge_.size()), [&](int edge) {439const Halfedge& he = a.halfedge_[edge];440if (!he.IsForward()) return;441// check if the edge is broken442auto it = std::lower_bound(443p1q2.begin(), p1q2.end(), edge,444[index](const std::array<int, 2>& collisionPair, int e) {445return collisionPair[index] < e;446});447if (it == p1q2.end() || (*it)[index] != edge)448uA.unite(he.startVert, he.endVert);449});450451// find components, the hope is the number of components should be small452std::unordered_set<int> components;453#if (MANIFOLD_PAR == 1)454if (a.vertPos_.size() > 1e5) {455tbb::combinable<std::unordered_set<int>> componentsShared;456for_each(autoPolicy(a.vertPos_.size()), countAt(0),457countAt(a.vertPos_.size()),458[&](int v) { componentsShared.local().insert(uA.find(v)); });459componentsShared.combine_each([&](const std::unordered_set<int>& data) {460components.insert(data.begin(), data.end());461});462} else463#endif464{465for (size_t v = 0; v < a.vertPos_.size(); v++)466components.insert(uA.find(v));467}468Vec<int> verts;469verts.reserve(components.size());470for (int c : components) verts.push_back(c);471472Vec<int> w03(a.NumVert(), 0);473Kernel02 k02{a.vertPos_, b.halfedge_, b.vertPos_,474expandP, inP.vertNormal_, forward};475auto recorderf = [&](int i, int b) {476const auto [s02, z02] = k02(verts[i], b);477if (std::isfinite(z02)) w03[verts[i]] += s02 * (!forward ? -1 : 1);478};479auto recorder = MakeSimpleRecorder(recorderf);480auto f = [&](int i) { return a.vertPos_[verts[i]]; };481b.collider_.Collisions<false, decltype(f), decltype(recorder)>(482f, verts.size(), recorder);483// flood fill484for_each(autoPolicy(w03.size()), countAt(0), countAt(w03.size()),485[&](size_t i) {486size_t root = uA.find(i);487if (root == i) return;488w03[i] = w03[root];489});490return w03;491}492} // namespace493494namespace manifold {495Boolean3::Boolean3(const Manifold::Impl& inP, const Manifold::Impl& inQ,496OpType op)497: inP_(inP), inQ_(inQ), expandP_(op == OpType::Add ? 1.0 : -1.0) {498// Symbolic perturbation:499// Union -> expand inP500// Difference, Intersection -> contract inP501constexpr size_t INT_MAX_SZ =502static_cast<size_t>(std::numeric_limits<int>::max());503504if (inP.IsEmpty() || inQ.IsEmpty() || !inP.bBox_.DoesOverlap(inQ.bBox_)) {505PRINT("No overlap, early out");506w03_.resize(inP.NumVert(), 0);507w30_.resize(inQ.NumVert(), 0);508return;509}510511#ifdef MANIFOLD_DEBUG512Timer intersections;513intersections.Start();514#endif515516// Level 3517// Build up the intersection of the edges and triangles, keeping only those518// that intersect, and record the direction the edge is passing through the519// triangle.520std::tie(x12_, v12_) = Intersect12(inP, inQ, p1q2_, expandP_, true);521PRINT("x12 size = " << x12_.size());522523std::tie(x21_, v21_) = Intersect12(inP, inQ, p2q1_, expandP_, false);524PRINT("x21 size = " << x21_.size());525526if (x12_.size() > INT_MAX_SZ || x21_.size() > INT_MAX_SZ) {527valid = false;528return;529}530531// Compute winding numbers of all vertices using flood fill532// Vertices on the same connected component have the same winding number533w03_ = Winding03(inP, inQ, p1q2_, expandP_, true);534w30_ = Winding03(inP, inQ, p2q1_, expandP_, false);535536#ifdef MANIFOLD_DEBUG537intersections.Stop();538539if (ManifoldParams().verbose) {540intersections.Print("Intersections");541}542#endif543}544} // namespace manifold545546547