14 #ifndef OPENVDB_TOOLS_LEVELSETDILATEDMESHIMPL_HAS_BEEN_INCLUDED 15 #define OPENVDB_TOOLS_LEVELSETDILATEDMESHIMPL_HAS_BEEN_INCLUDED 27 #include <tbb/parallel_for.h> 28 #include <tbb/parallel_reduce.h> 31 #include <type_traits> 47 template <
typename Gr
idType,
typename InterruptT = util::NullInterrupter>
51 OpenTriangularPrismVoxelizer<GridType, InterruptT>,
54 using GridPtr =
typename GridType::Ptr;
63 using BaseT::tileCeil;
72 OpenTriangularPrismVoxelizer<GridType, InterruptT>,
87 InterruptT* interrupter =
nullptr)
98 template<
typename ScalarType>
103 static_assert(std::is_floating_point<ScalarType>::value);
112 setXYRangeData(
const Index& step = 1)
114 const ValueT &x1 = mPts[0].x(), &x2 = mPts[1].x(), &x3 = mPts[2].x(),
115 &x4 = mPts[3].x(), &x5 = mPts[4].x(), &x6 = mPts[5].x();
117 const ValueT xmin =
math::Min(x1, x2, x3, x4, x5, x6);
118 const ValueT xmax =
math::Max(x1, x2, x3, x4, x5, x6);
119 mXYData.reset(xmin, xmax, step);
124 setXYSegmentRangeData<0,1,0>(step);
125 setXYSegmentRangeData<1,2,0>(step);
126 setXYSegmentRangeData<2,0,0>(step);
128 setXYSegmentRangeData<3,4,0>(step);
129 setXYSegmentRangeData<4,5,0>(step);
130 setXYSegmentRangeData<5,3,0>(step);
132 setXYSegmentRangeData<0,3,0>(step);
133 setXYSegmentRangeData<1,4,0>(step);
134 setXYSegmentRangeData<2,5,0>(step);
137 template<Index i, Index j,
int MinMax = 0>
139 setXYSegmentRangeData(
const Index& step = 1)
141 const ValueT &x1 = mPts[i].x(), &x2 = mPts[j].x();
145 if (tileCeil(x1, step) == tileCeil(x2, step))
148 const ValueT x_start = tileCeil(
math::Min(x1, x2), step),
150 stepv = ValueT(step);
152 for (ValueT x = x_start; x <= x_end; x += stepv) {
153 if constexpr (MinMax <= 0)
154 mXYData.expandYMin(x, line2D<i,j>(x));
155 if constexpr (MinMax >= 0)
156 mXYData.expandYMax(x, line2D<i,j>(x));
162 signedDistance(
const Vec3T& p)
const 164 return math::Abs(mTriNrml.dot(p - mA)) - mRad;
170 tilePointSignedDistance(
const Vec3T& p)
const 172 const Vec3T pa = p - mA,
193 tileCanFit(
const Index& dim)
const 195 return mRad >= BaseT::halfWidth() + ValueT(0.5) * (ValueT(dim)-ValueT(1));
198 std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> prismBottomTop =
199 [
this](ValueT& zb, ValueT& zt,
const ValueT& x,
const ValueT& y)
201 zb = std::numeric_limits<ValueT>::lowest();
206 setPlaneBottomTop<0>(zb, zt, x, y);
207 setPlaneBottomTop<1>(zb, zt, x, y);
208 setPlaneBottomTop<2>(zb, zt, x, y);
209 setPlaneBottomTop<3>(zb, zt, x, y);
210 setPlaneBottomTop<4>(zb, zt, x, y);
217 setPlaneBottomTop(ValueT& zb, ValueT& zt,
const ValueT& x,
const ValueT& y)
const 222 const ValueT z = mPlaneXCoeffs[i]*x + mPlaneYCoeffs[i]*y + mPlaneOffsets[i];
224 if (mFaceNrmls[i].z() < 0) {
235 template<
typename ScalarType>
240 const ValueT vx = BaseT::voxelSize(),
241 hw = BaseT::halfWidth();
253 mTriNrml = mBA.cross(mC-mA);
255 mBAXNrml = mTriNrml.cross(mBA);
256 mCBXNrml = mTriNrml.cross(mCB);
257 mACXNrml = mTriNrml.cross(mAC);
263 const ValueT len = mTriNrml.length();
270 const ValueT hwRad = mRad + hw;
275 mA + hwRad * mTriNrml, mB + hwRad * mTriNrml, mC + hwRad * mTriNrml,
276 mA - hwRad * mTriNrml, mB - hwRad * mTriNrml, mC - hwRad * mTriNrml
283 mTriNrml.cross(mA-mB).unitSafe(),
284 mTriNrml.cross(mB-mC).unitSafe(),
285 mTriNrml.cross(mC-mA).unitSafe()
289 static const std::vector<Index> p_ind = {0, 3, 0, 1, 2};
291 mPlaneXCoeffs.assign(5, ValueT(0));
292 mPlaneYCoeffs.assign(5, ValueT(0));
293 mPlaneOffsets.assign(5, ValueT(0));
295 for (
Index i = 0; i < 5; ++i) {
297 const ValueT cx = mFaceNrmls[i].x()/mFaceNrmls[i].z(),
298 cy = mFaceNrmls[i].y()/mFaceNrmls[i].z();
299 const Vec3T p = mPts[p_ind[i]];
300 mPlaneXCoeffs[i] = -cx;
301 mPlaneYCoeffs[i] = -cy;
302 mPlaneOffsets[i] = p.x()*cx + p.y()*cy + p.z();
307 BaseT::bottomTop = prismBottomTop;
314 template <Index i, Index j>
316 line2D(
const ValueT& x)
const 318 const ValueT &x1 = mPts[i].x(), &y1 = mPts[i].y(),
319 &x2 = mPts[j].x(), &y2 = mPts[j].y();
321 const ValueT m = (y2-y1)/(x2-x1);
323 return y1 + m * (x-x1);
332 Vec3T mBAXNrml, mCBXNrml, mACXNrml;
333 Vec3T mBANorm2, mCBNorm2, mACNorm2;
335 std::vector<Vec3T> mPts = std::vector<Vec3T>(6);
338 std::vector<Vec3T> mFaceNrmls = std::vector<Vec3T>(5);
340 std::vector<ValueT> mPlaneXCoeffs = std::vector<ValueT>(5),
341 mPlaneYCoeffs = std::vector<ValueT>(5),
342 mPlaneOffsets = std::vector<ValueT>(5);
352 template <
typename Gr
idType,
typename InterruptT = util::NullInterrupter>
356 OpenCapsuleWedgeVoxelizer<GridType, InterruptT>,
359 using GridPtr =
typename GridType::Ptr;
367 using BaseT::mXYData;
368 using BaseT::tileCeil;
378 OpenCapsuleWedgeVoxelizer<GridType, InterruptT>,
392 InterruptT* interrupter =
nullptr)
407 template<
typename ScalarType>
413 static_assert(std::is_floating_point<ScalarType>::value);
415 if (
initialize(pt1, pt2, radius, nrml1, nrml2))
423 setXYRangeData(
const Index& step = 1)
425 const ValueT stepv = ValueT(step);
428 if (mX1 - mORad > mX2 + mORad) {
435 mXYData.reset(mX1 - mORad, mX1 + mORad, step);
437 for (ValueT x = tileCeil(mX1 - mORad, step); x <= mX1 + mORad; x += stepv)
438 mXYData.expandYRange(x, circle1Bottom(x), circle1Top(x));
440 intersectWithXYWedgeLines();
446 const ValueT a0 = mX1 - mORad,
453 const ValueT tc0 = tileCeil(a0, step),
454 tc1 = tileCeil(a1, step),
455 tc2 = tileCeil(a2, step),
456 tc3 = tileCeil(a3, step),
457 tc4 = tileCeil(a4, step);
459 mXYData.reset(a0, a5, step);
461 for (ValueT x = tc0; x <= a1; x += stepv)
462 mXYData.expandYRange(x, circle1Bottom(x), circle1Top(x));
466 for (ValueT x = tc1; x <=
math::Min(a2, a3); x += stepv)
467 mXYData.expandYRange(x, lineBottom(x), circle1Top(x));
469 for (ValueT x = tc1; x <=
math::Min(a2, a3); x += stepv)
470 mXYData.expandYRange(x, circle1Bottom(x), lineTop(x));
475 for (ValueT x = tc2; x <= a3; x += stepv)
476 mXYData.expandYRange(x, lineBottom(x), lineTop(x));
479 for (ValueT x = tc3; x <= a2; x += stepv)
480 mXYData.expandYRange(x, circle2Bottom(x), circle1Top(x));
482 for (ValueT x = tc3; x <= a2; x += stepv)
483 mXYData.expandYRange(x, circle1Bottom(x), circle2Top(x));
489 for (ValueT x =
math::Max(tc2, tc3); x <= a4; x += stepv)
490 mXYData.expandYRange(x, circle2Bottom(x), lineTop(x));
492 for (ValueT x =
math::Max(tc2, tc3); x <= a4; x += stepv)
493 mXYData.expandYRange(x, lineBottom(x), circle2Top(x));
497 for (ValueT x = tc4; x <= a5; x += stepv)
498 mXYData.expandYRange(x, circle2Bottom(x), circle2Top(x));
500 intersectWithXYStrip();
501 intersectWithXYWedgeLines();
505 intersectWithXYStrip()
511 const Vec3T &pp1 = mPlanePts[0], &pp2 = mPlanePts[1];
512 const ValueT &vx = mV.x(), &vy = mV.y();
514 Vec2T n = Vec2T(-vy, vx).unitSafe();
515 Vec3T cvec = mORad * Vec3T(-vy, vx, ValueT(0)).unitSafe();
522 const Vec3T cpmin(mPt1 - cvec), cpmax(mPt1 + cvec);
525 const ValueT px = mPt1.x(),
530 intersectWithXYHalfSpace(n.x() < 0 ? n : -n, Vec2T(xmin, ValueT(0)));
533 intersectWithXYHalfSpace(n.x() > 0 ? n : -n, Vec2T(xmax, ValueT(0)));
535 const ValueT m = mYdiff/mXdiff;
536 const ValueT y1 = mPt1.y() - m * mPt1.x(),
537 y2 = pp1.y() - m * pp1.x(),
538 y3 = pp2.y() - m * pp2.x();
539 const ValueT ymin =
math::Min(y1, y2, y3),
542 if (!inWedge(vy <= 0 ? cpmin : cpmax))
543 intersectWithXYHalfSpace(n.y() < 0 ? n : -n, Vec2T(ValueT(0), ymin));
545 if (!inWedge(vy > 0 ? cpmin : cpmax))
546 intersectWithXYHalfSpace(n.y() > 0 ? n : -n, Vec2T(ValueT(0), ymax));
551 intersectWithXYWedgeLines()
553 const Vec3T v(mORad * mV.unitSafe()),
557 const Vec2T p1_2d(p1.x(), p1.y()), p2_2d(p2.x(), p2.y());
559 Vec2T d(-mPlaneNrmls[0].x() - mPlaneNrmls[1].x(),
560 -mPlaneNrmls[0].y() - mPlaneNrmls[1].y());
562 Vec2T n0(-mDirVectors[0].y(), mDirVectors[0].x()),
563 n1(-mDirVectors[1].y(), mDirVectors[1].x());
571 intersectWithXYHalfSpace(n0, n0.dot(p2_2d - p1_2d) < 0 ? p1_2d : p2_2d);
574 intersectWithXYHalfSpace(n1, n1.dot(p2_2d - p1_2d) < 0 ? p1_2d : p2_2d);
578 intersectWithXYHalfSpace(
const Vec2T& n,
const Vec2T& p)
580 if (mXYData.size() == 0)
584 const ValueT &px = p.x();
586 const Index m = mXYData.size();
587 for (
Index i = 0; i < m; ++i) {
588 const ValueT x = mXYData.getX(i);
590 if (x < px) mXYData.clearYRange(x);
594 Index i = mXYData.size()-1;
596 const ValueT x = mXYData.getX(i);
598 if (x > px) mXYData.clearYRange(x);
606 const bool set_min = n.y() < 0;
607 const Index m = mXYData.size();
609 const ValueT b = -n.x()/n.y();
610 const ValueT a = p.y() - b * p.x();
612 ValueT x, ymin, ymax;
613 for (
Index i = 0; i < m; ++i) {
614 mXYData.XYData(x, ymin, ymax, i);
615 const ValueT yint = a + b * x;
617 if (ymin <= yint && yint <= ymax) {
618 if (set_min) mXYData.setYMin(x, yint);
619 else mXYData.setYMax(x, yint);
621 if (set_min ? yint > ymax : yint < ymin)
622 mXYData.clearYRange(x);
632 signedDistance(
const Vec3T& p)
const 634 const Vec3T w = p - mPt1;
635 const ValueT dot = w.dot(mV);
639 return w.length() - mRad;
642 return (p - mPt2).length() - mRad;
644 const ValueT t = w.dot(mV)/mVLenSqr;
646 return (w - t * mV).length() - mRad;
650 tileCanFit(
const Index& dim)
const 652 return mRad >= BaseT::halfWidth() + ValueT(0.5) * (ValueT(dim)-ValueT(1));
655 std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> capsuleBottomTopVertical =
656 [
this](ValueT& zb, ValueT& zt,
const ValueT& x,
const ValueT& y)
658 zb = BaseT::sphereBottom(mX1, mY1,
math::Min(mZ1, mZ2), mORad, x, y);
659 zt = BaseT::sphereTop(mX2, mY2,
math::Max(mZ1, mZ2), mORad, x, y);
661 return std::isfinite(zb) && std::isfinite(zt);
664 std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> capsuleBottomTop =
665 [
this](ValueT& zb, ValueT& zt,
const ValueT& x,
const ValueT& y)
667 ValueT cylptb, cylptt;
668 if (!infiniteCylinderBottomTop(cylptb, cylptt, x, y))
671 const ValueT dotb = (Vec3T(x, y, cylptb) - mPt1).dot(mV);
672 const ValueT dott = (Vec3T(x, y, cylptt) - mPt1).dot(mV);
675 zb = sphere1Bottom(x, y);
676 else if (dotb > mVLenSqr)
677 zb = sphere2Bottom(x, y);
682 zt = sphere1Top(x, y);
683 else if (dott > mVLenSqr)
684 zt = sphere2Top(x, y);
688 if (!std::isfinite(zb) || !std::isfinite(zt))
691 intersectWedge<0,1>(zb, zt, x, y);
692 intersectWedge<1,0>(zb, zt, x, y);
694 return inWedge(x, y, ValueT(0.5)*(zb+zt));
697 template<Index i, Index j>
699 intersectWedge(ValueT& zb, ValueT& zt,
const ValueT& x,
const ValueT& y)
701 const Vec3T& n0 = mPlaneNrmls[i];
706 const ValueT zp = mPlaneXCoeffs[i]*x + mPlaneYCoeffs[i]*y + mPlaneOffsets[i];
708 if (zb <= zp && zp <= zt && inHalfSpace<j>(Vec3T(x, y, zp))) {
717 inWedge(
const ValueT& x,
const ValueT& y,
const ValueT& z)
719 return inWedge(Vec3T(x, y, z));
723 inWedge(
const Vec3T& pt)
725 return inHalfSpace<0>(pt) && inHalfSpace<1>(pt);
730 inHalfSpace(
const Vec3T& pt)
735 static const ValueT VOXFRAC = 0.125;
737 return mPlaneNrmls[i].dot(pt-mPt1) <= VOXFRAC;
742 infiniteCylinderBottomTop(ValueT& cylptb, ValueT& cylptt,
743 const ValueT& x,
const ValueT& y)
const 747 const Vec2T qproj = mPt12d + mV2d*((q - mPt12d).dot(mV2d))/mXYNorm2;
749 const ValueT t = mX1 != mX2 ? (qproj[0] - mX1)/mXdiff : (qproj[1] - mY1)/mYdiff;
751 const Vec3T qproj3D = mPt1 + t * mV;
753 const ValueT d2 = (q - qproj).lengthSqr();
759 const ValueT h =
math::Sqrt((mORad2 - d2) * mVLenSqr/mXYNorm2);
761 cylptb = qproj3D[2] - h;
762 cylptt = qproj3D[2] + h;
768 lineBottom(
const ValueT& x)
const 770 return mY1 + (mYdiff*(x-mX1) - mORad * mXYNorm)/mXdiff;
774 lineTop(
const ValueT& x)
const 776 return mY1 + (mYdiff*(x-mX1) + mORad * mXYNorm)/mXdiff;
780 circle1Bottom(
const ValueT& x)
const 782 return BaseT::circleBottom(mX1, mY1, mORad, x);
786 circle1Top(
const ValueT& x)
const 788 return BaseT::circleTop(mX1, mY1, mORad, x);
792 circle2Bottom(
const ValueT& x)
const 794 return BaseT::circleBottom(mX2, mY2, mORad, x);
798 circle2Top(
const ValueT& x)
const 800 return BaseT::circleTop(mX2, mY2, mORad, x);
804 sphere1Bottom(
const ValueT& x,
const ValueT& y)
const 806 return BaseT::sphereBottom(mX1, mY1, mZ1, mORad, x, y);
810 sphere1Top(
const ValueT& x,
const ValueT& y)
const 812 return BaseT::sphereTop(mX1, mY1, mZ1, mORad, x, y);
816 sphere2Bottom(
const ValueT& x,
const ValueT& y)
const 818 return BaseT::sphereBottom(mX2, mY2, mZ2, mORad, x, y);
822 sphere2Top(
const ValueT& x,
const ValueT& y)
const 824 return BaseT::sphereTop(mX2, mY2, mZ2, mORad, x, y);
829 template<
typename ScalarType>
835 const ValueT vx = BaseT::voxelSize(),
836 hw = BaseT::halfWidth();
839 if (pt1[0] <= pt2[0]) {
840 mPt1 = Vec3T(pt1)/vx;
841 mPt2 = Vec3T(pt2)/vx;
843 mPt1 = Vec3T(pt2)/vx;
844 mPt2 = Vec3T(pt1)/vx;
851 mORad2 = mORad * mORad;
858 mVLenSqr = mV.lengthSqr();
864 mX1 = mPt1[0]; mY1 = mPt1[1]; mZ1 = mPt1[2];
865 mX2 = mPt2[0]; mY2 = mPt2[1]; mZ2 = mPt2[2];
871 mPt12d = Vec2T(mX1, mY1);
872 mPt22d = Vec2T(mX2, mY2);
873 mV2d = mPt22d - mPt12d;
880 const Vec3T n1 = Vec3T(nrml1), n2 = Vec3T(nrml2);
886 mPlaneNrmls[0] = (n1 - n1.projection(mV)).unitSafe();
887 mPlaneNrmls[1] = (n2 - n2.projection(mV)).unitSafe();
890 if (approxAntiParallel(mPlaneNrmls[0], mPlaneNrmls[1]))
893 mDirVectors[0] = mORad * mV.cross(mPlaneNrmls[0]).unitSafe();
894 mDirVectors[1] = mORad * mV.cross(mPlaneNrmls[1]).unitSafe();
896 if (approxParallel(mPlaneNrmls[0], mPlaneNrmls[1])) {
897 mDirVectors[1] = -mDirVectors[0];
899 if (mPlaneNrmls[1].dot(mDirVectors[0]) > 0)
900 mDirVectors[0] *= ValueT(-1);
901 if (mPlaneNrmls[0].dot(mDirVectors[1]) > 0)
902 mDirVectors[1] *= ValueT(-1);
905 mPlanePts[0] = mPt1 + mDirVectors[0] + ValueT(0.025) * mPlaneNrmls[0];
906 mPlanePts[1] = mPt1 + mDirVectors[1] + ValueT(0.025) * mPlaneNrmls[1];
910 mPlaneXCoeffs.assign(2, ValueT(0));
911 mPlaneYCoeffs.assign(2, ValueT(0));
912 mPlaneOffsets.assign(2, ValueT(0));
914 for (
Index i = 0; i < 2; ++i) {
916 const ValueT cx = mPlaneNrmls[i].x()/mPlaneNrmls[i].z(),
917 cy = mPlaneNrmls[i].y()/mPlaneNrmls[i].z();
918 const Vec3T p = mPlanePts[i];
919 mPlaneXCoeffs[i] = -cx;
920 mPlaneYCoeffs[i] = -cy;
921 mPlaneOffsets[i] = p.x()*cx + p.y()*cy + p.z();
926 BaseT::bottomTop = mIsVertical ? capsuleBottomTopVertical : capsuleBottomTop;
932 approxAntiParallel(
const Vec3T& n1,
const Vec3T& n2)
934 return approxParallel(n1, -n2);
938 approxParallel(
const Vec3T& n1,
const Vec3T& n2)
940 return n1.unitSafe().eq(n2.unitSafe());
947 Vec3T mPt1, mPt2, mV;
949 Vec2T mPt12d, mPt22d, mV2d;
951 ValueT mORad, mORad2, mRad, mVLenSqr, mXdiff, mYdiff, mZdiff, mXYNorm, mXYNorm2;
953 ValueT mX1, mY1, mZ1, mX2, mY2, mZ2;
957 std::vector<Vec3T> mPlaneNrmls = std::vector<Vec3T>(2),
958 mDirVectors = std::vector<Vec3T>(2),
959 mPlanePts = std::vector<Vec3T>(2);
961 std::vector<ValueT> mPlaneXCoeffs = std::vector<ValueT>(2),
962 mPlaneYCoeffs = std::vector<ValueT>(2),
963 mPlaneOffsets = std::vector<ValueT>(2);
972 template<
typename ValueT>
975 static_assert(std::is_floating_point<ValueT>::value);
987 const std::vector<Vec3I>& cells)
988 : mCoords(coords), mCells(cells)
992 mNormals.resize(cells.size());
994 for (
Index i = 0; i < cells.size(); ++i) {
995 const Vec3I& cell = mCells[i];
998 Edge(cell[0], cell[1]),
999 Edge(cell[1], cell[2]),
1000 Edge(cell[2], cell[0])
1003 for (
const Edge& edge : edges) {
1004 mEdgeMap[edge].push_back(i);
1007 if (cell[0] >= n || cell[1] >= n || cell[2] >= n)
1010 const Vec3T &p1 = mCoords[cell[0]],
1011 &p2 = mCoords[cell[1]],
1012 &p3 = mCoords[cell[2]];
1014 mNormals[i] = (p2 - p1).cross(p3 - p1).
unitSafe();
1017 for (
auto& [edge, cells] : mEdgeMap)
1018 sortAdjacentCells(edge, cells);
1031 auto it = mEdgeMap.find(edge);
1032 if (it != mEdgeMap.end()) {
1033 cellIds = it->second;
1043 template <
typename T>
1047 static_assert(std::is_integral<T>::value,
"Index must be an integral type");
1056 template <
typename T>
1060 static_assert(std::is_integral<T>::value,
"Index must be an integral type");
1070 template <
typename T>
1071 inline std::vector<Vec3T>
1074 static_assert(std::is_integral<T>::value,
"Index must be an integral type");
1076 const Vec3I cell = mCells[i];
1078 return {mCoords[cell[0]], mCoords[cell[1]], mCoords[cell[2]]};
1085 template <
typename T>
1089 static_assert(std::is_integral<T>::value,
"Index must be an integral type");
1100 return mCoords.size();
1109 return mCells.size();
1123 return mV1 < e.mV1 || (mV1 == e.mV1 && mV2 < e.mV2);
1128 centroid(
Index cellIdx)
const 1130 const Vec3I cell = mCells[cellIdx];
1131 return (mCoords[cell[0]] + mCoords[cell[1]] + mCoords[cell[2]]) / 3.0;
1141 sortAdjacentCells(
const Edge& edge, std::vector<Index>& cells)
1143 if (cells.size() <= 2)
return;
1145 const Vec3I &base_cell = mCells[cells[0]];
1146 const Index offset = edge.mV1 + edge.mV2;
1148 const Index p1Ind = base_cell[0] + base_cell[1] + base_cell[2] - offset;
1150 const Vec3T &p1 = mCoords[p1Ind],
1151 &n1 = mNormals[cells[0]];
1153 const Vec3T p0 = mCoords[edge.mV1];
1155 Vec3T bi_nrml = n1.
cross(p0 - mCoords[edge.mV2]);
1156 if (bi_nrml.
dot(p1 - p0) > 0)
1157 bi_nrml *= ValueT(-1);
1159 auto windingamount = [&](
Index cellIdx)
1161 if (cellIdx == 0)
return 0.0f;
1163 const Vec3I &cell = mCells[cellIdx];
1164 const Index p2Ind = cell[0] + cell[1] + cell[2] - offset;
1166 const Vec3T &p2 = mCoords[p2Ind],
1167 &n2 = mNormals[cellIdx];
1169 const ValueT cos_theta =
math::Abs(n1.dot(n2));
1175 ? ValueT(1) + ValueT(sgn2) * cos_theta
1176 : ValueT(3) - ValueT(sgn2) * cos_theta
1178 : (onSameHalfPlane(bi_nrml, p0, p1, p2) ? ValueT(0) : ValueT(2));
1181 std::sort(cells.begin(), cells.end(), [&](
const Index& t1,
const Index& t2) {
1182 return windingamount(t1) < windingamount(t2);
1188 const std::vector<Vec3T>& mCoords;
1189 const std::vector<Vec3I>& mCells;
1191 std::vector<Vec3T> mNormals;
1193 std::map<Edge, std::vector<Index>> mEdgeMap;
1204 template <
typename GridType,
typename ScalarType = float,
1208 using GridPtr =
typename GridType::Ptr;
1209 using TreeT =
typename GridType::TreeType;
1210 using LeafT =
typename TreeT::LeafNodeType;
1221 static_assert(std::is_floating_point<ScalarType>::value);
1235 ScalarType radius,
float voxelSize,
float halfWidth, InterruptT* interrupter)
1237 , mVox(voxelSize), mHw(halfWidth), mRad(radius)
1238 , mInterrupter(interrupter)
1242 if constexpr (PtPartition)
1243 initializePartitioner();
1245 mPVoxelizer = std::make_shared<PrismVoxelizer>(mGrid,
false);
1246 mWVoxelizer = std::make_shared<WedgeVoxelizer>(mGrid,
false);
1250 : mMesh(other.mMesh), mVox(other.mVox), mHw(other.mHw)
1251 , mRad(other.mRad), mInterrupter(other.mInterrupter)
1252 , mPtPartitioner(other.mPtPartitioner)
1256 mPVoxelizer = std::make_shared<PrismVoxelizer>(mGrid,
false);
1257 mWVoxelizer = std::make_shared<WedgeVoxelizer>(mGrid,
false);
1262 if (!checkInterrupter())
1265 if constexpr (PtPartition) {
1266 for (
size_t i = rng.begin(); i < rng.end(); ++i)
1267 for (
auto it = mPtPartitioner->indices(i); it; ++it)
1268 voxelizeTriangle(*it);
1270 for (
size_t i = rng.begin(); i < rng.end(); ++i)
1271 voxelizeTriangle(i);
1281 other.mGrid =
nullptr;
1293 affinelyIndependent(
const Vec3T& p1,
const Vec3T& p2,
const Vec3T& p3)
const 1295 const Vec3T n = (p2-p1).cross(p3-p1);
1302 voxelizeTriangle(
const size_t& i)
1304 const Vec3I &cell = mMesh->getCell(i);
1305 const std::vector<Vec3T> pts = mMesh->getPrimitive(i);
1308 if (!affinelyIndependent(pts[0], pts[1], pts[2])) {
1309 voxelizeCapsule(pts[0], pts[1], pts[2]);
1314 (*mPVoxelizer)(pts[0], pts[1], pts[2], mRad);
1316 std::vector<Index> cellIds;
1320 for (
Index j = 0; j < 3; ++j) {
1321 const bool success = mMesh->getAdjacentCells(cell[j], cell[(j+1) % 3], cellIds);
1322 if (success && cellIds[0] == i) {
1323 if (findWedgeNormals(
Index(i), j, cellIds, n1, n2))
1324 (*mWVoxelizer)(pts[j], pts[(j+1) % 3], mRad, n1, n2);
1334 ScalarType d1 = (p2-p1).lengthSqr(),
1335 d2 = (p3-p2).lengthSqr(),
1336 d3 = (p1-p3).lengthSqr();
1338 ScalarType maxd =
math::Max(d1, d2, d3);
1341 voxelizer(p1, p2, mRad);
1342 else if (maxd == d2)
1343 voxelizer(p2, p3, mRad);
1345 voxelizer(p3, p1, mRad);
1349 findWedgeNormals(
const Index& cellIdx,
const Index& vIdx,
1350 const std::vector<Index>& cellIds,
Vec3T& n1,
Vec3T& n2)
const 1352 if (cellIds.size() == 1)
1353 return findWedgeNormals1(cellIdx, vIdx, n1, n2);
1354 else if (cellIds.size() == 2)
1355 return findWedgeNormals2(cellIdx, vIdx, cellIds[1], n1, n2);
1356 else if (cellIds.size() > 2)
1357 return findWedgeNormals3(cellIdx, vIdx, cellIds, n1, n2);
1363 findWedgeNormals1(
const Index& cellIdx,
const Index& vIdx,
1366 const Vec3I &cell = mMesh->getCell(cellIdx);
1367 const Vec3T &p1 = mMesh->getCoord(cell[vIdx]),
1368 &p2 = mMesh->getCoord(cell[(vIdx+1) % 3]),
1369 &p3 = mMesh->getCoord(cell[(vIdx+2) % 3]);
1371 const Vec3T &n = mMesh->getNormal(cellIdx);
1373 n1 = n.
cross(p2-p1).unitSafe();
1374 if (n1.
dot(p3-p1) < 0) n1 *= -1.0f;
1382 findWedgeNormals2(
const Index& cellIdx,
const Index& vIdx,
1385 const Vec3I &cell = mMesh->getCell(cellIdx),
1386 &cell2 = mMesh->getCell(cellIdx2);
1388 const Index cIdx2 = cell2[0] + cell2[1] + cell2[2] - cell[vIdx] - cell[(vIdx+1) % 3];
1390 const Vec3T &p1 = mMesh->getCoord(cell[vIdx]),
1391 &p2 = mMesh->getCoord(cell[(vIdx+1) % 3]),
1392 &p3 = mMesh->getCoord(cell[(vIdx+2) % 3]),
1393 &p4 = mMesh->getCoord(cIdx2);
1395 const Vec3T &nrml1 = mMesh->getNormal(cellIdx),
1396 &nrml2 = mMesh->getNormal(cellIdx2);
1398 n1 = nrml1.
cross(p2-p1).unitSafe(),
1399 n2 = nrml2.
cross(p2-p1).unitSafe();
1401 if (n1.
dot(p3-p1) < 0) n1 *= -1.0f;
1402 if (n2.
dot(p4-p1) < 0) n2 *= -1.0f;
1408 findWedgeNormals3(
const Index& cellIdx,
const Index& vIdx,
1409 const std::vector<Index>& cellIds,
Vec3T& n1,
Vec3T& n2)
const 1411 const Vec3I &cell = mMesh->getCell(cellIdx);
1414 const Index offset = cell[vIdx] + cell[(vIdx+1) % 3];
1416 for (
Index64 i = 0; i < n; ++i) {
1417 const Vec3I &cell0 = mMesh->getCell(cellIds[i]),
1418 &cell1 = mMesh->getCell(cellIds[(i+1) % n]),
1419 &cell2 = mMesh->getCell(cellIds[(i+2) % n]);
1421 const Index cIdx0 = cell0[0] + cell0[1] + cell0[2] - offset,
1422 cIdx1 = cell1[0] + cell1[1] + cell1[2] - offset,
1423 cIdx2 = cell2[0] + cell2[1] + cell2[2] - offset;
1425 const Vec3T &p0 = mMesh->getCoord(cIdx0),
1426 &p1 = mMesh->getCoord(cIdx1),
1427 &p2 = mMesh->getCoord(cIdx2);
1429 Vec3T nrml0 = mMesh->getNormal(cellIds[i]),
1430 nrml1 = mMesh->getNormal(cellIds[(i+1) % n]);
1432 if (nrml0.
dot(p1-p0) > 0) nrml0 *= ScalarType(-1);
1433 if (nrml1.dot(p0-p1) > 0) nrml1 *= ScalarType(-1);
1435 if (nrml0.
dot(p2-p0) > 0 || nrml1.dot(p2-p1) > 0)
1439 if (cell0[0] == cell[vIdx])
1440 vIdxi = cell0[1] == cell[(vIdx+1) % 3] ? 0 : 2;
1441 else if (cell0[1] == cell[vIdx])
1442 vIdxi = cell0[2] == cell[(vIdx+1) % 3] ? 1 : 0;
1444 vIdxi = cell0[0] == cell[(vIdx+1) % 3] ? 2 : 1;
1446 return findWedgeNormals2(cellIds[i], vIdxi, cellIds[(i+1) % n], n1, n2);
1453 computeCentroids(std::vector<Vec3T>& centroids)
1455 centroids.resize(mMesh->cellCount());
1457 tbb::parallel_for(tbb::blocked_range<size_t>(0, centroids.size()),
1458 [&](
const tbb::blocked_range<size_t>& r) {
1459 for (
size_t i = r.begin(); i != r.end(); ++i) {
1460 const std::vector<Vec3T> prim = mMesh->getPrimitive(i);
1461 centroids[i] = (prim[0] + prim[1] + prim[2]) / ScalarType(3);
1469 mGrid = createLevelSet<GridType>(mVox, mHw);
1473 initializePartitioner()
1475 std::vector<Vec3T> centroids;
1476 computeCentroids(centroids);
1480 mPtPartitioner = std::make_shared<PartitionerT>();
1481 mPtPartitioner->construct(points, mGrid->transform());
1488 openvdb::thread::cancelGroupExecution();
1496 std::shared_ptr<const MeshConnectivity> mMesh;
1498 const float mVox, mHw;
1500 const ScalarType mRad;
1502 InterruptT* mInterrupter;
1506 std::shared_ptr<PartitionerT> mPtPartitioner;
1508 std::shared_ptr<PrismVoxelizer> mPVoxelizer;
1509 std::shared_ptr<WedgeVoxelizer> mWVoxelizer;
1518 template <
typename Gr
idType,
typename ScalarType,
typename InterruptT>
1519 typename GridType::Ptr
1522 ScalarType radius,
float voxelSize,
float halfWidth, InterruptT* interrupter)
1524 static_assert(std::is_floating_point<ScalarType>::value);
1526 using GridPtr =
typename GridType::Ptr;
1527 using ValueT =
typename GridType::ValueType;
1531 static_assert(std::is_floating_point<ValueT>::value,
1532 "createLevelSetDilatedMesh must return a scalar grid");
1537 Voxelizer
op(vertices, triangles, radius, voxelSize, halfWidth, interrupter);
1539 const tbb::blocked_range<size_t> triangleRange(0, op.bucketSize());
1540 tbb::parallel_reduce(triangleRange, op);
1548 template <
typename Gr
idType,
typename ScalarType,
typename InterruptT>
1549 typename GridType::Ptr
1552 ScalarType radius,
float voxelSize,
float halfWidth, InterruptT* interrupter)
1554 static_assert(std::is_floating_point<ScalarType>::value);
1556 using ValueT =
typename GridType::ValueType;
1558 static_assert(std::is_floating_point<ValueT>::value,
1559 "createLevelSetDilatedMesh must return a scalar grid");
1564 const Index64 n = quads.size();
1565 std::vector<Vec3I> triangles(2*n);
1567 tbb::parallel_for(tbb::blocked_range<size_t>(0, n),
1568 [&](
const tbb::blocked_range<size_t>& r) {
1569 for (
Index64 i = r.begin(); i != r.end(); ++i) {
1570 const Vec4I& q = quads[i];
1571 triangles[i] =
Vec3I(q.
x(), q.
y(), q.
z());
1572 triangles[i + n] =
Vec3I(q.
x(), q.
z(), q.
w());
1576 return createLevelSetDilatedMesh<GridType, ScalarType, InterruptT>(
1577 vertices, triangles, radius, voxelSize, halfWidth, interrupter);
1580 template <
typename Gr
idType,
typename ScalarType,
typename InterruptT>
1581 typename GridType::Ptr
1583 const std::vector<Vec3I>& triangles,
const std::vector<Vec4I>& quads,
1584 ScalarType radius,
float voxelSize,
float halfWidth, InterruptT* interrupter)
1586 static_assert(std::is_floating_point<ScalarType>::value);
1588 using ValueT =
typename GridType::ValueType;
1590 static_assert(std::is_floating_point<ValueT>::value,
1591 "createLevelSetDilatedMesh must return a scalar grid");
1597 return createLevelSetDilatedMesh<GridType, ScalarType, InterruptT>(
1598 vertices, triangles, radius, voxelSize, halfWidth, interrupter);
1600 const Index64 tn = triangles.size(), qn = quads.size();
1602 std::vector<Vec3I> tris(tn + 2*qn);
1604 tbb::parallel_for(tbb::blocked_range<size_t>(0, tn),
1605 [&](
const tbb::blocked_range<size_t>& r) {
1606 for (
Index64 i = r.begin(); i != r.end(); ++i) {
1607 tris[i] = triangles[i];
1611 tbb::parallel_for(tbb::blocked_range<size_t>(0, qn),
1612 [&](
const tbb::blocked_range<size_t>& r) {
1613 for (
Index64 i = r.begin(); i != r.end(); ++i) {
1614 const Vec4I& q = quads[i];
1615 tris[i + tn] =
Vec3I(q.
x(), q.
y(), q.
z());
1616 tris[i + qn2] =
Vec3I(q.
x(), q.
z(), q.
w());
1620 return createLevelSetDilatedMesh<GridType, ScalarType, InterruptT>(
1621 vertices, tris, radius, voxelSize, halfWidth, interrupter);
1628 #endif // OPENVDB_TOOLS_LEVELSETDILATEDMESHIMPL_HAS_BEEN_INCLUDED T & y()
Definition: Vec3.h:87
T & z()
Definition: Vec4.h:80
Definition: NodeManager.h:37
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
uint64_t Index64
Definition: Types.h:53
Coord Abs(const Coord &xyz)
Definition: Coord.h:518
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
OPENVDB_IMPORT void initialize()
Global registration of native Grid, Transform, Metadata and Point attribute types. Also initializes blosc (if enabled).
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:761
Type Clamp01(Type x)
Return x clamped to [0, 1].
Definition: Math.h:271
T & w()
Definition: Vec4.h:81
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec4.h:78
Index32 Index
Definition: Types.h:54
T & z()
Definition: Vec3.h:88
bool isApproxZero(const Type &x)
Return true if x is equal to zero to within the default floating-point comparison tolerance...
Definition: Math.h:349
openvdb::GridBase::Ptr GridPtr
Definition: Utils.h:44
Spatially partitions points using a parallel radix-based sorting algorithm.
Defined various multi-threaded utility functions for trees.
Base class used to generate the narrow-band level set of a convex region.
Definition: Exceptions.h:65
void foreachTopDown(const NodeOp &op, bool threaded=true, size_t leafGrainSize=1, size_t nonLeafGrainSize=1)
Threaded method that applies a user-supplied functor to all the nodes in the tree.
Definition: NodeManager.h:977
Base class for interrupters.
Definition: NullInterrupter.h:25
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:86
Tag dispatch class that distinguishes constructors that steal.
Definition: Types.h:758
Vec3< T > cross(const Vec3< T > &v) const
Return the cross product of "this" vector and v;.
Definition: Vec3.h:221
static const int size
Definition: Tuple.h:36
Type Pow2(Type x)
Return x2.
Definition: Math.h:548
Tolerance for floating-point comparison.
Definition: Math.h:148
Definition: Exceptions.h:13
T & y()
Definition: Vec4.h:79
bool operator<(const Tuple< SIZE, T0 > &t0, const Tuple< SIZE, T1 > &t1)
Definition: Tuple.h:175
Vec3< T > unitSafe() const
return normalized this, or (1, 0, 0) if this is null vector
Definition: Vec3.h:392
Generate a narrow-band level set of a capsule, tapered capsule, and tube complex. ...
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
GridType
List of types that are currently supported by NanoVDB.
Definition: NanoVDB.h:220
math::Vec3< Index32 > Vec3I
Definition: Types.h:73
const Type & Min(const Type &a, const Type &b)
Return the minimum of two values.
Definition: Math.h:656
int Sign(const Type &x)
Return the sign of the given value as an integer (either -1, 0 or 1).
Definition: Math.h:736
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:595
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
void split(ContainerT &out, const std::string &in, const char delim)
Definition: Name.h:43
T dot(const Vec3< T > &v) const
Dot product.
Definition: Vec3.h:192
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218