@itwin/core-geometry 4.3.0-dev.8 → 4.4.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/lib/cjs/Geometry.d.ts +14 -2
- package/lib/cjs/Geometry.d.ts.map +1 -1
- package/lib/cjs/Geometry.js +20 -9
- package/lib/cjs/Geometry.js.map +1 -1
- package/lib/cjs/bspline/BSpline1dNd.d.ts +12 -2
- package/lib/cjs/bspline/BSpline1dNd.d.ts.map +1 -1
- package/lib/cjs/bspline/BSpline1dNd.js +27 -17
- package/lib/cjs/bspline/BSpline1dNd.js.map +1 -1
- package/lib/cjs/bspline/BSplineCurve.d.ts +30 -16
- package/lib/cjs/bspline/BSplineCurve.d.ts.map +1 -1
- package/lib/cjs/bspline/BSplineCurve.js +68 -44
- package/lib/cjs/bspline/BSplineCurve.js.map +1 -1
- package/lib/cjs/bspline/BSplineCurve3dH.d.ts +27 -20
- package/lib/cjs/bspline/BSplineCurve3dH.d.ts.map +1 -1
- package/lib/cjs/bspline/BSplineCurve3dH.js +136 -87
- package/lib/cjs/bspline/BSplineCurve3dH.js.map +1 -1
- package/lib/cjs/bspline/BSplineSurface.d.ts +138 -74
- package/lib/cjs/bspline/BSplineSurface.d.ts.map +1 -1
- package/lib/cjs/bspline/BSplineSurface.js +242 -119
- package/lib/cjs/bspline/BSplineSurface.js.map +1 -1
- package/lib/cjs/bspline/Bezier1dNd.d.ts +18 -4
- package/lib/cjs/bspline/Bezier1dNd.d.ts.map +1 -1
- package/lib/cjs/bspline/Bezier1dNd.js +19 -5
- package/lib/cjs/bspline/Bezier1dNd.js.map +1 -1
- package/lib/cjs/bspline/BezierCurve3dH.d.ts.map +1 -1
- package/lib/cjs/bspline/BezierCurve3dH.js +2 -1
- package/lib/cjs/bspline/BezierCurve3dH.js.map +1 -1
- package/lib/cjs/bspline/KnotVector.d.ts +30 -33
- package/lib/cjs/bspline/KnotVector.d.ts.map +1 -1
- package/lib/cjs/bspline/KnotVector.js +76 -69
- package/lib/cjs/bspline/KnotVector.js.map +1 -1
- package/lib/cjs/clipping/ClipUtils.d.ts +32 -5
- package/lib/cjs/clipping/ClipUtils.d.ts.map +1 -1
- package/lib/cjs/clipping/ClipUtils.js +92 -11
- package/lib/cjs/clipping/ClipUtils.js.map +1 -1
- package/lib/cjs/core-geometry.d.ts +0 -2
- package/lib/cjs/core-geometry.d.ts.map +1 -1
- package/lib/cjs/core-geometry.js +0 -2
- package/lib/cjs/core-geometry.js.map +1 -1
- package/lib/cjs/curve/Arc3d.d.ts.map +1 -1
- package/lib/cjs/curve/Arc3d.js +5 -5
- package/lib/cjs/curve/Arc3d.js.map +1 -1
- package/lib/cjs/curve/CurveLocationDetail.d.ts +1 -4
- package/lib/cjs/curve/CurveLocationDetail.d.ts.map +1 -1
- package/lib/cjs/curve/CurveLocationDetail.js +4 -10
- package/lib/cjs/curve/CurveLocationDetail.js.map +1 -1
- package/lib/cjs/curve/CurveOps.d.ts +4 -5
- package/lib/cjs/curve/CurveOps.d.ts.map +1 -1
- package/lib/cjs/curve/CurveOps.js +1 -1
- package/lib/cjs/curve/CurveOps.js.map +1 -1
- package/lib/cjs/curve/CurveTypes.d.ts +14 -2
- package/lib/cjs/curve/CurveTypes.d.ts.map +1 -1
- package/lib/cjs/curve/CurveTypes.js.map +1 -1
- package/lib/cjs/curve/LineString3d.d.ts +3 -2
- package/lib/cjs/curve/LineString3d.d.ts.map +1 -1
- package/lib/cjs/curve/LineString3d.js +1 -0
- package/lib/cjs/curve/LineString3d.js.map +1 -1
- package/lib/cjs/curve/PointString3d.d.ts +2 -1
- package/lib/cjs/curve/PointString3d.d.ts.map +1 -1
- package/lib/cjs/curve/PointString3d.js.map +1 -1
- package/lib/cjs/curve/RegionOps.d.ts +5 -11
- package/lib/cjs/curve/RegionOps.d.ts.map +1 -1
- package/lib/cjs/curve/RegionOps.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +15 -9
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js +68 -56
- package/lib/cjs/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.d.ts +14 -16
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js +51 -69
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.js +3 -2
- package/lib/cjs/curve/internalContexts/CurveCurveIntersectXYZ.js.map +1 -1
- package/lib/cjs/curve/internalContexts/MultiChainCollector.d.ts +2 -3
- package/lib/cjs/curve/internalContexts/MultiChainCollector.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/MultiChainCollector.js.map +1 -1
- package/lib/cjs/curve/internalContexts/PolygonOffsetContext.d.ts +4 -4
- package/lib/cjs/curve/internalContexts/PolygonOffsetContext.d.ts.map +1 -1
- package/lib/cjs/curve/internalContexts/PolygonOffsetContext.js +38 -36
- package/lib/cjs/curve/internalContexts/PolygonOffsetContext.js.map +1 -1
- package/lib/cjs/geometry3d/Angle.d.ts +0 -1
- package/lib/cjs/geometry3d/Angle.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Angle.js +0 -1
- package/lib/cjs/geometry3d/Angle.js.map +1 -1
- package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts +21 -5
- package/lib/cjs/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
- package/lib/cjs/geometry3d/CoincidentGeometryOps.js +71 -45
- package/lib/cjs/geometry3d/CoincidentGeometryOps.js.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.d.ts +20 -13
- package/lib/cjs/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Matrix3d.js +28 -21
- package/lib/cjs/geometry3d/Matrix3d.js.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts +11 -2
- package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Point3dVector3d.js +16 -5
- package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.d.ts +58 -20
- package/lib/cjs/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/cjs/geometry3d/PointHelpers.js +213 -62
- package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
- package/lib/cjs/geometry3d/Segment1d.d.ts +5 -1
- package/lib/cjs/geometry3d/Segment1d.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Segment1d.js +11 -7
- package/lib/cjs/geometry3d/Segment1d.js.map +1 -1
- package/lib/cjs/geometry3d/Transform.d.ts +4 -5
- package/lib/cjs/geometry3d/Transform.d.ts.map +1 -1
- package/lib/cjs/geometry3d/Transform.js +4 -5
- package/lib/cjs/geometry3d/Transform.js.map +1 -1
- package/lib/cjs/geometry4d/Point4d.d.ts +13 -0
- package/lib/cjs/geometry4d/Point4d.d.ts.map +1 -1
- package/lib/cjs/geometry4d/Point4d.js +21 -0
- package/lib/cjs/geometry4d/Point4d.js.map +1 -1
- package/lib/cjs/numerics/BezierPolynomials.d.ts +88 -64
- package/lib/cjs/numerics/BezierPolynomials.d.ts.map +1 -1
- package/lib/cjs/numerics/BezierPolynomials.js +92 -73
- package/lib/cjs/numerics/BezierPolynomials.js.map +1 -1
- package/lib/cjs/numerics/Newton.d.ts +143 -61
- package/lib/cjs/numerics/Newton.d.ts.map +1 -1
- package/lib/cjs/numerics/Newton.js +233 -66
- package/lib/cjs/numerics/Newton.js.map +1 -1
- package/lib/cjs/numerics/PascalCoefficients.d.ts +8 -12
- package/lib/cjs/numerics/PascalCoefficients.d.ts.map +1 -1
- package/lib/cjs/numerics/PascalCoefficients.js +10 -12
- package/lib/cjs/numerics/PascalCoefficients.js.map +1 -1
- package/lib/cjs/numerics/Polynomials.d.ts +6 -10
- package/lib/cjs/numerics/Polynomials.d.ts.map +1 -1
- package/lib/cjs/numerics/Polynomials.js +6 -10
- package/lib/cjs/numerics/Polynomials.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts +3 -0
- package/lib/cjs/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceBuilder.js +13 -2
- package/lib/cjs/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceData.d.ts +1 -1
- package/lib/cjs/polyface/PolyfaceData.js +1 -1
- package/lib/cjs/polyface/PolyfaceData.js.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.d.ts +38 -11
- package/lib/cjs/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/cjs/polyface/PolyfaceQuery.js +93 -16
- package/lib/cjs/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/cjs/serialization/BGFBReader.d.ts +10 -10
- package/lib/cjs/serialization/BGFBReader.d.ts.map +1 -1
- package/lib/cjs/serialization/BGFBReader.js +69 -42
- package/lib/cjs/serialization/BGFBReader.js.map +1 -1
- package/lib/cjs/serialization/BGFBWriter.d.ts +8 -8
- package/lib/cjs/serialization/BGFBWriter.d.ts.map +1 -1
- package/lib/cjs/serialization/BGFBWriter.js +80 -55
- package/lib/cjs/serialization/BGFBWriter.js.map +1 -1
- package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
- package/lib/cjs/serialization/GeometrySamples.js +6 -8
- package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.d.ts +8 -14
- package/lib/cjs/serialization/IModelJsonSchema.d.ts.map +1 -1
- package/lib/cjs/serialization/IModelJsonSchema.js +85 -259
- package/lib/cjs/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/cjs/serialization/SerializationHelpers.d.ts +109 -0
- package/lib/cjs/serialization/SerializationHelpers.d.ts.map +1 -0
- package/lib/cjs/serialization/SerializationHelpers.js +591 -0
- package/lib/cjs/serialization/SerializationHelpers.js.map +1 -0
- package/lib/cjs/topology/Graph.d.ts +399 -366
- package/lib/cjs/topology/Graph.d.ts.map +1 -1
- package/lib/cjs/topology/Graph.js +531 -464
- package/lib/cjs/topology/Graph.js.map +1 -1
- package/lib/cjs/topology/MaskManager.d.ts +8 -9
- package/lib/cjs/topology/MaskManager.d.ts.map +1 -1
- package/lib/cjs/topology/MaskManager.js +11 -12
- package/lib/cjs/topology/MaskManager.js.map +1 -1
- package/lib/esm/Geometry.d.ts +14 -2
- package/lib/esm/Geometry.d.ts.map +1 -1
- package/lib/esm/Geometry.js +20 -9
- package/lib/esm/Geometry.js.map +1 -1
- package/lib/esm/bspline/BSpline1dNd.d.ts +12 -2
- package/lib/esm/bspline/BSpline1dNd.d.ts.map +1 -1
- package/lib/esm/bspline/BSpline1dNd.js +27 -17
- package/lib/esm/bspline/BSpline1dNd.js.map +1 -1
- package/lib/esm/bspline/BSplineCurve.d.ts +30 -16
- package/lib/esm/bspline/BSplineCurve.d.ts.map +1 -1
- package/lib/esm/bspline/BSplineCurve.js +68 -44
- package/lib/esm/bspline/BSplineCurve.js.map +1 -1
- package/lib/esm/bspline/BSplineCurve3dH.d.ts +27 -20
- package/lib/esm/bspline/BSplineCurve3dH.d.ts.map +1 -1
- package/lib/esm/bspline/BSplineCurve3dH.js +137 -88
- package/lib/esm/bspline/BSplineCurve3dH.js.map +1 -1
- package/lib/esm/bspline/BSplineSurface.d.ts +138 -74
- package/lib/esm/bspline/BSplineSurface.d.ts.map +1 -1
- package/lib/esm/bspline/BSplineSurface.js +242 -119
- package/lib/esm/bspline/BSplineSurface.js.map +1 -1
- package/lib/esm/bspline/Bezier1dNd.d.ts +18 -4
- package/lib/esm/bspline/Bezier1dNd.d.ts.map +1 -1
- package/lib/esm/bspline/Bezier1dNd.js +19 -5
- package/lib/esm/bspline/Bezier1dNd.js.map +1 -1
- package/lib/esm/bspline/BezierCurve3dH.d.ts.map +1 -1
- package/lib/esm/bspline/BezierCurve3dH.js +2 -1
- package/lib/esm/bspline/BezierCurve3dH.js.map +1 -1
- package/lib/esm/bspline/KnotVector.d.ts +30 -33
- package/lib/esm/bspline/KnotVector.d.ts.map +1 -1
- package/lib/esm/bspline/KnotVector.js +76 -69
- package/lib/esm/bspline/KnotVector.js.map +1 -1
- package/lib/esm/clipping/ClipUtils.d.ts +32 -5
- package/lib/esm/clipping/ClipUtils.d.ts.map +1 -1
- package/lib/esm/clipping/ClipUtils.js +92 -11
- package/lib/esm/clipping/ClipUtils.js.map +1 -1
- package/lib/esm/core-geometry.d.ts +0 -2
- package/lib/esm/core-geometry.d.ts.map +1 -1
- package/lib/esm/core-geometry.js +0 -2
- package/lib/esm/core-geometry.js.map +1 -1
- package/lib/esm/curve/Arc3d.d.ts.map +1 -1
- package/lib/esm/curve/Arc3d.js +5 -5
- package/lib/esm/curve/Arc3d.js.map +1 -1
- package/lib/esm/curve/CurveLocationDetail.d.ts +1 -4
- package/lib/esm/curve/CurveLocationDetail.d.ts.map +1 -1
- package/lib/esm/curve/CurveLocationDetail.js +4 -10
- package/lib/esm/curve/CurveLocationDetail.js.map +1 -1
- package/lib/esm/curve/CurveOps.d.ts +4 -5
- package/lib/esm/curve/CurveOps.d.ts.map +1 -1
- package/lib/esm/curve/CurveOps.js +1 -1
- package/lib/esm/curve/CurveOps.js.map +1 -1
- package/lib/esm/curve/CurveTypes.d.ts +14 -2
- package/lib/esm/curve/CurveTypes.d.ts.map +1 -1
- package/lib/esm/curve/CurveTypes.js.map +1 -1
- package/lib/esm/curve/LineString3d.d.ts +3 -2
- package/lib/esm/curve/LineString3d.d.ts.map +1 -1
- package/lib/esm/curve/LineString3d.js +1 -0
- package/lib/esm/curve/LineString3d.js.map +1 -1
- package/lib/esm/curve/PointString3d.d.ts +2 -1
- package/lib/esm/curve/PointString3d.d.ts.map +1 -1
- package/lib/esm/curve/PointString3d.js.map +1 -1
- package/lib/esm/curve/RegionOps.d.ts +5 -11
- package/lib/esm/curve/RegionOps.d.ts.map +1 -1
- package/lib/esm/curve/RegionOps.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts +15 -9
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js +69 -57
- package/lib/esm/curve/internalContexts/CurveCurveCloseApproachXY.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.d.ts +14 -16
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js +51 -68
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXY.js.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.js +3 -2
- package/lib/esm/curve/internalContexts/CurveCurveIntersectXYZ.js.map +1 -1
- package/lib/esm/curve/internalContexts/MultiChainCollector.d.ts +2 -3
- package/lib/esm/curve/internalContexts/MultiChainCollector.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/MultiChainCollector.js.map +1 -1
- package/lib/esm/curve/internalContexts/PolygonOffsetContext.d.ts +4 -4
- package/lib/esm/curve/internalContexts/PolygonOffsetContext.d.ts.map +1 -1
- package/lib/esm/curve/internalContexts/PolygonOffsetContext.js +38 -36
- package/lib/esm/curve/internalContexts/PolygonOffsetContext.js.map +1 -1
- package/lib/esm/geometry3d/Angle.d.ts +0 -1
- package/lib/esm/geometry3d/Angle.d.ts.map +1 -1
- package/lib/esm/geometry3d/Angle.js +0 -1
- package/lib/esm/geometry3d/Angle.js.map +1 -1
- package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts +21 -5
- package/lib/esm/geometry3d/CoincidentGeometryOps.d.ts.map +1 -1
- package/lib/esm/geometry3d/CoincidentGeometryOps.js +71 -45
- package/lib/esm/geometry3d/CoincidentGeometryOps.js.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.d.ts +20 -13
- package/lib/esm/geometry3d/Matrix3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Matrix3d.js +28 -21
- package/lib/esm/geometry3d/Matrix3d.js.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.d.ts +11 -2
- package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Point3dVector3d.js +16 -5
- package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.d.ts +58 -20
- package/lib/esm/geometry3d/PointHelpers.d.ts.map +1 -1
- package/lib/esm/geometry3d/PointHelpers.js +213 -62
- package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
- package/lib/esm/geometry3d/Segment1d.d.ts +5 -1
- package/lib/esm/geometry3d/Segment1d.d.ts.map +1 -1
- package/lib/esm/geometry3d/Segment1d.js +11 -7
- package/lib/esm/geometry3d/Segment1d.js.map +1 -1
- package/lib/esm/geometry3d/Transform.d.ts +4 -5
- package/lib/esm/geometry3d/Transform.d.ts.map +1 -1
- package/lib/esm/geometry3d/Transform.js +4 -5
- package/lib/esm/geometry3d/Transform.js.map +1 -1
- package/lib/esm/geometry4d/Point4d.d.ts +13 -0
- package/lib/esm/geometry4d/Point4d.d.ts.map +1 -1
- package/lib/esm/geometry4d/Point4d.js +21 -0
- package/lib/esm/geometry4d/Point4d.js.map +1 -1
- package/lib/esm/numerics/BezierPolynomials.d.ts +88 -64
- package/lib/esm/numerics/BezierPolynomials.d.ts.map +1 -1
- package/lib/esm/numerics/BezierPolynomials.js +92 -73
- package/lib/esm/numerics/BezierPolynomials.js.map +1 -1
- package/lib/esm/numerics/Newton.d.ts +143 -61
- package/lib/esm/numerics/Newton.d.ts.map +1 -1
- package/lib/esm/numerics/Newton.js +229 -65
- package/lib/esm/numerics/Newton.js.map +1 -1
- package/lib/esm/numerics/PascalCoefficients.d.ts +8 -12
- package/lib/esm/numerics/PascalCoefficients.d.ts.map +1 -1
- package/lib/esm/numerics/PascalCoefficients.js +10 -12
- package/lib/esm/numerics/PascalCoefficients.js.map +1 -1
- package/lib/esm/numerics/Polynomials.d.ts +6 -10
- package/lib/esm/numerics/Polynomials.d.ts.map +1 -1
- package/lib/esm/numerics/Polynomials.js +6 -10
- package/lib/esm/numerics/Polynomials.js.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.d.ts +3 -0
- package/lib/esm/polyface/PolyfaceBuilder.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceBuilder.js +13 -2
- package/lib/esm/polyface/PolyfaceBuilder.js.map +1 -1
- package/lib/esm/polyface/PolyfaceData.d.ts +1 -1
- package/lib/esm/polyface/PolyfaceData.js +1 -1
- package/lib/esm/polyface/PolyfaceData.js.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.d.ts +38 -11
- package/lib/esm/polyface/PolyfaceQuery.d.ts.map +1 -1
- package/lib/esm/polyface/PolyfaceQuery.js +93 -16
- package/lib/esm/polyface/PolyfaceQuery.js.map +1 -1
- package/lib/esm/serialization/BGFBReader.d.ts +10 -10
- package/lib/esm/serialization/BGFBReader.d.ts.map +1 -1
- package/lib/esm/serialization/BGFBReader.js +69 -42
- package/lib/esm/serialization/BGFBReader.js.map +1 -1
- package/lib/esm/serialization/BGFBWriter.d.ts +8 -8
- package/lib/esm/serialization/BGFBWriter.d.ts.map +1 -1
- package/lib/esm/serialization/BGFBWriter.js +80 -55
- package/lib/esm/serialization/BGFBWriter.js.map +1 -1
- package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
- package/lib/esm/serialization/GeometrySamples.js +7 -9
- package/lib/esm/serialization/GeometrySamples.js.map +1 -1
- package/lib/esm/serialization/IModelJsonSchema.d.ts +8 -14
- package/lib/esm/serialization/IModelJsonSchema.d.ts.map +1 -1
- package/lib/esm/serialization/IModelJsonSchema.js +86 -260
- package/lib/esm/serialization/IModelJsonSchema.js.map +1 -1
- package/lib/esm/serialization/SerializationHelpers.d.ts +109 -0
- package/lib/esm/serialization/SerializationHelpers.d.ts.map +1 -0
- package/lib/esm/serialization/SerializationHelpers.js +588 -0
- package/lib/esm/serialization/SerializationHelpers.js.map +1 -0
- package/lib/esm/topology/Graph.d.ts +399 -366
- package/lib/esm/topology/Graph.d.ts.map +1 -1
- package/lib/esm/topology/Graph.js +531 -464
- package/lib/esm/topology/Graph.js.map +1 -1
- package/lib/esm/topology/MaskManager.d.ts +8 -9
- package/lib/esm/topology/MaskManager.d.ts.map +1 -1
- package/lib/esm/topology/MaskManager.js +11 -12
- package/lib/esm/topology/MaskManager.js.map +1 -1
- package/package.json +3 -4
|
@@ -12,18 +12,16 @@ import { Point2d, Vector2d } from "../geometry3d/Point2dVector2d";
|
|
|
12
12
|
import { Point3d, Vector3d } from "../geometry3d/Point3dVector3d";
|
|
13
13
|
import { SmallSystem } from "../numerics/Polynomials";
|
|
14
14
|
import { MaskManager } from "./MaskManager";
|
|
15
|
-
// import { GraphChecker } from "../test/topology/Graph.test";
|
|
15
|
+
// import { GraphChecker } from "../test/topology/Graph.test"; // used for debugging
|
|
16
16
|
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
17
|
-
// cspell:word CONSTU
|
|
18
|
-
// cspell:word CONSTV
|
|
19
|
-
// cspell:word USEAM
|
|
20
|
-
// cspell:word VSEAM
|
|
17
|
+
// cspell:word CONSTU CONSTV USEAM VSEAM internaldocs
|
|
21
18
|
/**
|
|
22
19
|
* * Each node of the graph has a mask member.
|
|
23
20
|
* * The mask member is a number which is used as set of single bit boolean values.
|
|
24
21
|
* * Particular meanings of the various bits are HIGHLY application dependent.
|
|
25
22
|
* * The EXTERIOR mask bit is widely used to mark nodes that are "outside" the active areas
|
|
26
|
-
* * The PRIMARY_EDGE bit is widely used to indicate linework created directly from input data, hence protected from
|
|
23
|
+
* * The PRIMARY_EDGE bit is widely used to indicate linework created directly from input data, hence protected from
|
|
24
|
+
* triangle edge flipping.
|
|
27
25
|
* * The BOUNDARY bit is widely used to indicate that crossing this edge is a transition from outside to inside.
|
|
28
26
|
* * VISITED is used locally in many searches.
|
|
29
27
|
* * Never use VISITED unless the search logic is highly self contained.
|
|
@@ -31,17 +29,6 @@ import { MaskManager } from "./MaskManager";
|
|
|
31
29
|
*/
|
|
32
30
|
export var HalfEdgeMask;
|
|
33
31
|
(function (HalfEdgeMask) {
|
|
34
|
-
/** Mask commonly set consistently around exterior faces.
|
|
35
|
-
* * A boundary edge with interior to one side, exterior to the other will have EXTERIOR only on the outside.
|
|
36
|
-
* * An an edge inserted "within a purely exterior face" can have EXTERIOR on both sides.
|
|
37
|
-
* * An interior edge (such as added during triangulation) will have no EXTERIOR bits.
|
|
38
|
-
*/
|
|
39
|
-
HalfEdgeMask[HalfEdgeMask["EXTERIOR"] = 1] = "EXTERIOR";
|
|
40
|
-
/** Mask commonly set (on both sides) of original geometry edges that are transition from outside from to inside.
|
|
41
|
-
* * At the moment of creating an edge from primary user boundary loop coordinates, the fact that an edge is BOUNDARY is often clear even though
|
|
42
|
-
* there is uncertainty about which side should be EXTERIOR.
|
|
43
|
-
*/
|
|
44
|
-
HalfEdgeMask[HalfEdgeMask["BOUNDARY_EDGE"] = 2] = "BOUNDARY_EDGE";
|
|
45
32
|
// REMARK: Various mask names are COMMENTED here for reference to native legacy code.
|
|
46
33
|
// CONSTU_MASK = 0x00000004,
|
|
47
34
|
// CONSTV_MASK = 0x00000008,
|
|
@@ -50,21 +37,37 @@ export var HalfEdgeMask;
|
|
|
50
37
|
// BOUNDARY_VERTEX_MASK = 0x00000040,
|
|
51
38
|
// PRIMARY_VERTEX_MASK = 0x00000080,
|
|
52
39
|
// DIRECTED_EDGE_MASK = 0x00000100,
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
40
|
+
/**
|
|
41
|
+
* Mask commonly set consistently around exterior faces.
|
|
42
|
+
* * A boundary edge with interior to one side, exterior to the other, will have EXTERIOR only on the outside.
|
|
43
|
+
* * An an edge inserted "within a purely exterior face" can have EXTERIOR on both sides.
|
|
44
|
+
* * An interior edge (such as added during triangulation) will have no EXTERIOR bits.
|
|
45
|
+
*/
|
|
46
|
+
HalfEdgeMask[HalfEdgeMask["EXTERIOR"] = 1] = "EXTERIOR";
|
|
47
|
+
/**
|
|
48
|
+
* Mask commonly set (on both sides) of original geometry edges that are transition from outside from to inside.
|
|
49
|
+
* * At the moment of creating an edge from primary user boundary loop coordinates, the fact that an edge is BOUNDARY
|
|
50
|
+
* is often clear even though there is uncertainty about which side should be EXTERIOR.
|
|
51
|
+
*/
|
|
52
|
+
HalfEdgeMask[HalfEdgeMask["BOUNDARY_EDGE"] = 2] = "BOUNDARY_EDGE";
|
|
53
|
+
/**
|
|
54
|
+
* Mask commonly set (on both sides) of original geometry edges, but NOT indicating that the edge is certainly a
|
|
55
|
+
* boundary between outside and inside.
|
|
56
|
+
* * For instance, if geometry is provided as stray sticks (not loops), it can be marked PRIMARY_EDGE but neither
|
|
57
|
+
* BOUNDARY_EDGE nor EXTERIOR_EDGE.
|
|
55
58
|
*/
|
|
56
59
|
HalfEdgeMask[HalfEdgeMask["PRIMARY_EDGE"] = 4] = "PRIMARY_EDGE";
|
|
57
|
-
/** Mask used for low level searches to identify previously-visited nodes */
|
|
60
|
+
/** Mask used for low level searches to identify previously-visited nodes. */
|
|
58
61
|
HalfEdgeMask[HalfEdgeMask["VISITED"] = 16] = "VISITED";
|
|
59
|
-
/** Mask applied to triangles by earcut triangulator */
|
|
62
|
+
/** Mask applied to triangles by earcut triangulator. */
|
|
60
63
|
HalfEdgeMask[HalfEdgeMask["TRIANGULATED_FACE"] = 256] = "TRIANGULATED_FACE";
|
|
61
|
-
/**
|
|
64
|
+
/** Mask applied in a face with 2 edges. */
|
|
62
65
|
HalfEdgeMask[HalfEdgeMask["NULL_FACE"] = 512] = "NULL_FACE";
|
|
63
|
-
/**
|
|
66
|
+
/** No mask bits. */
|
|
64
67
|
HalfEdgeMask[HalfEdgeMask["NULL_MASK"] = 0] = "NULL_MASK";
|
|
65
|
-
/** The "upper 12
|
|
68
|
+
/** The "upper 12" bits of 32 bit integer reserved for grab/drop. */
|
|
66
69
|
HalfEdgeMask[HalfEdgeMask["ALL_GRAB_DROP_MASKS"] = 4293918720] = "ALL_GRAB_DROP_MASKS";
|
|
67
|
-
/**
|
|
70
|
+
/** All mask bits */
|
|
68
71
|
HalfEdgeMask[HalfEdgeMask["ALL_MASK"] = 4294967295] = "ALL_MASK";
|
|
69
72
|
// informal convention on preassigned mask bit numbers:
|
|
70
73
|
// byte0 (EXTERIOR, BOUNDARY_EDGE, PRIMARY_EDGE) -- edge properties
|
|
@@ -72,35 +75,61 @@ export var HalfEdgeMask;
|
|
|
72
75
|
// byte2 (TRIANGULATED_FACE, NULL_FACE) -- face properties.
|
|
73
76
|
})(HalfEdgeMask || (HalfEdgeMask = {}));
|
|
74
77
|
/**
|
|
78
|
+
* A HalfEdge is "one side of an edge" in a structure of faces, edges and vertices. From a node there are
|
|
79
|
+
* navigational links to:
|
|
80
|
+
* * "faceSuccessor" -- the next half edge in a loop around a face.
|
|
81
|
+
* * "facePredecessor" -- the previous half edge in a loop around a face.
|
|
82
|
+
* * "edgeMate" -- the node's partner on the other side of the edge.
|
|
83
|
+
*
|
|
84
|
+
* The next, prev, and mate are the essential connectivity. Additional node content is for application-specific
|
|
85
|
+
* uses. The most useful ones are:
|
|
86
|
+
* * x,y -- coordinates in the xy plane
|
|
87
|
+
* * z -- z coordinate. This is normally ignored during planar setup, but used for output.
|
|
88
|
+
* * maskBits -- an integer value manipulated as individual bits.
|
|
75
89
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* ** "edgeMate" -- the node's partner on the other side of the edge.
|
|
80
|
-
* * The next, prev, and mate are the essential connectivity. Additional node content is for application-specific
|
|
81
|
-
* uses. The most useful ones are:
|
|
82
|
-
* ** x,y -- coordinates in the xy plane
|
|
83
|
-
* ** z -- z coordinate. This is normally ignored during planar setup, but used for output.
|
|
84
|
-
* ** buffer -- a integer value manipulated as individual bits.
|
|
85
|
-
* * In properly connected planar graph, interior face loops are counterclockwise. But that property (along with
|
|
86
|
-
* expected masking) is a result of extensive validation of inputs, and is not true in intermediate phases
|
|
87
|
-
* of graph manipulation.
|
|
90
|
+
* In properly connected planar graph, interior face loops are counterclockwise. But that property (along with
|
|
91
|
+
* expected masking) is a result of extensive validation of inputs, and is not true in intermediate phases of
|
|
92
|
+
* graph manipulation.
|
|
88
93
|
* @internal
|
|
89
94
|
*/
|
|
90
95
|
class HalfEdge {
|
|
91
|
-
/**
|
|
92
|
-
get id() {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
get
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
/** Immutable ID assigned sequentially during construction --- useful for debugging. */
|
|
97
|
+
get id() {
|
|
98
|
+
return this._id;
|
|
99
|
+
}
|
|
100
|
+
/** Previous half edge "around the face" */
|
|
101
|
+
get facePredecessor() {
|
|
102
|
+
return this._facePredecessor;
|
|
103
|
+
}
|
|
104
|
+
/** Next half edge "around the face" */
|
|
105
|
+
get faceSuccessor() {
|
|
106
|
+
return this._faceSuccessor;
|
|
107
|
+
}
|
|
108
|
+
/** Half edge on the other side of this edge. */
|
|
109
|
+
get edgeMate() {
|
|
110
|
+
return this._edgeMate;
|
|
111
|
+
}
|
|
112
|
+
constructor(x = 0, y = 0, z = 0, i = 0) {
|
|
113
|
+
this._id = HalfEdge._totalNodesCreated++;
|
|
114
|
+
this.i = i;
|
|
115
|
+
this.maskBits = 0x00000000;
|
|
116
|
+
this.x = x;
|
|
117
|
+
this.y = y;
|
|
118
|
+
this.z = z;
|
|
119
|
+
// explicit init to undefined is important for performance here
|
|
120
|
+
this.sortAngle = undefined;
|
|
121
|
+
this.sortData = undefined;
|
|
122
|
+
this.edgeTag = undefined;
|
|
123
|
+
this.faceTag = undefined;
|
|
124
|
+
// always created in pairs, init here to make TS compiler and JS runtime happy
|
|
125
|
+
this._facePredecessor = this;
|
|
126
|
+
this._faceSuccessor = this;
|
|
127
|
+
this._edgeMate = this;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Take numStep face steps and return y coordinate.
|
|
131
|
+
* * Positive steps are through faceSuccessor.
|
|
132
|
+
* * Negative steps are through facePredecessor.
|
|
104
133
|
*/
|
|
105
134
|
faceStepY(numStep) {
|
|
106
135
|
let node = this;
|
|
@@ -113,12 +142,13 @@ class HalfEdge {
|
|
|
113
142
|
return node.y;
|
|
114
143
|
}
|
|
115
144
|
/**
|
|
116
|
-
*
|
|
145
|
+
* Create 2 half edges.
|
|
117
146
|
* * The two edges are joined as edgeMate pair.
|
|
118
147
|
* * The two edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
119
|
-
* @returns
|
|
148
|
+
* @returns the reference to the first half edge created.
|
|
120
149
|
*/
|
|
121
150
|
static createHalfEdgePair(heArray) {
|
|
151
|
+
// Visualization can be found at geometry/internaldocs/Graph.md
|
|
122
152
|
const a = new HalfEdge();
|
|
123
153
|
const b = new HalfEdge();
|
|
124
154
|
if (heArray) {
|
|
@@ -134,8 +164,8 @@ class HalfEdge {
|
|
|
134
164
|
* * Create 2 half edges.
|
|
135
165
|
* * The two edges are joined as edgeMate pair.
|
|
136
166
|
* * The two edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
137
|
-
* * Properties x,y,z,i are inserted in each
|
|
138
|
-
* @returns
|
|
167
|
+
* * Properties x,y,z,i are inserted in each half edge.
|
|
168
|
+
* @returns the reference to the first half edge created.
|
|
139
169
|
*/
|
|
140
170
|
static createHalfEdgePairWithCoordinates(xA = 0, yA = 0, zA = 0, iA = 0, xB = 0, yB = 0, zB = 0, iB = 0, heArray) {
|
|
141
171
|
const a = HalfEdge.createHalfEdgePair(heArray);
|
|
@@ -151,32 +181,32 @@ class HalfEdge {
|
|
|
151
181
|
return a;
|
|
152
182
|
}
|
|
153
183
|
/**
|
|
154
|
-
* *
|
|
184
|
+
* * Set heA <==> heB pointer relation through heA._faceSuccessor and heB._facePredecessor.
|
|
155
185
|
* * This changes heA._faceSuccessor and heB._facePredecessor, but not heA._facePredecessor and heB._faceSuccessor.
|
|
156
|
-
* *
|
|
186
|
+
* * This must always be done with another call to setFaceLinks(heB,heA) in order to re-establish the entire
|
|
187
|
+
* double-linked list.
|
|
157
188
|
*/
|
|
158
189
|
static setFaceLinks(heA, heB) {
|
|
159
190
|
heA._faceSuccessor = heB;
|
|
160
191
|
heB._facePredecessor = heA;
|
|
161
192
|
}
|
|
162
|
-
/**
|
|
163
|
-
* * set heA <==> heB pointer relation edgeMate
|
|
164
|
-
*/
|
|
193
|
+
/** set heA <==> heB pointer relation edgeMate. */
|
|
165
194
|
static setEdgeMates(heA, heB) {
|
|
166
195
|
heA._edgeMate = heB;
|
|
167
196
|
heB._edgeMate = heA;
|
|
168
197
|
}
|
|
169
198
|
/**
|
|
170
|
-
*
|
|
171
|
-
* *
|
|
172
|
-
* *
|
|
173
|
-
* *
|
|
174
|
-
* *
|
|
175
|
-
*
|
|
176
|
-
* *
|
|
177
|
-
* @returns
|
|
199
|
+
* Create a new vertex within the edge beginning at `baseA`.
|
|
200
|
+
* * This creates two new nodes in their own vertex loop.
|
|
201
|
+
* * If the base is undefined, create a single-edge loop.
|
|
202
|
+
* * Existing nodes stay in their face and vertex loops and retain xyz and i values.
|
|
203
|
+
* * Unlike [[pinch]], this breaks the edgeMate pairing of the input edge:
|
|
204
|
+
* each node of the input edge gets a new node as its edge mate.
|
|
205
|
+
* * On each side of the edge, if edgeTag is present, it is copied to the new node on that side.
|
|
206
|
+
* @returns reference to the half edge created, the new face successor of `baseA`.
|
|
178
207
|
*/
|
|
179
208
|
static splitEdge(baseA, xA = 0, yA = 0, zA = 0, iA = 0, heArray) {
|
|
209
|
+
// Visualization can be found at geometry/internaldocs/Graph.md
|
|
180
210
|
const newA = new HalfEdge(xA, yA, zA, iA);
|
|
181
211
|
const newB = new HalfEdge(xA, yA, zA, iA);
|
|
182
212
|
if (heArray) {
|
|
@@ -192,10 +222,12 @@ class HalfEdge {
|
|
|
192
222
|
const nextA = baseA._faceSuccessor;
|
|
193
223
|
const mateA = baseA._edgeMate;
|
|
194
224
|
const vPredA = mateA._faceSuccessor;
|
|
225
|
+
// insert newA and newB between existing half edges
|
|
195
226
|
HalfEdge.setFaceLinks(newA, nextA);
|
|
196
227
|
HalfEdge.setFaceLinks(baseA, newA);
|
|
197
228
|
HalfEdge.setFaceLinks(mateA, newB);
|
|
198
229
|
HalfEdge.setFaceLinks(newB, vPredA);
|
|
230
|
+
// set correct edge mates
|
|
199
231
|
HalfEdge.setEdgeMates(newA, mateA);
|
|
200
232
|
HalfEdge.setEdgeMates(newB, baseA);
|
|
201
233
|
this.transferEdgeProperties(baseA, newA);
|
|
@@ -204,29 +236,26 @@ class HalfEdge {
|
|
|
204
236
|
return newA;
|
|
205
237
|
}
|
|
206
238
|
/**
|
|
207
|
-
*
|
|
208
|
-
* *
|
|
209
|
-
* *
|
|
210
|
-
* *
|
|
211
|
-
*
|
|
212
|
-
* *
|
|
213
|
-
* *
|
|
214
|
-
*
|
|
215
|
-
* @returns Returns the reference to the half edge created.
|
|
239
|
+
* Create a new sliver face "inside" an existing edge.
|
|
240
|
+
* * This creates two nodes that are each face predecessor and successor to the other.
|
|
241
|
+
* * Existing nodes stay in their face and vertex loops and retain xyz and i values.
|
|
242
|
+
* * Unlike [[pinch]], this breaks the edgeMate pairing of the input edge:
|
|
243
|
+
* each node of the input edge gets a new node as its edge mate.
|
|
244
|
+
* * New nodes get the xyz and i values shared by the nodes in the vertex loops into which they are placed.
|
|
245
|
+
* * New nodes' faceTag and edgeTag are `undefined`.
|
|
246
|
+
* @returns reference to the half edge created in the vertex loop of baseA.
|
|
216
247
|
*/
|
|
217
248
|
static splitEdgeCreateSliverFace(baseA, heArray) {
|
|
218
|
-
//
|
|
249
|
+
// Visualization can be found at geometry/internaldocs/Graph.md
|
|
250
|
+
const baseB = baseA.edgeMate;
|
|
219
251
|
const newA = new HalfEdge();
|
|
220
252
|
const newB = new HalfEdge();
|
|
221
|
-
const baseB = baseA.edgeMate;
|
|
222
253
|
if (heArray) {
|
|
223
254
|
heArray.push(newA);
|
|
224
255
|
heArray.push(newB);
|
|
225
256
|
}
|
|
226
257
|
newA._faceSuccessor = newA._facePredecessor = newB;
|
|
227
258
|
newB._faceSuccessor = newB._facePredecessor = newA;
|
|
228
|
-
// newA is in vertex loop with baseA etc.
|
|
229
|
-
// newA mates to baseB
|
|
230
259
|
HalfEdge.setEdgeMates(newA, baseB);
|
|
231
260
|
HalfEdge.setEdgeMates(newB, baseA);
|
|
232
261
|
newA.copyDataFrom(baseA, true, true, false, false);
|
|
@@ -234,11 +263,9 @@ class HalfEdge {
|
|
|
234
263
|
return newA;
|
|
235
264
|
}
|
|
236
265
|
/**
|
|
237
|
-
* Copy "edge based" content of fromNode to toNode
|
|
266
|
+
* Copy "edge based" content of `fromNode` to `toNode`:
|
|
238
267
|
* * edgeTag
|
|
239
|
-
* * masks
|
|
240
|
-
* @param fromNode
|
|
241
|
-
* @param toNode
|
|
268
|
+
* * masks EXTERIOR, BOUNDARY_EDGE, NULL_FACE, PRIMARY_EDGE
|
|
242
269
|
*/
|
|
243
270
|
static transferEdgeProperties(fromNode, toNode) {
|
|
244
271
|
toNode.edgeTag = fromNode.edgeTag;
|
|
@@ -249,49 +276,38 @@ class HalfEdge {
|
|
|
249
276
|
toNode.clearMask(mask);
|
|
250
277
|
}
|
|
251
278
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
this.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.
|
|
259
|
-
// Explicit init to undefined is important for performance here
|
|
260
|
-
this.sortAngle = undefined;
|
|
261
|
-
this.sortData = undefined;
|
|
262
|
-
this.edgeTag = undefined;
|
|
263
|
-
this.faceTag = undefined;
|
|
264
|
-
// Always created in pairs, init here to make TS compiler and JS runtime happy
|
|
265
|
-
this._facePredecessor = this;
|
|
266
|
-
this._faceSuccessor = this;
|
|
267
|
-
this._edgeMate = this;
|
|
279
|
+
/** Return the next half edge around this vertex in the CCW direction. */
|
|
280
|
+
get vertexSuccessor() {
|
|
281
|
+
return this.facePredecessor.edgeMate;
|
|
282
|
+
}
|
|
283
|
+
/** Return the next half edge around this vertex in the CW direction. */
|
|
284
|
+
get vertexPredecessor() {
|
|
285
|
+
return this.edgeMate.faceSuccessor;
|
|
268
286
|
}
|
|
269
287
|
/**
|
|
270
|
-
*
|
|
271
|
-
|
|
272
|
-
get vertexSuccessor() { return this.facePredecessor.edgeMate; }
|
|
273
|
-
/**
|
|
274
|
-
* Return the next outbound half edge around this vertex in the CW direction
|
|
275
|
-
*/
|
|
276
|
-
get vertexPredecessor() { return this.edgeMate.faceSuccessor; }
|
|
277
|
-
/**
|
|
278
|
-
* Set mask bits on this HalfEdge
|
|
279
|
-
* @param mask mask to apply
|
|
288
|
+
* Set mask bits on this HalfEdge.
|
|
289
|
+
* @param mask mask bits to apply
|
|
280
290
|
*/
|
|
281
|
-
setMask(mask) {
|
|
291
|
+
setMask(mask) {
|
|
292
|
+
this.maskBits |= mask;
|
|
293
|
+
}
|
|
282
294
|
/**
|
|
283
|
-
* Get mask bits from this HalfEdge
|
|
284
|
-
* @param mask mask to query
|
|
295
|
+
* Get mask bits from this HalfEdge.
|
|
296
|
+
* @param mask mask bits to query
|
|
285
297
|
*/
|
|
286
|
-
getMask(mask) {
|
|
298
|
+
getMask(mask) {
|
|
299
|
+
return (this.maskBits & mask);
|
|
300
|
+
}
|
|
287
301
|
/**
|
|
288
|
-
* Clear mask bits from this HalfEdge
|
|
289
|
-
* @param mask mask to clear
|
|
302
|
+
* Clear mask bits from this HalfEdge.
|
|
303
|
+
* @param mask mask bits to clear
|
|
290
304
|
*/
|
|
291
|
-
clearMask(mask) {
|
|
305
|
+
clearMask(mask) {
|
|
306
|
+
this.maskBits &= ~mask;
|
|
307
|
+
}
|
|
292
308
|
/**
|
|
293
309
|
* Set a mask at all nodes around a vertex.
|
|
294
|
-
* @param mask mask to apply to the half edges around this HalfEdge's vertex loop
|
|
310
|
+
* @param mask mask to apply to the half edges around this HalfEdge's vertex loop.
|
|
295
311
|
*/
|
|
296
312
|
setMaskAroundVertex(mask) {
|
|
297
313
|
let node = this;
|
|
@@ -300,10 +316,7 @@ class HalfEdge {
|
|
|
300
316
|
node = node.vertexSuccessor;
|
|
301
317
|
} while (node !== this);
|
|
302
318
|
}
|
|
303
|
-
/**
|
|
304
|
-
* Set x,y,z at all nodes around a vertex.
|
|
305
|
-
* @param mask mask to apply to the half edges around this HalfEdge's vertex loop
|
|
306
|
-
*/
|
|
319
|
+
/** Set x,y,z at all nodes around a vertex. */
|
|
307
320
|
setXYZAroundVertex(x, y, z) {
|
|
308
321
|
let node = this;
|
|
309
322
|
do {
|
|
@@ -315,7 +328,7 @@ class HalfEdge {
|
|
|
315
328
|
}
|
|
316
329
|
/**
|
|
317
330
|
* Apply a mask to all edges around a face.
|
|
318
|
-
* @param mask mask to apply to the half edges around this HalfEdge's face loop
|
|
331
|
+
* @param mask mask to apply to the half edges around this HalfEdge's face loop.
|
|
319
332
|
*/
|
|
320
333
|
setMaskAroundFace(mask) {
|
|
321
334
|
let node = this;
|
|
@@ -326,7 +339,7 @@ class HalfEdge {
|
|
|
326
339
|
}
|
|
327
340
|
/**
|
|
328
341
|
* Apply a mask to both sides of an edge.
|
|
329
|
-
* @param mask mask to apply to this edge and its
|
|
342
|
+
* @param mask mask to apply to this edge and its edgeMate.
|
|
330
343
|
*/
|
|
331
344
|
setMaskAroundEdge(mask) {
|
|
332
345
|
this.setMask(mask);
|
|
@@ -334,7 +347,7 @@ class HalfEdge {
|
|
|
334
347
|
}
|
|
335
348
|
/**
|
|
336
349
|
* Clear a mask on both sides of an edge.
|
|
337
|
-
* @param mask mask to clear on this edge and its
|
|
350
|
+
* @param mask mask to clear on this edge and its edgeMate.
|
|
338
351
|
*/
|
|
339
352
|
clearMaskAroundEdge(mask) {
|
|
340
353
|
this.clearMask(mask);
|
|
@@ -350,7 +363,7 @@ class HalfEdge {
|
|
|
350
363
|
} while (node !== this);
|
|
351
364
|
return count;
|
|
352
365
|
}
|
|
353
|
-
/** Return true if other is in the vertex loop around this. */
|
|
366
|
+
/** Return true if `other` node is in the vertex loop around `this` node. */
|
|
354
367
|
findAroundVertex(other) {
|
|
355
368
|
let node = this;
|
|
356
369
|
do {
|
|
@@ -360,7 +373,7 @@ class HalfEdge {
|
|
|
360
373
|
} while (node !== this);
|
|
361
374
|
return false;
|
|
362
375
|
}
|
|
363
|
-
/** Return true if other is in the face loop around this. */
|
|
376
|
+
/** Return true if `other` node is in the face loop around `this` node. */
|
|
364
377
|
findAroundFace(other) {
|
|
365
378
|
let node = this;
|
|
366
379
|
do {
|
|
@@ -371,7 +384,9 @@ class HalfEdge {
|
|
|
371
384
|
return false;
|
|
372
385
|
}
|
|
373
386
|
/**
|
|
374
|
-
*
|
|
387
|
+
* Returns whether the mask is set or unset on all nodes of the face loop.
|
|
388
|
+
* @param mask the mask to check.
|
|
389
|
+
* @param value true for mask set and false for mask unset.
|
|
375
390
|
*/
|
|
376
391
|
isMaskedAroundFace(mask, value = true) {
|
|
377
392
|
let node = this;
|
|
@@ -392,10 +407,10 @@ class HalfEdge {
|
|
|
392
407
|
return true;
|
|
393
408
|
}
|
|
394
409
|
/**
|
|
395
|
-
* Apply a
|
|
396
|
-
*
|
|
397
|
-
* @param edgeTag
|
|
398
|
-
* @param
|
|
410
|
+
* Apply a mask and edgeTag to all edges around a face. Optionally apply it to all edge mates.
|
|
411
|
+
* @param mask mask to apply.
|
|
412
|
+
* @param tag edgeTag to apply
|
|
413
|
+
* @param applyToMate If true, also apply the tag to the edge mates around the face.
|
|
399
414
|
*/
|
|
400
415
|
setMaskAndEdgeTagAroundFace(mask, tag, applyToMate = false) {
|
|
401
416
|
let node = this;
|
|
@@ -404,8 +419,8 @@ class HalfEdge {
|
|
|
404
419
|
node.edgeTag = tag;
|
|
405
420
|
if (applyToMate) {
|
|
406
421
|
const mate = node.edgeMate;
|
|
407
|
-
mate.edgeTag = tag;
|
|
408
422
|
mate.setMask(mask);
|
|
423
|
+
mate.edgeTag = tag;
|
|
409
424
|
}
|
|
410
425
|
node = node.faceSuccessor;
|
|
411
426
|
} while (node !== this);
|
|
@@ -420,7 +435,11 @@ class HalfEdge {
|
|
|
420
435
|
} while (node !== this);
|
|
421
436
|
return count;
|
|
422
437
|
}
|
|
423
|
-
/**
|
|
438
|
+
/**
|
|
439
|
+
* Returns the number of nodes that match (or do not match) the given mask value around this face loop.
|
|
440
|
+
* @param mask the mask to check.
|
|
441
|
+
* @param value true for mask match and false for mask not match.
|
|
442
|
+
*/
|
|
424
443
|
countMaskAroundFace(mask, value = true) {
|
|
425
444
|
let count = 0;
|
|
426
445
|
let node = this;
|
|
@@ -440,7 +459,11 @@ class HalfEdge {
|
|
|
440
459
|
}
|
|
441
460
|
return count;
|
|
442
461
|
}
|
|
443
|
-
/**
|
|
462
|
+
/**
|
|
463
|
+
* Returns the number of nodes that match (or do not match) the given mask value around this vertex loop.
|
|
464
|
+
* @param mask the mask to check.
|
|
465
|
+
* @param value true for mask match and false for mask not match.
|
|
466
|
+
*/
|
|
444
467
|
countMaskAroundVertex(mask, value = true) {
|
|
445
468
|
let count = 0;
|
|
446
469
|
let node = this;
|
|
@@ -460,7 +483,12 @@ class HalfEdge {
|
|
|
460
483
|
}
|
|
461
484
|
return count;
|
|
462
485
|
}
|
|
463
|
-
/**
|
|
486
|
+
/**
|
|
487
|
+
* Returns the first node that matches (or does not match) the given mask value around this vertex loop, starting
|
|
488
|
+
* with the instance node and proceeding via vertex successors.
|
|
489
|
+
* @param mask the mask to check.
|
|
490
|
+
* @param value true for mask match and false for mask not match.
|
|
491
|
+
*/
|
|
464
492
|
findMaskAroundVertex(mask, value = true) {
|
|
465
493
|
let node = this;
|
|
466
494
|
do {
|
|
@@ -470,7 +498,12 @@ class HalfEdge {
|
|
|
470
498
|
} while (node !== this);
|
|
471
499
|
return undefined;
|
|
472
500
|
}
|
|
473
|
-
/**
|
|
501
|
+
/**
|
|
502
|
+
* Returns the first node that matches (or does not match) the given mask value around this face loop, starting
|
|
503
|
+
* with the instance node and proceeding via face successors.
|
|
504
|
+
* @param mask the mask to check.
|
|
505
|
+
* @param value true for mask match and false for mask not match.
|
|
506
|
+
*/
|
|
474
507
|
findMaskAroundFace(mask, value = true) {
|
|
475
508
|
let node = this;
|
|
476
509
|
do {
|
|
@@ -480,7 +513,12 @@ class HalfEdge {
|
|
|
480
513
|
} while (node !== this);
|
|
481
514
|
return undefined;
|
|
482
515
|
}
|
|
483
|
-
/**
|
|
516
|
+
/**
|
|
517
|
+
* Returns the first node that matches (or does not match) the given mask value around this edge, starting
|
|
518
|
+
* with the instance node and then checking its edge mate.
|
|
519
|
+
* @param mask the mask to check.
|
|
520
|
+
* @param value true for mask match and false for mask not match.
|
|
521
|
+
*/
|
|
484
522
|
findMaskAroundEdge(mask, value = true) {
|
|
485
523
|
if (this.isMaskSet(mask) === value)
|
|
486
524
|
return this;
|
|
@@ -489,8 +527,9 @@ class HalfEdge {
|
|
|
489
527
|
return mate;
|
|
490
528
|
return undefined;
|
|
491
529
|
}
|
|
492
|
-
/**
|
|
493
|
-
*
|
|
530
|
+
/**
|
|
531
|
+
* Set a mask and return prior value.
|
|
532
|
+
* @param mask mask to apply.
|
|
494
533
|
*/
|
|
495
534
|
testAndSetMask(mask) {
|
|
496
535
|
const oldMask = this.maskBits & mask;
|
|
@@ -498,8 +537,8 @@ class HalfEdge {
|
|
|
498
537
|
return oldMask;
|
|
499
538
|
}
|
|
500
539
|
/**
|
|
501
|
-
* Set
|
|
502
|
-
* @param node node containing xyz
|
|
540
|
+
* Set `this.x`, `this.y`, `this.z` from `node.x`, `node.y`, `node.z`.
|
|
541
|
+
* @param node node containing xyz.
|
|
503
542
|
*/
|
|
504
543
|
setXYZFrom(node) {
|
|
505
544
|
this.x = node.x;
|
|
@@ -507,7 +546,7 @@ class HalfEdge {
|
|
|
507
546
|
this.z = node.z;
|
|
508
547
|
}
|
|
509
548
|
/**
|
|
510
|
-
* Set
|
|
549
|
+
* Set `this.x`, `this.y`, `this.z` from `xyz.x`, `xyz.y`, `xyz.z`.
|
|
511
550
|
* @param node source with x,y,z properties
|
|
512
551
|
*/
|
|
513
552
|
setXYZ(xyz) {
|
|
@@ -516,32 +555,37 @@ class HalfEdge {
|
|
|
516
555
|
this.z = xyz.z;
|
|
517
556
|
}
|
|
518
557
|
/**
|
|
519
|
-
* Test if mask bits are set in the node's bitMask.
|
|
520
|
-
* @return
|
|
558
|
+
* Test if any of the `mask` bits are set in the node's bitMask.
|
|
559
|
+
* @return true (as a simple boolean, not a mask) if any bits of the `mask` match bits of the node's bitMask.
|
|
521
560
|
*/
|
|
522
|
-
isMaskSet(mask) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
561
|
+
isMaskSet(mask) {
|
|
562
|
+
return (this.maskBits & mask) !== 0;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Static method to test if any of the `mask` bits are set in the `node`'s bitMask.
|
|
566
|
+
* * This is used as filter in searches.
|
|
567
|
+
* @returns `node.isMaskSet(mask)`
|
|
526
568
|
*/
|
|
527
569
|
static filterIsMaskOn(node, mask) {
|
|
528
570
|
return node.isMaskSet(mask);
|
|
529
571
|
}
|
|
530
|
-
/**
|
|
531
|
-
*
|
|
532
|
-
*
|
|
572
|
+
/**
|
|
573
|
+
* Static method to test if any of the `mask` bits are set in the `node`'s bitMask.
|
|
574
|
+
* * This is used as filter in searches.
|
|
575
|
+
* @returns `!node.isMaskSet(mask)`
|
|
533
576
|
*/
|
|
534
577
|
static filterIsMaskOff(node, mask) {
|
|
535
578
|
return !node.isMaskSet(mask);
|
|
536
579
|
}
|
|
537
580
|
/**
|
|
538
581
|
* Create an edge with initial id,x,y at each end.
|
|
539
|
-
* @param id0 id for first node
|
|
540
|
-
* @param x0
|
|
541
|
-
* @param y0
|
|
542
|
-
* @param id1 id for second node
|
|
543
|
-
* @param x1 x coordinate for second node
|
|
544
|
-
* @param y1 y coordinate for second node
|
|
582
|
+
* @param id0 id for first node.
|
|
583
|
+
* @param x0 x coordinate for first node.
|
|
584
|
+
* @param y0 y coordinate for first node.
|
|
585
|
+
* @param id1 id for second node.
|
|
586
|
+
* @param x1 x coordinate for second node.
|
|
587
|
+
* @param y1 y coordinate for second node.
|
|
588
|
+
* @returns the reference to the new node at (x0,y0).
|
|
545
589
|
*/
|
|
546
590
|
static createEdgeXYXY(id0, x0, y0, id1, x1, y1) {
|
|
547
591
|
const node0 = new HalfEdge(x0, y0);
|
|
@@ -552,15 +596,15 @@ class HalfEdge {
|
|
|
552
596
|
node1._id = id1;
|
|
553
597
|
return node0;
|
|
554
598
|
}
|
|
555
|
-
/**
|
|
556
|
-
*
|
|
557
|
-
* * is the
|
|
558
|
-
*
|
|
559
|
-
* *
|
|
560
|
-
* *
|
|
561
|
-
* * if nodeA, nodeB are in the same face loop, the loop splits into two loops.
|
|
599
|
+
/**
|
|
600
|
+
*"Pinch" is the universal operator for manipulating a node's next and previous pointers.
|
|
601
|
+
* * It is its own inverse: applying it twice on the same inputs (i.e., `pinch(a,b); pinch(a,b);`) gets back to
|
|
602
|
+
* where you started.
|
|
603
|
+
* * If the inputs are in different face loops, the loops join to one face loop after the pinch.
|
|
604
|
+
* * If the inputs are in the same face loop, the loop splits into two face loops after the pinch.
|
|
562
605
|
*/
|
|
563
606
|
static pinch(nodeA, nodeB) {
|
|
607
|
+
// Visualization can be found at geometry/internaldocs/Graph.md
|
|
564
608
|
if (nodeA !== nodeB) {
|
|
565
609
|
const predA = nodeA._facePredecessor;
|
|
566
610
|
const predB = nodeB._facePredecessor;
|
|
@@ -572,17 +616,19 @@ class HalfEdge {
|
|
|
572
616
|
}
|
|
573
617
|
/**
|
|
574
618
|
* Pinch this half edge out of its base vertex loop.
|
|
575
|
-
* @return the surviving HalfEdge in the vertex loop
|
|
619
|
+
* @return the surviving HalfEdge in the vertex loop or `undefined` if the instance HalfEdge is already dangling.
|
|
576
620
|
*/
|
|
577
621
|
yankFromVertexLoop() {
|
|
578
622
|
const other = this.edgeMate.faceSuccessor;
|
|
579
623
|
if (other === this)
|
|
580
624
|
return undefined;
|
|
625
|
+
// at this point "other" is the vertex predecessor of "this"
|
|
581
626
|
HalfEdge.pinch(this, other);
|
|
582
627
|
return other;
|
|
583
628
|
}
|
|
584
|
-
/**
|
|
585
|
-
*
|
|
629
|
+
/**
|
|
630
|
+
* Turn all pointers to `undefined` so garbage collector can reuse the object.
|
|
631
|
+
* * This is to be called only by a Graph object that is being decommissioned.
|
|
586
632
|
*/
|
|
587
633
|
decommission() {
|
|
588
634
|
this._facePredecessor = undefined;
|
|
@@ -590,27 +636,34 @@ class HalfEdge {
|
|
|
590
636
|
this._edgeMate = undefined;
|
|
591
637
|
}
|
|
592
638
|
/** Return the node. This identity function is useful as the NodeFunction in collector methods. */
|
|
593
|
-
static nodeToSelf(node) {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
/** Return the id of a node.Useful for collector methods. */
|
|
597
|
-
static
|
|
598
|
-
|
|
639
|
+
static nodeToSelf(node) {
|
|
640
|
+
return node;
|
|
641
|
+
}
|
|
642
|
+
/** Return the id of a node. Useful for collector methods. */
|
|
643
|
+
static nodeToId(node) {
|
|
644
|
+
return node.id;
|
|
645
|
+
}
|
|
646
|
+
/** Return the id of a node as string. Useful for collector methods. */
|
|
647
|
+
static nodeToIdString(node) {
|
|
648
|
+
return node.id.toString();
|
|
649
|
+
}
|
|
650
|
+
/** Return the [id, mask, [x,y]] of a node. Useful for collector methods. */
|
|
599
651
|
static nodeToIdMaskXY(node) {
|
|
600
652
|
return { id: node.id, mask: HalfEdge.nodeToMaskString(node), xy: [node.x, node.y] };
|
|
601
653
|
}
|
|
602
|
-
/** Return the [id, [x,y]] of a node.
|
|
654
|
+
/** Return the [id, mask, [x,y]] of a node as string. Useful for collector methods. */
|
|
603
655
|
static nodeToIdXYString(node) {
|
|
604
656
|
const s = `${node.id.toString()}+${HalfEdge.nodeToMaskString(node)}[${node.x},${node.y}]`;
|
|
605
657
|
return s;
|
|
606
658
|
}
|
|
607
|
-
/** Return the [id, [x,y
|
|
659
|
+
/** Return the [id, [x,y,z]] of a node as string. Useful for collector methods. */
|
|
608
660
|
static nodeToIdXYZString(node) {
|
|
609
661
|
return `[${node.id.toString()}: ${node.x},${node.y},${node.z}]`;
|
|
610
662
|
}
|
|
611
|
-
/**
|
|
663
|
+
/**
|
|
664
|
+
* Create a string representation of the mask.
|
|
612
665
|
* * Null mask is empty string.
|
|
613
|
-
* * Appended characters B,P,X for
|
|
666
|
+
* * Appended characters B,P,X,N are for BOUNDARY_EDGE, PRIMARY_EDGE, EXTERIOR, and NULL_FACE mask bits.
|
|
614
667
|
*/
|
|
615
668
|
static nodeToMaskString(node) {
|
|
616
669
|
let s = "";
|
|
@@ -624,49 +677,46 @@ class HalfEdge {
|
|
|
624
677
|
s += "N";
|
|
625
678
|
return s;
|
|
626
679
|
}
|
|
627
|
-
/** Return [x,y] with coordinates of node */
|
|
628
|
-
static nodeToXY(node) {
|
|
629
|
-
|
|
680
|
+
/** Return [x,y] with coordinates of node. */
|
|
681
|
+
static nodeToXY(node) {
|
|
682
|
+
return [node.x, node.y];
|
|
683
|
+
}
|
|
684
|
+
/** Return Vector2d from `this` to face successor (with only xy coordinates). */
|
|
630
685
|
vectorToFaceSuccessorXY(result) {
|
|
631
686
|
return Vector2d.create(this.faceSuccessor.x - this.x, this.faceSuccessor.y - this.y, result);
|
|
632
687
|
}
|
|
633
|
-
/** Return Vector3d to face successor */
|
|
688
|
+
/** Return Vector3d from `this` to face successor. */
|
|
634
689
|
vectorToFaceSuccessor(result) {
|
|
635
690
|
const other = this.faceSuccessor;
|
|
636
691
|
return Vector3d.create(other.x - this.x, other.y - this.y, other.z - this.z, result);
|
|
637
692
|
}
|
|
638
|
-
/** Return Vector3d to face successor */
|
|
693
|
+
/** Return Vector3d from `this` to face successor. */
|
|
639
694
|
vectorToFacePredecessor(result) {
|
|
640
695
|
const other = this.facePredecessor;
|
|
641
696
|
return Vector3d.create(other.x - this.x, other.y - this.y, other.z - this.z, result);
|
|
642
697
|
}
|
|
643
|
-
/**
|
|
698
|
+
/** Test if `spaceNode` is in the sector at `sectorNode`. */
|
|
644
699
|
static isNodeVisibleInSector(spaceNode, sectorNode) {
|
|
645
|
-
// remark: fussy details ported from native code.
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
// (As usual, hard coded zero is suspect, but it seems to work nicely in the discrete decisions.)
|
|
700
|
+
// remark: fussy details ported from native code. The obscure cases seemed "unlikely" at first. But pre-existing
|
|
701
|
+
// unit tests for triangulation pinged just about everything. So it really matters to do the "0" cases this way
|
|
702
|
+
// (as usual, hard coded zero is suspect, but it seems to work nicely in the discrete decisions).
|
|
649
703
|
if (sectorNode.vertexSuccessor === sectorNode)
|
|
650
704
|
return true;
|
|
651
705
|
const successor = sectorNode.faceSuccessor;
|
|
652
706
|
const predecessor = sectorNode.facePredecessor;
|
|
653
707
|
const successorCross = this.crossProductXYToTargets(sectorNode, successor, spaceNode);
|
|
654
708
|
const predecessorCross = this.crossProductXYToTargets(predecessor, sectorNode, spaceNode);
|
|
655
|
-
// simplest case:
|
|
709
|
+
// simplest case: two positives
|
|
656
710
|
if (successorCross > 0.0 && predecessorCross > 0.0)
|
|
657
711
|
return true;
|
|
658
712
|
const sectorCross = this.crossProductXYToTargets(predecessor, sectorNode, successor);
|
|
659
713
|
if (predecessorCross <= 0.0 && successorCross <= 0.0) {
|
|
660
714
|
if (predecessorCross === 0.0 && successorCross === 0.0 && sectorCross === 0.0) {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
in if it is the other node in the degenerate face.
|
|
664
|
-
*/
|
|
715
|
+
// Everything is on a line. If the sector is a degenerate face, nodeP
|
|
716
|
+
// can only be in if it is the other node in the degenerate face.
|
|
665
717
|
if (predecessor === successor && sectorNode.vertexSuccessor !== sectorNode)
|
|
666
718
|
return spaceNode === successor;
|
|
667
|
-
|
|
668
|
-
to sectorP points forward to nodeP.
|
|
669
|
-
*/
|
|
719
|
+
// Sector is 360 degrees. Call it in only if vector from predP to sectorP points forward to nodeP.
|
|
670
720
|
return HalfEdge.dotProductNodeToNodeVectorsXY(predecessor, sectorNode, sectorNode, spaceNode) > 0.0;
|
|
671
721
|
}
|
|
672
722
|
else {
|
|
@@ -675,56 +725,59 @@ class HalfEdge {
|
|
|
675
725
|
}
|
|
676
726
|
else {
|
|
677
727
|
if (sectorCross === 0.0 && predecessorCross !== 0.0 && successorCross !== 0.0) {
|
|
678
|
-
// The incoming and outgoing edges at the sector are identical direction.
|
|
679
|
-
//
|
|
680
|
-
//
|
|
681
|
-
//
|
|
682
|
-
// Exact equality for zero is ok because cross product should be using identical
|
|
683
|
-
// coordinates in subtracted terms. (All furrow eyebrows in unison ....)
|
|
728
|
+
// The incoming and outgoing edges at the sector are identical direction. We have to decide if this node is
|
|
729
|
+
// inside the degenerate face (i.e. a geometrically empty sector) or outside (i.e. a nearly complete sector).
|
|
730
|
+
// In the inside case, the face is just two nodes. Exact equality for zero is ok because cross product should
|
|
731
|
+
// be using identical coordinates in subtracted terms (all furrow eyebrows in unison).
|
|
684
732
|
return predecessor !== successor;
|
|
685
733
|
}
|
|
686
734
|
return sectorCross < 0.0;
|
|
687
735
|
}
|
|
688
736
|
}
|
|
689
|
-
/** Returns
|
|
737
|
+
/** Returns 2D cross product of vectors from `base` to `targetA` and from `base` to `targetB`. */
|
|
690
738
|
static crossProductXYToTargets(base, targetA, targetB) {
|
|
691
739
|
return Geometry.crossProductXYXY(targetA.x - base.x, targetA.y - base.y, targetB.x - base.x, targetB.y - base.y);
|
|
692
740
|
}
|
|
693
|
-
/** Returns
|
|
741
|
+
/** Returns 2D dot product of vectors from `baseA` to `targetA` and from `baseB` to `targetB`. */
|
|
694
742
|
static dotProductNodeToNodeVectorsXY(baseA, targetA, baseB, targetB) {
|
|
695
743
|
return Geometry.dotProductXYXY(targetA.x - baseA.x, targetA.y - baseA.y, targetB.x - baseB.x, targetB.y - baseB.y);
|
|
696
744
|
}
|
|
697
|
-
/** Return cross product
|
|
698
|
-
*/
|
|
745
|
+
/** Return 2D cross product of vectors from `nodeA` to `nodeB` and from `nodeB` to `nodeC`. */
|
|
699
746
|
static crossProductXYAlongChain(nodeA, nodeB, nodeC) {
|
|
700
747
|
return Geometry.crossProductXYXY(nodeB.x - nodeA.x, nodeB.y - nodeA.y, nodeC.x - nodeB.x, nodeC.y - nodeB.y);
|
|
701
748
|
}
|
|
702
749
|
/**
|
|
703
750
|
* Compute whether the sector defined by the chain of nodes is convex.
|
|
751
|
+
* * This function is determining if, in the traversal of the HalfEdges in a face loop, a corner makes a left turn
|
|
752
|
+
* (convex) or a right turn (not-convex). Note that if we have a convex face, then to traverse it in ccw orientation,
|
|
753
|
+
* we always do left turns. However, if the face is not convex, we make both left and right turns.
|
|
704
754
|
* * This computation ignores z-coordinates and connectivity, so the nodes are not required to be in the same face loop.
|
|
705
|
-
* @param nodeA the first node in the chain, nominally the face predecessor of nodeB
|
|
706
|
-
* @param nodeB the second node in the chain; the node at the sector vertex
|
|
707
|
-
* @param nodeC the third node in the chain, nominally the face successor of nodeB
|
|
708
|
-
* @param signedAreaTol optional signed area tolerance to use in test for parallel vectors.
|
|
709
|
-
*
|
|
710
|
-
* @returns true iff the sector is convex
|
|
755
|
+
* @param nodeA the first node in the chain, nominally the face predecessor of nodeB.
|
|
756
|
+
* @param nodeB the second node in the chain; the node at the sector vertex.
|
|
757
|
+
* @param nodeC the third node in the chain, nominally the face successor of nodeB.
|
|
758
|
+
* @param signedAreaTol optional signed area tolerance to use in test for parallel vectors. Typically this is a
|
|
759
|
+
* fraction of the sector's face's signed area. We can't compute area here, so if undefined, zero tolerance is used.
|
|
760
|
+
* @returns true iff the sector is convex. A degenerate sector, where the incident edges overlap, returns false.
|
|
711
761
|
*/
|
|
712
762
|
static isSectorConvex(nodeA, nodeB, nodeC, signedAreaTol = 0) {
|
|
713
763
|
const signedSectorArea = 0.5 * HalfEdge.crossProductXYAlongChain(nodeA, nodeB, nodeC);
|
|
714
764
|
signedAreaTol = signedAreaTol ?? 0.0;
|
|
715
765
|
if (Math.abs(signedSectorArea) <= Math.abs(signedAreaTol)) {
|
|
716
|
-
// the sector vectors are nearly parallel or
|
|
766
|
+
// the sector vectors are nearly parallel or anti-parallel; only the former is deemed convex.
|
|
717
767
|
return HalfEdge.dotProductNodeToNodeVectorsXY(nodeA, nodeB, nodeB, nodeC) > 0.0;
|
|
718
768
|
}
|
|
719
|
-
|
|
769
|
+
// // sector is convex if the area is positive. Call it convex even if we are a little bit on the other side of zero.
|
|
770
|
+
return signedSectorArea > -signedAreaTol;
|
|
720
771
|
}
|
|
721
772
|
/**
|
|
722
773
|
* Compute whether the sector at this node is convex.
|
|
774
|
+
* * This function is determining if, in the traversal of the HalfEdges in a face loop, a corner makes a left turn
|
|
775
|
+
* (convex) or a right turn (not-convex). Note that if we have a convex face, then to traverse it in ccw orientation,
|
|
776
|
+
* we always do left turns. However, if the face is not convex, we make both left and right turns.
|
|
723
777
|
* * This computation ignores z-coordinates.
|
|
724
|
-
* @param signedAreaTol optional signed area tolerance to use in test for parallel vectors.
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
* @returns true iff the sector is convex and its two edges are not antiparallel.
|
|
778
|
+
* @param signedAreaTol optional signed area tolerance to use in test for parallel vectors. If undefined, a fraction
|
|
779
|
+
* (`Geometry.smallMetricDistanceSquared`) of the computed signed area is used. Pass 0 to skip toleranced computation.
|
|
780
|
+
* @returns true iff the sector is convex. A degenerate sector, where the incident edges overlap, returns false.
|
|
728
781
|
*/
|
|
729
782
|
isSectorConvex(signedAreaTol) {
|
|
730
783
|
if (signedAreaTol === undefined)
|
|
@@ -733,10 +786,11 @@ class HalfEdge {
|
|
|
733
786
|
}
|
|
734
787
|
/**
|
|
735
788
|
* Compute whether this face is convex.
|
|
789
|
+
* * Note that if we have a convex face, then to traverse it in ccw orientation, we always do left turns.
|
|
790
|
+
* However, if the face is not convex, we make both left and right turns.
|
|
736
791
|
* * This computation ignores z-coordinates.
|
|
737
|
-
* @param tolerance optional relative tolerance to use in test for parallel vectors.
|
|
738
|
-
*
|
|
739
|
-
* Pass zero to skip toleranced computation.
|
|
792
|
+
* @param tolerance optional relative tolerance to use in test for parallel vectors. Default value is
|
|
793
|
+
* `Geometry.smallMetricDistanceSquared`. Pass 0 to skip toleranced computation.
|
|
740
794
|
* @returns true iff this face is convex.
|
|
741
795
|
*/
|
|
742
796
|
isFaceConvex(tolerance = Geometry.smallMetricDistanceSquared) {
|
|
@@ -749,83 +803,80 @@ class HalfEdge {
|
|
|
749
803
|
} while (node !== this);
|
|
750
804
|
return true;
|
|
751
805
|
}
|
|
752
|
-
/**
|
|
753
|
-
* Isolate the edge from the graph by yanking each end from its vertex loop.
|
|
754
|
-
*/
|
|
806
|
+
/** Isolate the edge from the graph by yanking each end from its vertex loop. */
|
|
755
807
|
isolateEdge() {
|
|
756
808
|
const mate = this.edgeMate;
|
|
757
809
|
this.yankFromVertexLoop();
|
|
758
810
|
mate.yankFromVertexLoop();
|
|
759
811
|
}
|
|
760
|
-
/**
|
|
761
|
-
* @return whether this edge is isolated from the rest of the graph.
|
|
762
|
-
*/
|
|
812
|
+
/** Specify whether this edge is isolated from the rest of the graph. */
|
|
763
813
|
get isIsolatedEdge() {
|
|
764
814
|
return this === this.vertexSuccessor && this.edgeMate === this.edgeMate.vertexSuccessor;
|
|
765
815
|
}
|
|
766
|
-
/** Return true if `this` is lexically below `other
|
|
816
|
+
/** Return true if `this` is lexically below `other`. We compare y first, then x, and ignore z. */
|
|
767
817
|
belowYX(other) {
|
|
768
|
-
// Check y's
|
|
769
|
-
// if (!Geometry.isSameCoordinate(a.y, b.y))
|
|
770
818
|
if (this.y < other.y)
|
|
771
819
|
return true;
|
|
772
820
|
if (this.y > other.y)
|
|
773
821
|
return false;
|
|
774
|
-
// same y.
|
|
775
|
-
// Check x's
|
|
776
822
|
if (this.x < other.x)
|
|
777
823
|
return true;
|
|
778
824
|
return false;
|
|
779
825
|
}
|
|
780
|
-
/** Returns
|
|
781
|
-
static testNodeMaskNotExterior(node) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
/** Returns
|
|
785
|
-
|
|
786
|
-
|
|
826
|
+
/** Returns `true` if the node does NOT have `Mask.EXTERIOR_MASK` set. */
|
|
827
|
+
static testNodeMaskNotExterior(node) {
|
|
828
|
+
return !node.isMaskSet(HalfEdgeMask.EXTERIOR);
|
|
829
|
+
}
|
|
830
|
+
/** Returns `true` if the edge mate has `Mask.EXTERIOR_MASK` set. */
|
|
831
|
+
static testMateMaskExterior(node) {
|
|
832
|
+
return node.edgeMate.isMaskSet(HalfEdgeMask.EXTERIOR);
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Returns radians between this edge and its face predecessor edge, using all three coordinates x,y,z and
|
|
836
|
+
* given normal to resolve sweep direction.
|
|
837
|
+
* * The returned angle is non-negative: 0 <= radians < 2*PI.
|
|
838
|
+
*/
|
|
787
839
|
static sectorSweepRadiansXYZ(node, normal) {
|
|
788
|
-
const
|
|
789
|
-
const
|
|
790
|
-
return Angle.orientedRadiansBetweenVectorsXYZ(
|
|
840
|
+
const suc = node.faceSuccessor;
|
|
841
|
+
const pred = node.facePredecessor;
|
|
842
|
+
return Angle.orientedRadiansBetweenVectorsXYZ(suc.x - node.x, suc.y - node.y, suc.z - node.z, pred.x - node.x, pred.y - node.y, pred.z - node.z, normal.x, normal.y, normal.z, true);
|
|
791
843
|
}
|
|
792
|
-
/** Returns
|
|
844
|
+
/** Returns true if the face has positive area in xy parts. */
|
|
793
845
|
static testFacePositiveAreaXY(node) {
|
|
794
846
|
return node.countEdgesAroundFace() > 2 && node.signedFaceArea() > 0.0;
|
|
795
847
|
}
|
|
796
|
-
/** Return true if x and y coordinates of this and other are exactly equal
|
|
848
|
+
/** Return true if x and y coordinates of `this` and `other` are exactly equal .*/
|
|
797
849
|
isEqualXY(other) {
|
|
798
850
|
return this.x === other.x && this.y === other.y;
|
|
799
851
|
}
|
|
800
|
-
/** Return distance between xy coordinates of this and other node */
|
|
852
|
+
/** Return distance between xy coordinates of `this` and `other` node. */
|
|
801
853
|
distanceXY(other) {
|
|
802
854
|
return Geometry.distanceXYXY(this.x, this.y, other.x, other.y);
|
|
803
855
|
}
|
|
804
|
-
/** Return distance between xyz coordinates of this and other node */
|
|
856
|
+
/** Return distance between xyz coordinates of `this` and `other` node. */
|
|
805
857
|
distanceXYZ(other) {
|
|
806
858
|
return Geometry.distanceXYZXYZ(this.x, this.y, this.z, other.x, other.y, other.z);
|
|
807
859
|
}
|
|
808
860
|
/**
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
* @returns Return the array of function values.
|
|
861
|
+
* Evaluate `f(node)` at each node around `this` node's face loop. Collect the function values.
|
|
862
|
+
* @param f optional node function. If `undefined`, collect the nodes themselves.
|
|
863
|
+
* @returns the array of function values.
|
|
813
864
|
*/
|
|
814
865
|
collectAroundFace(f) {
|
|
815
866
|
const nodes = [];
|
|
816
867
|
let node = this;
|
|
817
868
|
do {
|
|
818
|
-
nodes.push(f ? f(node) : node);
|
|
869
|
+
nodes.push(f ? f(node) : node); // push the node itself if "f" is undefined
|
|
819
870
|
node = node.faceSuccessor;
|
|
820
871
|
} while (node !== this);
|
|
821
872
|
return nodes;
|
|
822
873
|
}
|
|
823
874
|
/**
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
* @param mask target mask
|
|
827
|
-
* @param value target value for mask on half edges.
|
|
828
|
-
* @param
|
|
875
|
+
* Search around `this` node's vertex loop for nodes with the specified mask value.
|
|
876
|
+
* * Returned nodes satisfy `node.isMaskSet(mask) === value`.
|
|
877
|
+
* @param mask target mask.
|
|
878
|
+
* @param value target boolean value for mask on half edges.
|
|
879
|
+
* @param result optional array to be cleared and receive masked nodes.
|
|
829
880
|
*/
|
|
830
881
|
collectMaskedEdgesAroundVertex(mask, value = true, result) {
|
|
831
882
|
if (result === undefined)
|
|
@@ -841,25 +892,23 @@ class HalfEdge {
|
|
|
841
892
|
return result;
|
|
842
893
|
}
|
|
843
894
|
/**
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
* @returns Return the array of function values.
|
|
895
|
+
* Evaluate `f(node)` at each node around `this` node's vertex loop. Collect the function values.
|
|
896
|
+
* @param f optional node function. If `undefined`, collect the nodes themselves.
|
|
897
|
+
* @returns the array of function values.
|
|
848
898
|
*/
|
|
849
899
|
collectAroundVertex(f) {
|
|
850
900
|
const nodes = [];
|
|
851
901
|
let node = this;
|
|
852
902
|
do {
|
|
853
|
-
nodes.push(f ? f(node) : node);
|
|
903
|
+
nodes.push(f ? f(node) : node); // push the node itself if "f" is undefined
|
|
854
904
|
node = node.vertexSuccessor;
|
|
855
905
|
} while (node !== this);
|
|
856
906
|
return nodes;
|
|
857
907
|
}
|
|
858
908
|
/**
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
862
|
-
* @returns Return the sum
|
|
909
|
+
* Evaluate `f(node)` at each node around `this` node's face loop. Sum the function values.
|
|
910
|
+
* @param f node to number function.
|
|
911
|
+
* @returns the sum of function values.
|
|
863
912
|
*/
|
|
864
913
|
sumAroundFace(f) {
|
|
865
914
|
let node = this;
|
|
@@ -871,10 +920,9 @@ class HalfEdge {
|
|
|
871
920
|
return sum;
|
|
872
921
|
}
|
|
873
922
|
/**
|
|
874
|
-
*
|
|
875
|
-
*
|
|
876
|
-
*
|
|
877
|
-
* @returns Return the sum
|
|
923
|
+
* Evaluate `f(node)` at each node around `this` node's vertex loop. Sum the function values.
|
|
924
|
+
* @param f node to number function.
|
|
925
|
+
* @returns the sum of function values.
|
|
878
926
|
*/
|
|
879
927
|
sumAroundVertex(f) {
|
|
880
928
|
let node = this;
|
|
@@ -885,7 +933,7 @@ class HalfEdge {
|
|
|
885
933
|
} while (node !== this);
|
|
886
934
|
return sum;
|
|
887
935
|
}
|
|
888
|
-
/**
|
|
936
|
+
/** Clear the given mask bits for all nodes in `this` node's face loop. */
|
|
889
937
|
clearMaskAroundFace(mask) {
|
|
890
938
|
let node = this;
|
|
891
939
|
do {
|
|
@@ -893,7 +941,7 @@ class HalfEdge {
|
|
|
893
941
|
node = node.faceSuccessor;
|
|
894
942
|
} while (node !== this);
|
|
895
943
|
}
|
|
896
|
-
/**
|
|
944
|
+
/** Clear out the given mask bits for all nodes in `this` node's vertex loop. */
|
|
897
945
|
clearMaskAroundVertex(mask) {
|
|
898
946
|
let node = this;
|
|
899
947
|
do {
|
|
@@ -902,29 +950,39 @@ class HalfEdge {
|
|
|
902
950
|
} while (node !== this);
|
|
903
951
|
}
|
|
904
952
|
/**
|
|
905
|
-
* Compute the signed
|
|
953
|
+
* Compute the signed xy area of `this` node's face.
|
|
906
954
|
* * A positive area is counterclockwise.
|
|
907
955
|
* * A negative area is clockwise.
|
|
908
|
-
* @returns signed area of this node's face
|
|
956
|
+
* @returns signed area of `this` node's face.
|
|
909
957
|
*/
|
|
910
958
|
signedFaceArea() {
|
|
911
959
|
let sum = 0;
|
|
912
|
-
//
|
|
913
|
-
//
|
|
914
|
-
//
|
|
915
|
-
//
|
|
960
|
+
// We start from `this` node and traverse the face, forming trapezoids with vertical bases.
|
|
961
|
+
// Some trapezoids will have a zero-length base, e.g., a triangle.
|
|
962
|
+
// Trapezoid bases are measured from `this.y` to keep area values numerically smaller.
|
|
963
|
+
// Split each trapezoid into two triangles whose y-coordinates are above/below `this.y`.
|
|
964
|
+
// Each trapezoid's signed area is computed by summing the signed areas of these two triangles.
|
|
965
|
+
// Area signs depend on careful assignment of signs to trapezoid height (relative to `this.x`) and trapezoid
|
|
966
|
+
// bases (relative to `this.y`).
|
|
967
|
+
// Some trapezoids have signed areas outside the face that are cancelled out by subsequent trapezoids.
|
|
968
|
+
// The formula in the loop accumulates twice the trapezoid areas, so at the end we halve the sum.
|
|
969
|
+
// Illustration of the algorithm can be found at geometry/internaldocs/Graph.md
|
|
970
|
+
let x0 = this.x;
|
|
971
|
+
let x1 = 0.0;
|
|
916
972
|
const y0 = this.y;
|
|
917
973
|
let dy0 = 0.0;
|
|
918
974
|
let dy1 = 0.0;
|
|
919
|
-
let x0 = this.x;
|
|
920
|
-
let x1;
|
|
921
975
|
let node1;
|
|
922
976
|
let node0 = this;
|
|
923
977
|
do {
|
|
924
978
|
node1 = node0.faceSuccessor;
|
|
925
979
|
x1 = node1.x;
|
|
926
980
|
dy1 = node1.y - y0;
|
|
927
|
-
|
|
981
|
+
// When trapezoid bases dy0 and dy1 have opposite sign, this product is (twice) the difference of the areas
|
|
982
|
+
// of the trapezoid above and below y=y0. This is equal to (twice) the difference of the areas of the congruent
|
|
983
|
+
// triangles formed by the trapezoid diagonals and bases, a consequence of the other two triangles having equal
|
|
984
|
+
// area, and thus cancelling out.
|
|
985
|
+
sum += (x0 - x1) * (dy0 + dy1); // twice trapezoid area = trapezoid height * sum of trapezoid bases
|
|
928
986
|
x0 = x1;
|
|
929
987
|
dy0 = dy1;
|
|
930
988
|
node0 = node1;
|
|
@@ -932,108 +990,105 @@ class HalfEdge {
|
|
|
932
990
|
return 0.5 * sum;
|
|
933
991
|
}
|
|
934
992
|
/**
|
|
935
|
-
*
|
|
993
|
+
* Interpolate xy coordinates between `this` node and its face successor.
|
|
936
994
|
* @param fraction fractional position along this edge.
|
|
937
|
-
* @param result
|
|
995
|
+
* @param result optional point to populate and return.
|
|
938
996
|
*/
|
|
939
997
|
fractionToPoint2d(fraction, result) {
|
|
940
|
-
const
|
|
941
|
-
return Point2d.create(this.x + (
|
|
998
|
+
const suc = this.faceSuccessor;
|
|
999
|
+
return Point2d.create(this.x + (suc.x - this.x) * fraction, this.y + (suc.y - this.y) * fraction, result);
|
|
942
1000
|
}
|
|
943
1001
|
/**
|
|
944
|
-
*
|
|
1002
|
+
* Interpolate xyz coordinates between `this` node and its face successor.
|
|
945
1003
|
* @param fraction fractional position along this edge.
|
|
946
|
-
* @param result
|
|
1004
|
+
* @param result optional point to populate and return.
|
|
947
1005
|
*/
|
|
948
1006
|
fractionToPoint3d(fraction, result) {
|
|
949
|
-
const
|
|
950
|
-
return Point3d.create(this.x + (
|
|
1007
|
+
const suc = this.faceSuccessor;
|
|
1008
|
+
return Point3d.create(this.x + (suc.x - this.x) * fraction, this.y + (suc.y - this.y) * fraction, this.z + (suc.z - this.z) * fraction, result);
|
|
951
1009
|
}
|
|
952
1010
|
/**
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
* @param
|
|
956
|
-
* @param
|
|
1011
|
+
* Interpolate xy coordinates at `fractionAlong` between this node and its face successor. Then shift perpendicular
|
|
1012
|
+
* to the left of this edge by `fractionPerpendicular`.
|
|
1013
|
+
* @param fractionAlong fractional position along this edge.
|
|
1014
|
+
* @param fractionPerpendicular fractional position along the left perpendicular with the same length as this edge.
|
|
1015
|
+
* @param result optional xy coordinates.
|
|
957
1016
|
*/
|
|
958
1017
|
fractionAlongAndPerpendicularToPoint2d(fractionAlong, fractionPerpendicular, result) {
|
|
959
|
-
const
|
|
960
|
-
const dx =
|
|
961
|
-
const dy =
|
|
1018
|
+
const suc = this.faceSuccessor;
|
|
1019
|
+
const dx = suc.x - this.x;
|
|
1020
|
+
const dy = suc.y - this.y;
|
|
962
1021
|
return Point2d.create(this.x + dx * fractionAlong - dy * fractionPerpendicular, this.y + dy * fractionAlong + dx * fractionPerpendicular, result);
|
|
963
1022
|
}
|
|
964
|
-
/**
|
|
965
|
-
* return the 3d coordinates at this half edge base
|
|
966
|
-
*/
|
|
1023
|
+
/** Return the 3d coordinates at this half edge. */
|
|
967
1024
|
getPoint3d(result) {
|
|
968
1025
|
return Point3d.create(this.x, this.y, this.z, result);
|
|
969
1026
|
}
|
|
970
|
-
/**
|
|
971
|
-
* return the 2d coordinates at this half edge base
|
|
972
|
-
*/
|
|
1027
|
+
/** Return the 2d coordinates at this half edge. */
|
|
973
1028
|
getPoint2d(result) {
|
|
974
1029
|
return Point2d.create(this.x, this.y, result);
|
|
975
1030
|
}
|
|
976
|
-
/**
|
|
977
|
-
* return a 3d vector from start to end of this half edge.
|
|
978
|
-
*/
|
|
1031
|
+
/** Return a 3d vector from start to end of this half edge. */
|
|
979
1032
|
getVector3dAlongEdge(result) {
|
|
980
|
-
const
|
|
981
|
-
return Vector3d.create(
|
|
1033
|
+
const suc = this.faceSuccessor;
|
|
1034
|
+
return Vector3d.create(suc.x - this.x, suc.y - this.y, suc.z - this.z, result);
|
|
982
1035
|
}
|
|
983
|
-
/**
|
|
984
|
-
* return a 2d vector from start to end of this half edge
|
|
985
|
-
*/
|
|
1036
|
+
/** Return a 2d vector from start to end of this half edge. */
|
|
986
1037
|
getVector2dAlongEdge(result) {
|
|
987
|
-
const
|
|
988
|
-
return Vector2d.create(
|
|
1038
|
+
const suc = this.faceSuccessor;
|
|
1039
|
+
return Vector2d.create(suc.x - this.x, suc.y - this.y, result);
|
|
989
1040
|
}
|
|
990
1041
|
/**
|
|
991
|
-
* Return the interpolated x coordinate between this node and its face successor.
|
|
1042
|
+
* Return the interpolated x coordinate between `this` node and its face successor.
|
|
992
1043
|
* @param fraction fractional position along this edge.
|
|
993
1044
|
*/
|
|
994
1045
|
fractionToX(fraction) {
|
|
995
|
-
const
|
|
996
|
-
return this.x + (
|
|
1046
|
+
const suc = this.faceSuccessor;
|
|
1047
|
+
return this.x + (suc.x - this.x) * fraction;
|
|
997
1048
|
}
|
|
998
1049
|
/**
|
|
999
|
-
* Return the interpolated y coordinate between this node and its face successor.
|
|
1050
|
+
* Return the interpolated y coordinate between `this` node and its face successor.
|
|
1000
1051
|
* @param fraction fractional position along this edge.
|
|
1001
1052
|
*/
|
|
1002
1053
|
fractionToY(fraction) {
|
|
1003
|
-
const
|
|
1004
|
-
return this.y + (
|
|
1054
|
+
const suc = this.faceSuccessor;
|
|
1055
|
+
return this.y + (suc.y - this.y) * fraction;
|
|
1005
1056
|
}
|
|
1006
1057
|
/**
|
|
1007
|
-
* Return the interpolated z coordinate between this node and its face successor.
|
|
1058
|
+
* Return the interpolated z coordinate between `this` node and its face successor.
|
|
1008
1059
|
* @param fraction fractional position along this edge.
|
|
1009
1060
|
*/
|
|
1010
1061
|
fractionToZ(fraction) {
|
|
1011
|
-
const
|
|
1012
|
-
return this.z + (
|
|
1062
|
+
const suc = this.faceSuccessor;
|
|
1063
|
+
return this.z + (suc.z - this.z) * fraction;
|
|
1013
1064
|
}
|
|
1014
1065
|
/**
|
|
1015
|
-
*
|
|
1066
|
+
* Compute fractional coordinates of the intersection of edges from given base nodes.
|
|
1016
1067
|
* * If parallel or colinear, return undefined.
|
|
1017
1068
|
* * If (possibly extended) lines intersect, return the fractions of intersection as x,y in the result.
|
|
1018
|
-
* @param nodeA0
|
|
1019
|
-
* @param nodeB0
|
|
1020
|
-
* @param result optional preallocated result
|
|
1069
|
+
* @param nodeA0 base node of edge A.
|
|
1070
|
+
* @param nodeB0 base node of edge B.
|
|
1071
|
+
* @param result optional preallocated result.
|
|
1021
1072
|
*/
|
|
1022
1073
|
static transverseIntersectionFractions(nodeA0, nodeB0, result) {
|
|
1023
1074
|
const nodeA1 = nodeA0.faceSuccessor;
|
|
1024
1075
|
const nodeB1 = nodeB0.faceSuccessor;
|
|
1025
1076
|
if (!result)
|
|
1026
1077
|
result = Vector2d.create();
|
|
1078
|
+
// To find the fraction of intersection (ta,tb), you need to solve these 2 equations:
|
|
1079
|
+
// (nodeA1.x - nodeA0.x)ta + (nodeB0.x - nodeB1.x)tb = nodeB0.x - nodeA0.x
|
|
1080
|
+
// (nodeA1.y - nodeA0.y)ta + (nodeB0.y - nodeB1.y)tb = nodeB0.y - nodeA0.y
|
|
1081
|
+
// Proof can be found at geometry/internaldocs/Graph.md
|
|
1027
1082
|
if (SmallSystem.linearSystem2d(nodeA1.x - nodeA0.x, nodeB0.x - nodeB1.x, nodeA1.y - nodeA0.y, nodeB0.y - nodeB1.y, nodeB0.x - nodeA0.x, nodeB0.y - nodeA0.y, result))
|
|
1028
1083
|
return result;
|
|
1029
1084
|
return undefined;
|
|
1030
1085
|
}
|
|
1031
1086
|
/**
|
|
1032
|
-
*
|
|
1087
|
+
* Compute fractional position (possibly outside 0..1) of the intersection of a horizontal line with an edge.
|
|
1033
1088
|
* * If the edge is horizontal with (approximate) identical y, return the node.
|
|
1034
|
-
* * If the edge is horizontal with different y, return undefined
|
|
1035
|
-
*
|
|
1036
|
-
* @param
|
|
1089
|
+
* * If the edge is horizontal with different y, return `undefined`.
|
|
1090
|
+
* @param node0 base node of edge.
|
|
1091
|
+
* @param y y coordinate of the horizontal line.
|
|
1037
1092
|
*/
|
|
1038
1093
|
static horizontalScanFraction(node0, y) {
|
|
1039
1094
|
const node1 = node0.faceSuccessor;
|
|
@@ -1042,13 +1097,16 @@ class HalfEdge {
|
|
|
1042
1097
|
return node0;
|
|
1043
1098
|
if (Geometry.isSameCoordinate(dy, 0.0))
|
|
1044
1099
|
return undefined;
|
|
1100
|
+
// parametric equation of line is (1-t)y0 + ty1 which is equal to y at the intersection so
|
|
1101
|
+
// (1-t)y0 + ty1 = y or t = (y-y0)/(y1-y0)
|
|
1045
1102
|
return Geometry.conditionalDivideFraction(y - node0.y, dy);
|
|
1046
1103
|
}
|
|
1047
1104
|
/**
|
|
1048
|
-
*
|
|
1049
|
-
* * If
|
|
1050
|
-
* * If the edge is
|
|
1051
|
-
* @param node0
|
|
1105
|
+
* Compute fractional position (inside 0..1) of the intersection of a horizontal line with an edge.
|
|
1106
|
+
* * If fractional position is outside 0..1, return `undefined`.
|
|
1107
|
+
* * If the edge is horizontal return `undefined` (no test for horizontal at y).
|
|
1108
|
+
* @param node0 base node of edge.
|
|
1109
|
+
* @param y y coordinate of the horizontal line.
|
|
1052
1110
|
*/
|
|
1053
1111
|
static horizontalScanFraction01(node0, y) {
|
|
1054
1112
|
const node1 = node0.faceSuccessor;
|
|
@@ -1063,12 +1121,12 @@ class HalfEdge {
|
|
|
1063
1121
|
return undefined;
|
|
1064
1122
|
}
|
|
1065
1123
|
/**
|
|
1066
|
-
* Copy various data from source to this
|
|
1067
|
-
* @param source
|
|
1068
|
-
* @param
|
|
1069
|
-
* @param copyVertexData true to copy data belonging to the vertex
|
|
1070
|
-
* @param
|
|
1071
|
-
* @param copyFaceData true to copy faceTag
|
|
1124
|
+
* Copy various data from source to `this`.
|
|
1125
|
+
* @param source source half edge.
|
|
1126
|
+
* @param copyXYZ true to copy xyz coordinates.
|
|
1127
|
+
* @param copyVertexData true to copy data belonging to the vertex (i.e. the `i` member).
|
|
1128
|
+
* @param copyEdgeData true to copy data belonging to the edge (i.e. edge masks, `edgeTag`).
|
|
1129
|
+
* @param copyFaceData true to copy `faceTag`.
|
|
1072
1130
|
*/
|
|
1073
1131
|
copyDataFrom(source, copyXYZ, copyVertexData, copyEdgeData, copyFaceData) {
|
|
1074
1132
|
if (copyXYZ) {
|
|
@@ -1088,13 +1146,16 @@ class HalfEdge {
|
|
|
1088
1146
|
}
|
|
1089
1147
|
}
|
|
1090
1148
|
}
|
|
1091
|
-
HalfEdge._edgePropertyMasks = [HalfEdgeMask.BOUNDARY_EDGE, HalfEdgeMask.EXTERIOR, HalfEdgeMask.PRIMARY_EDGE, HalfEdgeMask.NULL_FACE];
|
|
1092
1149
|
HalfEdge._totalNodesCreated = 0;
|
|
1150
|
+
/** Edge property masks. */
|
|
1151
|
+
HalfEdge._edgePropertyMasks = [
|
|
1152
|
+
HalfEdgeMask.BOUNDARY_EDGE, HalfEdgeMask.EXTERIOR, HalfEdgeMask.PRIMARY_EDGE, HalfEdgeMask.NULL_FACE,
|
|
1153
|
+
];
|
|
1093
1154
|
export { HalfEdge };
|
|
1094
1155
|
/**
|
|
1095
1156
|
* A HalfEdgeGraph has:
|
|
1096
|
-
* * An array of (pointers to
|
|
1097
|
-
* * A pool of masks for grab/drop
|
|
1157
|
+
* * An array of (pointers to) HalfEdge objects.
|
|
1158
|
+
* * A pool of masks for grab/drop used by algorithms.
|
|
1098
1159
|
* @internal
|
|
1099
1160
|
*/
|
|
1100
1161
|
export class HalfEdgeGraph {
|
|
@@ -1103,7 +1164,8 @@ export class HalfEdgeGraph {
|
|
|
1103
1164
|
this.allHalfEdges = [];
|
|
1104
1165
|
this._maskManager = MaskManager.create(HalfEdgeMask.ALL_GRAB_DROP_MASKS);
|
|
1105
1166
|
}
|
|
1106
|
-
/**
|
|
1167
|
+
/**
|
|
1168
|
+
* Ask for a mask (from the graph's free pool) for caller's use.
|
|
1107
1169
|
* * Optionally clear the mask throughout the graph.
|
|
1108
1170
|
*/
|
|
1109
1171
|
grabMask(clearInAllHalfEdges = true) {
|
|
@@ -1113,49 +1175,49 @@ export class HalfEdgeGraph {
|
|
|
1113
1175
|
}
|
|
1114
1176
|
return mask;
|
|
1115
1177
|
}
|
|
1178
|
+
/** Return `mask` to the free pool. */
|
|
1179
|
+
dropMask(mask) {
|
|
1180
|
+
this._maskManager.dropMask(mask);
|
|
1181
|
+
}
|
|
1116
1182
|
/**
|
|
1117
|
-
*
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
*
|
|
1122
|
-
* * The two edges are joined as edgeMate pair.
|
|
1123
|
-
* * The two edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
1124
|
-
* * The two edges are added to the graph's HalfEdge set
|
|
1125
|
-
* @returns Return pointer to the first half edge created.
|
|
1183
|
+
* Create 2 half edges forming 2 vertices, 1 edge, and 1 face.
|
|
1184
|
+
* * The two half edges are joined as edgeMate pair.
|
|
1185
|
+
* * The two half edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
1186
|
+
* * The two half edges are added to the graph's HalfEdge set.
|
|
1187
|
+
* @returns pointer to the first half edge created.
|
|
1126
1188
|
*/
|
|
1127
1189
|
createEdgeXYZXYZ(xA = 0, yA = 0, zA = 0, iA = 0, xB = 0, yB = 0, zB = 0, iB = 0) {
|
|
1128
|
-
|
|
1129
|
-
return a;
|
|
1190
|
+
return HalfEdge.createHalfEdgePairWithCoordinates(xA, yA, zA, iA, xB, yB, zB, iB, this.allHalfEdges);
|
|
1130
1191
|
}
|
|
1131
1192
|
/**
|
|
1132
|
-
*
|
|
1133
|
-
* * The two edges are joined as edgeMate pair.
|
|
1134
|
-
* * The two edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
1135
|
-
* * The two edges are added to the graph's HalfEdge set
|
|
1193
|
+
* Create 2 half edges forming 2 vertices, 1 edge, and 1 face.
|
|
1194
|
+
* * The two half edges are joined as edgeMate pair.
|
|
1195
|
+
* * The two half edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
1196
|
+
* * The two half edges are added to the graph's HalfEdge set.
|
|
1136
1197
|
* * Coordinates are set to zero.
|
|
1137
|
-
* *
|
|
1138
|
-
* @returns
|
|
1198
|
+
* * IDs are installed in the two half edges.
|
|
1199
|
+
* @returns pointer to the first half edge created, with ID set to iA.
|
|
1139
1200
|
*/
|
|
1140
1201
|
createEdgeIdId(iA = 0, iB = 0) {
|
|
1141
|
-
|
|
1142
|
-
return a;
|
|
1202
|
+
return HalfEdge.createHalfEdgePairWithCoordinates(0.0, 0.0, 0.0, iA, 0.0, 0.0, 0.0, iB, this.allHalfEdges);
|
|
1143
1203
|
}
|
|
1144
1204
|
/**
|
|
1145
|
-
*
|
|
1146
|
-
* @returns
|
|
1205
|
+
* Create an edge from coordinates x,y,z connected to the graph at the vertex of the given `node`.
|
|
1206
|
+
* @returns pointer to the dangling node at x,y,z.
|
|
1147
1207
|
*/
|
|
1148
1208
|
createEdgeXYZHalfEdge(xA = 0, yA = 0, zA = 0, iA = 0, node, iB = 0) {
|
|
1209
|
+
// Visualization can be found at geometry/internaldocs/Graph.md
|
|
1149
1210
|
const a = HalfEdge.createHalfEdgePairWithCoordinates(xA, yA, zA, iA, node.x, node.y, node.z, iB, this.allHalfEdges);
|
|
1150
1211
|
const b = a.faceSuccessor;
|
|
1151
1212
|
HalfEdge.pinch(node, b);
|
|
1152
1213
|
return a;
|
|
1153
1214
|
}
|
|
1154
1215
|
/**
|
|
1155
|
-
*
|
|
1156
|
-
* @returns
|
|
1216
|
+
* Create an edge from the vertex of `nodeA` to the vertex of `nodeB`.
|
|
1217
|
+
* @returns pointer to the new half edge at the vertex of `nodeA`.
|
|
1157
1218
|
*/
|
|
1158
1219
|
createEdgeHalfEdgeHalfEdge(nodeA, idA, nodeB, idB = 0) {
|
|
1220
|
+
// Visualization can be found at geometry/internaldocs/Graph.md
|
|
1159
1221
|
const a = HalfEdge.createHalfEdgePairWithCoordinates(nodeA.x, nodeA.y, nodeA.z, idA, nodeB.x, nodeB.y, nodeB.z, idB, this.allHalfEdges);
|
|
1160
1222
|
const b = a.faceSuccessor;
|
|
1161
1223
|
HalfEdge.pinch(nodeA, a);
|
|
@@ -1163,56 +1225,56 @@ export class HalfEdgeGraph {
|
|
|
1163
1225
|
return a;
|
|
1164
1226
|
}
|
|
1165
1227
|
/**
|
|
1166
|
-
*
|
|
1167
|
-
* * The two edges are joined as edgeMate pair.
|
|
1168
|
-
* * The two edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
1169
|
-
* * The two edges are added to the graph's HalfEdge set
|
|
1170
|
-
* @returns
|
|
1228
|
+
* Create 2 half edges forming 2 vertices, 1 edge, and 1 face
|
|
1229
|
+
* * The two half edges are joined as edgeMate pair.
|
|
1230
|
+
* * The two half edges are a 2-half-edge face loop in both the faceSuccessor and facePredecessor directions.
|
|
1231
|
+
* * The two half edges are added to the graph's HalfEdge set.
|
|
1232
|
+
* @returns pointer to the first half edge created, with coordinates `xyz0`.
|
|
1171
1233
|
*/
|
|
1172
1234
|
createEdgeXYAndZ(xyz0, id0, xyz1, id1) {
|
|
1173
|
-
|
|
1174
|
-
return a;
|
|
1235
|
+
return HalfEdge.createHalfEdgePairWithCoordinates(xyz0.x, xyz0.y, xyz0.z, id0, xyz1.x, xyz1.y, xyz1.z, id1, this.allHalfEdges);
|
|
1175
1236
|
}
|
|
1176
1237
|
/**
|
|
1177
|
-
*
|
|
1178
|
-
* *
|
|
1179
|
-
* *
|
|
1180
|
-
* *
|
|
1181
|
-
* *
|
|
1182
|
-
*
|
|
1238
|
+
* Create a new vertex within the edge beginning at `base`.
|
|
1239
|
+
* * This creates two new nodes in their own vertex loop.
|
|
1240
|
+
* * If the base is undefined, create a single-edge loop.
|
|
1241
|
+
* * Existing nodes stay in their face and vertex loops and retain xyz and i values.
|
|
1242
|
+
* * Unlike [[pinch]], this breaks the edgeMate pairing of the input edge:
|
|
1243
|
+
* each node of the input edge gets a new node as its edge mate.
|
|
1244
|
+
* * On each side of the edge, if edgeTag is present, it is copied to the new node on that side.
|
|
1245
|
+
* @returns reference to the half edge created, the new face successor of `base`.
|
|
1183
1246
|
*/
|
|
1184
1247
|
splitEdge(base, xA = 0, yA = 0, zA = 0, iA = 0) {
|
|
1185
|
-
|
|
1186
|
-
return he;
|
|
1248
|
+
return HalfEdge.splitEdge(base, xA, yA, zA, iA, this.allHalfEdges);
|
|
1187
1249
|
}
|
|
1188
1250
|
/**
|
|
1189
|
-
*
|
|
1190
|
-
* *
|
|
1191
|
-
* *
|
|
1192
|
-
* *
|
|
1193
|
-
*
|
|
1194
|
-
* * New
|
|
1195
|
-
* *
|
|
1196
|
-
* @returns
|
|
1251
|
+
* Create a new sliver face "inside" an existing edge.
|
|
1252
|
+
* * This creates two nodes that are each face predecessor and successor to the other.
|
|
1253
|
+
* * Existing nodes stay in their face and vertex loops and retain xyz and i values.
|
|
1254
|
+
* * Unlike [[pinch]], this breaks the edgeMate pairing of the input edge:
|
|
1255
|
+
* each node of the input edge gets a new node as its edge mate.
|
|
1256
|
+
* * New nodes get the xyz and i values shared by the nodes in the vertex loops into which they are placed.
|
|
1257
|
+
* * New nodes' faceTag and edgeTag are `undefined`.
|
|
1258
|
+
* @returns reference to the half edge created in the vertex loop of baseA.
|
|
1197
1259
|
*/
|
|
1198
1260
|
splitEdgeCreateSliverFace(base) {
|
|
1199
|
-
|
|
1200
|
-
return he;
|
|
1261
|
+
return HalfEdge.splitEdgeCreateSliverFace(base, this.allHalfEdges);
|
|
1201
1262
|
}
|
|
1202
1263
|
/**
|
|
1203
|
-
*
|
|
1204
|
-
* *
|
|
1205
|
-
* *
|
|
1206
|
-
* *
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1264
|
+
* Create a new vertex within the edge beginning at `base`, with coordinates specified by a fraction along the edge.
|
|
1265
|
+
* * This creates two new nodes in their own vertex loop.
|
|
1266
|
+
* * Existing nodes stay in their face and vertex loops and retain xyz and i values.
|
|
1267
|
+
* * Unlike [[pinch]], this breaks the edgeMate pairing of the input edge:
|
|
1268
|
+
* each node of the input edge gets a new node as its edge mate.
|
|
1269
|
+
* * On each side of the edge, if edgeTag is present, it is copied to the new node on that side.
|
|
1270
|
+
* @returns reference to the half edge created, the new face successor of `base`.
|
|
1209
1271
|
*/
|
|
1210
1272
|
splitEdgeAtFraction(base, fraction) {
|
|
1211
|
-
|
|
1212
|
-
return he;
|
|
1273
|
+
return HalfEdge.splitEdge(base, base.fractionToX(fraction), base.fractionToY(fraction), base.fractionToZ(fraction), 0, this.allHalfEdges);
|
|
1213
1274
|
}
|
|
1214
|
-
/**
|
|
1215
|
-
*
|
|
1275
|
+
/**
|
|
1276
|
+
* This is a destructor-like action that eliminates all inter-connections among the graph's nodes.
|
|
1277
|
+
* After this is called, the graph is unusable.
|
|
1216
1278
|
*/
|
|
1217
1279
|
decommission() {
|
|
1218
1280
|
for (const node of this.allHalfEdges) {
|
|
@@ -1221,8 +1283,9 @@ export class HalfEdgeGraph {
|
|
|
1221
1283
|
this.allHalfEdges.length = 0;
|
|
1222
1284
|
this.allHalfEdges = undefined;
|
|
1223
1285
|
}
|
|
1224
|
-
/**
|
|
1225
|
-
*
|
|
1286
|
+
/**
|
|
1287
|
+
* Create two nodes of a new edge.
|
|
1288
|
+
* @returns the reference to the new node at (x0,y0).
|
|
1226
1289
|
*/
|
|
1227
1290
|
addEdgeXY(x0, y0, x1, y1) {
|
|
1228
1291
|
const baseNode = HalfEdge.createEdgeXYXY(this._numNodesCreated, x0, y0, this._numNodesCreated + 1, x1, y1);
|
|
@@ -1231,17 +1294,17 @@ export class HalfEdgeGraph {
|
|
|
1231
1294
|
this.allHalfEdges.push(baseNode.faceSuccessor);
|
|
1232
1295
|
return baseNode;
|
|
1233
1296
|
}
|
|
1234
|
-
/** Clear selected bits in all nodes of the graph. */
|
|
1297
|
+
/** Clear selected `mask` bits in all nodes of the graph. */
|
|
1235
1298
|
clearMask(mask) {
|
|
1236
1299
|
for (const node of this.allHalfEdges)
|
|
1237
1300
|
node.maskBits &= ~mask;
|
|
1238
1301
|
}
|
|
1239
|
-
/** Set selected bits in all nodes of the graph. */
|
|
1302
|
+
/** Set selected `mask` bits in all nodes of the graph. */
|
|
1240
1303
|
setMask(mask) {
|
|
1241
1304
|
for (const node of this.allHalfEdges)
|
|
1242
1305
|
node.maskBits |= mask;
|
|
1243
1306
|
}
|
|
1244
|
-
/**
|
|
1307
|
+
/** Toggle selected `mask` bits in all nodes of the graph. */
|
|
1245
1308
|
reverseMask(mask) {
|
|
1246
1309
|
for (const node of this.allHalfEdges) {
|
|
1247
1310
|
node.maskBits ^= mask;
|
|
@@ -1258,10 +1321,11 @@ export class HalfEdgeGraph {
|
|
|
1258
1321
|
n++;
|
|
1259
1322
|
return n;
|
|
1260
1323
|
}
|
|
1261
|
-
/**
|
|
1262
|
-
*
|
|
1324
|
+
/**
|
|
1325
|
+
* Return an array of LineSegment3d.
|
|
1326
|
+
* * The array has one segment per edge.
|
|
1263
1327
|
* * The coordinates are taken from a node and its face successor.
|
|
1264
|
-
* * On each edge, the line segment
|
|
1328
|
+
* * On each edge, the line segment starts at the HalfEdge with lower ID than its edgeMate.
|
|
1265
1329
|
*/
|
|
1266
1330
|
collectSegments() {
|
|
1267
1331
|
const segments = [];
|
|
@@ -1271,24 +1335,27 @@ export class HalfEdgeGraph {
|
|
|
1271
1335
|
}
|
|
1272
1336
|
return segments;
|
|
1273
1337
|
}
|
|
1274
|
-
/** Returns the number of vertex loops in a graph structure */
|
|
1338
|
+
/** Returns the number of vertex loops in a graph structure. */
|
|
1275
1339
|
countVertexLoops() {
|
|
1276
1340
|
this.clearMask(HalfEdgeMask.VISITED);
|
|
1277
1341
|
let count = 0;
|
|
1278
|
-
this.announceVertexLoops((_graph, _seed) => {
|
|
1342
|
+
this.announceVertexLoops((_graph, _seed) => {
|
|
1343
|
+
count++;
|
|
1344
|
+
return true;
|
|
1345
|
+
});
|
|
1279
1346
|
return count;
|
|
1280
1347
|
}
|
|
1281
|
-
/** Returns the number of face loops */
|
|
1348
|
+
/** Returns the number of face loops in a graph structure. */
|
|
1282
1349
|
countFaceLoops() {
|
|
1283
1350
|
this.clearMask(HalfEdgeMask.VISITED);
|
|
1284
1351
|
let count = 0;
|
|
1285
|
-
this.announceFaceLoops((_graph, _seed) => {
|
|
1352
|
+
this.announceFaceLoops((_graph, _seed) => {
|
|
1353
|
+
count++;
|
|
1354
|
+
return true;
|
|
1355
|
+
});
|
|
1286
1356
|
return count;
|
|
1287
1357
|
}
|
|
1288
|
-
/**
|
|
1289
|
-
* Returns the number of face loops satisfying a filter function with mask argument.
|
|
1290
|
-
*
|
|
1291
|
-
*/
|
|
1358
|
+
/** Returns the number of face loops satisfying a filter function with mask argument. */
|
|
1292
1359
|
countFaceLoopsWithMaskFilter(filter, mask) {
|
|
1293
1360
|
this.clearMask(HalfEdgeMask.VISITED);
|
|
1294
1361
|
let count = 0;
|
|
@@ -1299,32 +1366,47 @@ export class HalfEdgeGraph {
|
|
|
1299
1366
|
});
|
|
1300
1367
|
return count;
|
|
1301
1368
|
}
|
|
1302
|
-
/** Returns an array of nodes, where each node represents a starting point of a
|
|
1303
|
-
|
|
1369
|
+
/** Returns an array of nodes, where each node represents a starting point of a vertex loop. */
|
|
1370
|
+
collectVertexLoops() {
|
|
1371
|
+
const returnArray = [];
|
|
1372
|
+
this.announceVertexLoops((_graph, node) => {
|
|
1373
|
+
returnArray.push(node);
|
|
1374
|
+
return true;
|
|
1375
|
+
});
|
|
1376
|
+
return returnArray;
|
|
1377
|
+
}
|
|
1378
|
+
/** Returns an array of nodes, where each node represents a starting point of a face loop. */
|
|
1304
1379
|
collectFaceLoops() {
|
|
1305
1380
|
const returnArray = [];
|
|
1306
|
-
this.announceFaceLoops((_graph, node) => {
|
|
1381
|
+
this.announceFaceLoops((_graph, node) => {
|
|
1382
|
+
returnArray.push(node);
|
|
1383
|
+
return true;
|
|
1384
|
+
});
|
|
1307
1385
|
return returnArray;
|
|
1308
1386
|
}
|
|
1309
|
-
/**
|
|
1387
|
+
/**
|
|
1388
|
+
* Visit each vertex loop of the graph once.
|
|
1389
|
+
* * Call the `announceVertex` function.
|
|
1390
|
+
* * Continue search if `announceVertex(graph, node)` returns `true`.
|
|
1391
|
+
* * Terminate search if `announceVertex(graph, node)` returns `false`.
|
|
1392
|
+
* @param announceVertex function to apply at one node of each vertex.
|
|
1310
1393
|
*/
|
|
1311
|
-
|
|
1394
|
+
announceVertexLoops(announceVertex) {
|
|
1312
1395
|
this.clearMask(HalfEdgeMask.VISITED);
|
|
1313
|
-
const returnArray = [];
|
|
1314
1396
|
for (const node of this.allHalfEdges) {
|
|
1315
1397
|
if (node.getMask(HalfEdgeMask.VISITED))
|
|
1316
1398
|
continue;
|
|
1317
|
-
returnArray.push(node);
|
|
1318
1399
|
node.setMaskAroundVertex(HalfEdgeMask.VISITED);
|
|
1400
|
+
if (!announceVertex(this, node))
|
|
1401
|
+
break;
|
|
1319
1402
|
}
|
|
1320
|
-
return returnArray;
|
|
1321
1403
|
}
|
|
1322
1404
|
/**
|
|
1323
|
-
*
|
|
1324
|
-
* * Call the announceFace function
|
|
1325
|
-
* *
|
|
1326
|
-
* *
|
|
1327
|
-
* @param
|
|
1405
|
+
* Visit each facet of the graph once.
|
|
1406
|
+
* * Call the `announceFace` function.
|
|
1407
|
+
* * Continue search if `announceFace(graph, node)` returns `true`.
|
|
1408
|
+
* * Terminate search if `announceFace(graph, node)` returns `false`.
|
|
1409
|
+
* @param announceFace function to apply at one node of each face.
|
|
1328
1410
|
*/
|
|
1329
1411
|
announceFaceLoops(announceFace) {
|
|
1330
1412
|
this.clearMask(HalfEdgeMask.VISITED);
|
|
@@ -1337,13 +1419,13 @@ export class HalfEdgeGraph {
|
|
|
1337
1419
|
}
|
|
1338
1420
|
}
|
|
1339
1421
|
/**
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1422
|
+
* Visit each edge of the graph once.
|
|
1423
|
+
* * Call the `announceEdge` function.
|
|
1424
|
+
* * The edge mate will NOT appear in an announceEdge call.
|
|
1425
|
+
* * Continue search if `announceEdge(graph, node)` returns `true`.
|
|
1426
|
+
* * Terminate search if `announceEdge(graph, node)` returns `false`.
|
|
1427
|
+
* @param announceEdge function to apply at one node of each edge.
|
|
1428
|
+
*/
|
|
1347
1429
|
announceEdges(announceEdge) {
|
|
1348
1430
|
this.clearMask(HalfEdgeMask.VISITED);
|
|
1349
1431
|
for (const node of this.allHalfEdges) {
|
|
@@ -1357,28 +1439,11 @@ export class HalfEdgeGraph {
|
|
|
1357
1439
|
}
|
|
1358
1440
|
}
|
|
1359
1441
|
/**
|
|
1360
|
-
*
|
|
1361
|
-
* * Call the
|
|
1362
|
-
* *
|
|
1363
|
-
* *
|
|
1364
|
-
* @param
|
|
1365
|
-
*/
|
|
1366
|
-
announceVertexLoops(announceVertex) {
|
|
1367
|
-
this.clearMask(HalfEdgeMask.VISITED);
|
|
1368
|
-
for (const node of this.allHalfEdges) {
|
|
1369
|
-
if (node.getMask(HalfEdgeMask.VISITED))
|
|
1370
|
-
continue;
|
|
1371
|
-
node.setMaskAroundVertex(HalfEdgeMask.VISITED);
|
|
1372
|
-
if (!announceVertex(this, node))
|
|
1373
|
-
break;
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* * Visit each half edge (node) of the graph once.
|
|
1378
|
-
* * Call the announceNode function
|
|
1379
|
-
* * continue search if announceNode(graph, node) returns true
|
|
1380
|
-
* * terminate search if announce face (graph, node) returns false
|
|
1381
|
-
* @param announceNode function to apply at one node of each face.
|
|
1442
|
+
* Visit each half edge (node) of the graph once.
|
|
1443
|
+
* * Call the `announceNode` function.
|
|
1444
|
+
* * Continue search if `announceNode(graph, node)` returns `true`.
|
|
1445
|
+
* * Terminate search if `announceFace(graph, node)` returns `false`.
|
|
1446
|
+
* @param announceNode function to apply at each node.
|
|
1382
1447
|
*/
|
|
1383
1448
|
announceNodes(announceNode) {
|
|
1384
1449
|
for (const node of this.allHalfEdges) {
|
|
@@ -1386,8 +1451,10 @@ export class HalfEdgeGraph {
|
|
|
1386
1451
|
break;
|
|
1387
1452
|
}
|
|
1388
1453
|
}
|
|
1389
|
-
/** Return the number of nodes in the graph */
|
|
1390
|
-
countNodes() {
|
|
1454
|
+
/** Return the number of nodes in the graph. */
|
|
1455
|
+
countNodes() {
|
|
1456
|
+
return this.allHalfEdges.length;
|
|
1457
|
+
}
|
|
1391
1458
|
/** Apply transform to the xyz coordinates in the graph. */
|
|
1392
1459
|
transformInPlace(transform) {
|
|
1393
1460
|
for (const node of this.allHalfEdges) {
|
|
@@ -1395,8 +1462,8 @@ export class HalfEdgeGraph {
|
|
|
1395
1462
|
}
|
|
1396
1463
|
}
|
|
1397
1464
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
* @param deleteThisNode returns true to delete the corresponding
|
|
1465
|
+
* Disconnect and delete all nodes that satisfy a filter condition.
|
|
1466
|
+
* @param deleteThisNode returns true to delete the corresponding node. Should act symmetrically on the edgeMate.
|
|
1400
1467
|
* @returns the number of nodes deleted (twice the number of deleted edges).
|
|
1401
1468
|
*/
|
|
1402
1469
|
yankAndDeleteEdges(deleteThisNode) {
|
|
@@ -1405,9 +1472,9 @@ export class HalfEdgeGraph {
|
|
|
1405
1472
|
for (let i = 0; i < numTotal; i++) {
|
|
1406
1473
|
const candidate = this.allHalfEdges[i];
|
|
1407
1474
|
if (!deleteThisNode(candidate))
|
|
1408
|
-
this.allHalfEdges[numAccepted++] = candidate;
|
|
1475
|
+
this.allHalfEdges[numAccepted++] = candidate; // overwrite a previously "deleted node"
|
|
1409
1476
|
else
|
|
1410
|
-
candidate.yankFromVertexLoop(); //
|
|
1477
|
+
candidate.yankFromVertexLoop(); // assume callback symmetry so we eventually yank the mate
|
|
1411
1478
|
}
|
|
1412
1479
|
const numDeleted = numTotal - numAccepted;
|
|
1413
1480
|
this.allHalfEdges.length = numAccepted;
|