// do full tree traversal below iBox to make sure that all child points are contained void debug_check_child_points_in_box(int iBox) { AxisAlignedBox3d box = get_box(iBox); var t = new TreeTraversal() { NextPointF = (vID) => { Vector3d v = points.GetVertex(vID); if (box.Contains(v) == false) { Util.gBreakToDebugger(); } } }; tree_traversal(iBox, 0, t); }
double box_ray_intersect_t(int iBox, Ray3d ray) { Vector3d c = box_centers[iBox]; Vector3d e = box_extents[iBox]; AxisAlignedBox3d box = new AxisAlignedBox3d(c - e, c + e); IntrRay3AxisAlignedBox3 intr = new IntrRay3AxisAlignedBox3(ray, box); if (intr.Find()) { return(intr.RayParam0); } else { Debug.Assert(intr.Result != IntersectionResult.InvalidQuery); return(double.MaxValue); } }
// accumulate point counts and track each box-parent index. // also checks that points are contained in boxes private void test_coverage(int[] point_counts, int[] parent_indices, int iBox) { int idx = box_to_index[iBox]; debug_check_child_points_in_box(iBox); if (idx < points_end) { // point-list case, array is [N t1 t2 ... tN] int n = index_list[idx]; AxisAlignedBox3d box = get_box(iBox); for (int i = 1; i <= n; ++i) { int vi = index_list[idx + i]; point_counts[vi]++; Vector3d v = points.GetVertex(vi); if (!box.Contains(v)) { Util.gBreakToDebugger(); } } } else { int i0 = index_list[idx]; if (i0 < 0) { // negative index means we only have one 'child' box to descend into i0 = (-i0) - 1; parent_indices[i0] = iBox; test_coverage(point_counts, parent_indices, i0); } else { // positive index, two sequential child box indices to descend into i0 = i0 - 1; parent_indices[i0] = iBox; test_coverage(point_counts, parent_indices, i0); int i1 = index_list[idx + 1]; i1 = i1 - 1; parent_indices[i1] = iBox; test_coverage(point_counts, parent_indices, i1); } } }
public AxisAlignedBox3d ToAABB() { // [TODO] probably more efficient way to do this...at minimum can move center-shift // to after the containments... Vector3d extAxis0 = Extent.x * AxisX; Vector3d extAxis1 = Extent.y * AxisY; Vector3d extAxis2 = Extent.z * AxisZ; AxisAlignedBox3d result = new AxisAlignedBox3d(Center - extAxis0 - extAxis1 - extAxis2); result.Contain(Center + extAxis0 - extAxis1 - extAxis2); result.Contain(Center + extAxis0 + extAxis1 - extAxis2); result.Contain(Center - extAxis0 + extAxis1 - extAxis2); result.Contain(Center - extAxis0 - extAxis1 + extAxis2); result.Contain(Center + extAxis0 - extAxis1 + extAxis2); result.Contain(Center + extAxis0 + extAxis1 + extAxis2); result.Contain(Center - extAxis0 + extAxis1 + extAxis2); return(result); }
static public bool DoClipping(ref double t0, ref double t1, Vector3d origin, Vector3d direction, AxisAlignedBox3d box, bool solid, ref int quantity, ref Vector3d point0, ref Vector3d point1, ref IntersectionType intrType) { Vector3d BOrigin = origin - box.Center; Vector3d extent = box.Extents; double saveT0 = t0, saveT1 = t1; bool notAllClipped = Clip(+direction.x, -BOrigin.x - extent.x, ref t0, ref t1) && Clip(-direction.x, +BOrigin.x - extent.x, ref t0, ref t1) && Clip(+direction.y, -BOrigin.y - extent.y, ref t0, ref t1) && Clip(-direction.y, +BOrigin.y - extent.y, ref t0, ref t1) && Clip(+direction.z, -BOrigin.z - extent.z, ref t0, ref t1) && Clip(-direction.z, +BOrigin.z - extent.z, ref t0, ref t1); if (notAllClipped && (solid || t0 != saveT0 || t1 != saveT1)) { if (t1 > t0) { intrType = IntersectionType.Segment; quantity = 2; point0 = origin + t0 * direction; point1 = origin + t1 * direction; } else { intrType = IntersectionType.Point; quantity = 1; point0 = origin + t0 * direction; } } else { quantity = 0; intrType = IntersectionType.Empty; } return(intrType != IntersectionType.Empty); }
public static AxisAlignedBox3d BoundsV(IMesh mesh, IEnumerable <int> vertexIndices, Func <Vector3d, Vector3d> TransformF = null) { AxisAlignedBox3d bounds = AxisAlignedBox3d.Empty; if (TransformF == null) { foreach (int vid in vertexIndices) { bounds.Contain(mesh.GetVertex(vid)); } } else { foreach (int vid in vertexIndices) { bounds.Contain(TransformF(mesh.GetVertex(vid))); } } return(bounds); }
public void Sample(BoundedImplicitFunction3d f, double expandRadius = 0) { AxisAlignedBox3d bounds = f.Bounds(); Vector3d expand = expandRadius * Vector3d.One; Vector3i gridMin = Indexer.ToGrid(bounds.Min - expand), gridMax = Indexer.ToGrid(bounds.Max + expand) + Vector3i.One; gridMin = GridBounds.ClampExclusive(gridMin); gridMax = GridBounds.ClampExclusive(gridMax); AxisAlignedBox3i gridbox = new AxisAlignedBox3i(gridMin, gridMax); switch (CombineMode) { case CombineModes.DistanceMinUnion: sample_min(f, gridbox.IndicesInclusive()); break; } }
public static AxisAlignedBox3d Bounds(DMesh3 mesh, Func <Vector3d, Vector3d> TransformF) { AxisAlignedBox3d bounds = AxisAlignedBox3d.Empty; if (TransformF == null) { foreach (Vector3d v in mesh.Vertices()) { bounds.Contain(v); } } else { foreach (Vector3d v in mesh.Vertices()) { Vector3d vT = TransformF(v); bounds.Contain(ref vT); } } return(bounds); }
public static AxisAlignedBox3d Bounds(IMesh mesh, Func <Vector3d, Vector3d> TransformF) { AxisAlignedBox3d bounds = AxisAlignedBox3d.Empty; if (TransformF == null) { foreach (int vID in mesh.VertexIndices()) { bounds.Contain(mesh.GetVertex(vID)); } } else { foreach (int vID in mesh.VertexIndices()) { Vector3d vT = TransformF(mesh.GetVertex(vID)); bounds.Contain(ref vT); } } return(bounds); }
/// <summary> /// Find intersection of ray with AABB, without having to construct any new classes. /// Returns ray T-value of first intersection (or double.MaxVlaue on miss) /// </summary> public static bool FindRayIntersectT(ref Ray3d ray, ref AxisAlignedBox3d box, out double RayParam) { double RayParam0 = 0.0; double RayParam1 = double.MaxValue; int Quantity = 0; Vector3d Point0 = Vector3d.Zero; Vector3d Point1 = Vector3d.Zero; IntersectionType Type = IntersectionType.Empty; IntrLine3AxisAlignedBox3.DoClipping(ref RayParam0, ref RayParam1, ref ray.Origin, ref ray.Direction, ref box, true, ref Quantity, ref Point0, ref Point1, ref Type); if (Type != IntersectionType.Empty) { RayParam = RayParam0; return(true); } else { RayParam = double.MaxValue; return(false); } }
/// <summary> /// calculate extents of mesh along axes of frame, with optional transform /// </summary> public static AxisAlignedBox3d BoundsInFrame(DMesh3 mesh, Frame3f frame, Func <Vector3d, Vector3d> TransformF = null) { AxisAlignedBox3d bounds = AxisAlignedBox3d.Empty; if (TransformF == null) { foreach (Vector3d v in mesh.Vertices()) { Vector3d fv = frame.ToFrameP(v); bounds.Contain(ref fv); } } else { foreach (Vector3d v in mesh.Vertices()) { Vector3d vT = TransformF(v); Vector3d fv = frame.ToFrameP(ref vT); bounds.Contain(ref fv); } } return(bounds); }
public void Compute() { // figure out origin & dimensions AxisAlignedBox3d bounds = Mesh.CachedBounds; float fBufferWidth = 2 * ExactBandWidth * CellSize; grid_origin = (Vector3f)bounds.Min - fBufferWidth * Vector3f.One - (Vector3f)ExpandBounds; Vector3f max = (Vector3f)bounds.Max + fBufferWidth * Vector3f.One + (Vector3f)ExpandBounds; int ni = (int)((max.x - grid_origin.x) / CellSize) + 1; int nj = (int)((max.y - grid_origin.y) / CellSize) + 1; int nk = (int)((max.z - grid_origin.z) / CellSize) + 1; grid = new DenseGrid3f(); if (UseParallel) { make_level_set3_parallel(grid_origin, CellSize, ni, nj, nk, grid, ExactBandWidth); } else { make_level_set3(grid_origin, CellSize, ni, nj, nk, grid, ExactBandWidth); } }
protected bool check_for_cracks(DMesh3 mesh, out int boundary_edge_count, double crack_tol = MathUtil.ZeroTolerancef) { boundary_edge_count = 0; var boundary_verts = new MeshVertexSelection(mesh); foreach (int eid in mesh.BoundaryEdgeIndices()) { Index2i ev = mesh.GetEdgeV(eid); boundary_verts.Select(ev.a); boundary_verts.Select(ev.b); boundary_edge_count++; } if (boundary_verts.Count == 0) { return(false); } AxisAlignedBox3d bounds = mesh.CachedBounds; var borderV = new PointHashGrid3d <int>(bounds.MaxDim / 128, -1); foreach (int vid in boundary_verts) { Vector3d v = mesh.GetVertex(vid); var result = borderV.FindNearestInRadius(v, crack_tol, (existing_vid) => { return(v.Distance(mesh.GetVertex(existing_vid))); }); if (result.Key != -1) { return(true); // we found a crack vertex! } borderV.InsertPoint(vid, v); } // found no cracks return(false); }
public bool Intersects(AxisAlignedBox3d box) { return(!((box.Max.x <= Min.x) || (box.Min.x >= Max.x) || (box.Max.y <= Min.y) || (box.Min.y >= Max.y) || (box.Max.z <= Min.z) || (box.Min.z >= Max.z))); }
public IntrLine3AxisAlignedBox3(Line3d l, AxisAlignedBox3d b) { line = l; box = b; }
protected virtual DMesh3 BuildMesh_TolerantWeld(STLSolid solid, double weld_tolerance) { var builder = new DMesh3Builder(); builder.AppendNewMesh(false, false, false, false); DVectorArray3f vertices = solid.Vertices; int N = vertices.Count; int[] mapV = new int[N]; AxisAlignedBox3d bounds = AxisAlignedBox3d.Empty; for (int i = 0; i < N; ++i) { bounds.Contain(vertices[i]); } // [RMS] because we are only searching within tiny radius, there is really no downside to // using lots of bins here, except memory usage. If we don't, and the mesh has a ton of triangles // very close together (happens all the time on big meshes!), then this step can start // to take an *extremely* long time! int num_bins = 256; if (N > 100000) { num_bins = 512; } if (N > 1000000) { num_bins = 1024; } if (N > 2000000) { num_bins = 2048; } if (N > 5000000) { num_bins = 4096; } var uniqueV = new PointHashGrid3d <int>(bounds.MaxDim / (float)num_bins, -1); var pos = new Vector3f[N]; for (int vi = 0; vi < N; ++vi) { Vector3f v = vertices[vi]; var pair = uniqueV.FindNearestInRadius(v, weld_tolerance, (vid) => { return(v.Distance(pos[vid])); }); if (pair.Key == -1) { int vid = builder.AppendVertex(v.x, v.y, v.z); uniqueV.InsertPoint(vid, v); mapV[vi] = vid; pos[vid] = v; } else { mapV[vi] = pair.Key; } } append_mapped_triangles(solid, builder, mapV); return(builder.Meshes[0]); }
// (sequentially) find each triangle that path point lies in, and insert a vertex for // that point into mesh. void insert_corners(HashSet <int> MeshVertsOnCurve) { PrimalQuery2d query = new PrimalQuery2d(PointF); if (UseTriSpatial) { int count = Mesh.TriangleCount + Curve.VertexCount; int bins = 32; if (count < 25) { bins = 8; } else if (count < 100) { bins = 16; } AxisAlignedBox3d bounds3 = Mesh.CachedBounds; AxisAlignedBox2d bounds2 = new AxisAlignedBox2d(bounds3.Min.xy, bounds3.Max.xy); triSpatial = new TriangleBinsGrid2d(bounds2, bins); foreach (int tid in Mesh.TriangleIndices()) { spatial_add_triangle(tid); } } Func <int, Vector2d, bool> inTriangleF = (tid, pos) => { Index3i tv = Mesh.GetTriangle(tid); int query_result = query.ToTriangleUnsigned(pos, tv.a, tv.b, tv.c); return(query_result == -1 || query_result == 0); }; CurveVertices = new int[Curve.VertexCount]; for (int i = 0; i < Curve.VertexCount; ++i) { Vector2d vInsert = Curve[i]; bool inserted = false; int contain_tid = DMesh3.InvalidID; if (triSpatial != null) { contain_tid = triSpatial.FindContainingTriangle(vInsert, inTriangleF); } else { foreach (int tid in Mesh.TriangleIndices()) { Index3i tv = Mesh.GetTriangle(tid); // [RMS] using unsigned query here because we do not need to care about tri CW/CCW orientation // (right? otherwise we have to explicitly invert mesh. Nothing else we do depends on tri orientation) //int query_result = query.ToTriangle(vInsert, tv.a, tv.b, tv.c); int query_result = query.ToTriangleUnsigned(vInsert, tv.a, tv.b, tv.c); if (query_result == -1 || query_result == 0) { contain_tid = tid; break; } } } if (contain_tid != DMesh3.InvalidID) { Index3i tv = Mesh.GetTriangle(contain_tid); Vector3d bary = MathUtil.BarycentricCoords(vInsert, PointF(tv.a), PointF(tv.b), PointF(tv.c)); // SpatialEpsilon is our zero-tolerance, so merge if we are closer than that bool is_existing_v; int vid = insert_corner_from_bary(i, contain_tid, bary, 0.01, 100 * SpatialEpsilon, out is_existing_v); if (vid > 0) // this should be always happening.. { CurveVertices[i] = vid; if (is_existing_v) { MeshVertsOnCurve.Add(vid); } inserted = true; } else { throw new Exception("MeshInsertUVPolyCurve.insert_corners: failed to insert vertex " + i.ToString()); } } if (inserted == false) { throw new Exception("MeshInsertUVPolyCurve.insert_corners: curve vertex " + i.ToString() + " is not inside or on any mesh triangle!"); } } }
public void Contain(AxisAlignedBox3d box) { Contain(box.Min); Contain(box.Max); }
public IntrRay3AxisAlignedBox3(Ray3d r, AxisAlignedBox3d b) { ray = r; box = b; }
/// <summary> /// test if ray intersects box. /// expandExtents allows you to scale box for hit-testing purposes. /// </summary> public static bool Intersects(ref Ray3d ray, ref AxisAlignedBox3d box, double expandExtents = 0) { Vector3d WdU = Vector3d.Zero; Vector3d AWdU = Vector3d.Zero; Vector3d DdU = Vector3d.Zero; Vector3d ADdU = Vector3d.Zero; Vector3d AWxDdU = Vector3d.Zero; double RHS; Vector3d diff = ray.Origin - box.Center; Vector3d extent = box.Extents + expandExtents; WdU[0] = ray.Direction.x; // ray.Direction.Dot(Vector3d.AxisX); AWdU[0] = Math.Abs(WdU[0]); DdU[0] = diff.x; // diff.Dot(Vector3d.AxisX); ADdU[0] = Math.Abs(DdU[0]); if (ADdU[0] > extent.x && DdU[0] * WdU[0] >= (double)0) { return(false); } WdU[1] = ray.Direction.y; // ray.Direction.Dot(Vector3d.AxisY); AWdU[1] = Math.Abs(WdU[1]); DdU[1] = diff.y; // diff.Dot(Vector3d.AxisY); ADdU[1] = Math.Abs(DdU[1]); if (ADdU[1] > extent.y && DdU[1] * WdU[1] >= (double)0) { return(false); } WdU[2] = ray.Direction.z; // ray.Direction.Dot(Vector3d.AxisZ); AWdU[2] = Math.Abs(WdU[2]); DdU[2] = diff.z; // diff.Dot(Vector3d.AxisZ); ADdU[2] = Math.Abs(DdU[2]); if (ADdU[2] > extent.z && DdU[2] * WdU[2] >= (double)0) { return(false); } Vector3d WxD = ray.Direction.Cross(diff); AWxDdU[0] = Math.Abs(WxD.x); // Math.Abs(WxD.Dot(Vector3d.AxisX)); RHS = extent.y * AWdU[2] + extent.z * AWdU[1]; if (AWxDdU[0] > RHS) { return(false); } AWxDdU[1] = Math.Abs(WxD.y); // Math.Abs(WxD.Dot(Vector3d.AxisY)); RHS = extent.x * AWdU[2] + extent.z * AWdU[0]; if (AWxDdU[1] > RHS) { return(false); } AWxDdU[2] = Math.Abs(WxD.z); // Math.Abs(WxD.Dot(Vector3d.AxisZ)); RHS = extent.x * AWdU[1] + extent.y * AWdU[0]; if (AWxDdU[2] > RHS) { return(false); } return(true); }
int split_point_set_midpoint(int[] pt_indices, Vector3d[] positions, int iStart, int iCount, int depth, int minIndexCount, boxes_set leafs, boxes_set nodes, out AxisAlignedBox3d box) { box = AxisAlignedBox3d.Empty; int iBox = -1; if (iCount < minIndexCount) { // append new points box iBox = leafs.iBoxCur++; leafs.box_to_index.insert(leafs.iIndicesCur, iBox); leafs.index_list.insert(iCount, leafs.iIndicesCur++); for (int i = 0; i < iCount; ++i) { leafs.index_list.insert(pt_indices[iStart + i], leafs.iIndicesCur++); box.Contain(points.GetVertex(pt_indices[iStart + i])); } leafs.box_centers.insert(box.Center, iBox); leafs.box_extents.insert(box.Extents, iBox); return(-(iBox + 1)); } //compute interval along an axis and find midpoint int axis = depth % 3; Interval1d interval = Interval1d.Empty; for (int i = 0; i < iCount; ++i) { interval.Contain(positions[iStart + i][axis]); } double midpoint = interval.Center; int n0, n1; if (Math.Abs(interval.a - interval.b) > MathUtil.ZeroTolerance) { // we have to re-sort the centers & indices lists so that centers < midpoint // are first, so that we can recurse on the two subsets. We walk in from each side, // until we find two out-of-order locations, then we swap them. int l = 0; int r = iCount - 1; while (l < r) { // [RMS] is <= right here? if v.axis == midpoint, then this loop // can get stuck unless one of these has an equality test. But // I did not think enough about if this is the right thing to do... while (positions[iStart + l][axis] <= midpoint) { l++; } while (positions[iStart + r][axis] > midpoint) { r--; } if (l >= r) { break; //done! } //swap Vector3d tmpc = positions[iStart + l]; positions[iStart + l] = positions[iStart + r]; positions[iStart + r] = tmpc; int tmpt = pt_indices[iStart + l]; pt_indices[iStart + l] = pt_indices[iStart + r]; pt_indices[iStart + r] = tmpt; } n0 = l; n1 = iCount - n0; Debug.Assert(n0 >= 1 && n1 >= 1); } else { // interval is near-empty, so no point trying to do sorting, just split half and half n0 = iCount / 2; n1 = iCount - n0; } // create child boxes AxisAlignedBox3d box1; int child0 = split_point_set_midpoint(pt_indices, positions, iStart, n0, depth + 1, minIndexCount, leafs, nodes, out box); int child1 = split_point_set_midpoint(pt_indices, positions, iStart + n0, n1, depth + 1, minIndexCount, leafs, nodes, out box1); box.Contain(box1); // append new box iBox = nodes.iBoxCur++; nodes.box_to_index.insert(nodes.iIndicesCur, iBox); nodes.index_list.insert(child0, nodes.iIndicesCur++); nodes.index_list.insert(child1, nodes.iIndicesCur++); nodes.box_centers.insert(box.Center, iBox); nodes.box_extents.insert(box.Extents, iBox); return(iBox); }
public virtual void Generate() { AxisAlignedBox3d graphBox = Graph.CachedBounds; graphBox.Expand(2 * PostRadius); double cellSize = (SamplerCellSizeHint == 0) ? (PostRadius / 5) : SamplerCellSizeHint; ImplicitFieldSampler3d sampler = new ImplicitFieldSampler3d(graphBox, cellSize); ActualCellSize = cellSize; // sample segments into graph ImplicitLine3d line = new ImplicitLine3d() { Radius = PostRadius }; foreach (int eid in Graph.EdgeIndices()) { Index2i ev = Graph.GetEdgeV(eid); Vector3d v0 = Graph.GetVertex(ev.a); Vector3d v1 = Graph.GetVertex(ev.b); double r = PostRadius; int upper_vid = (v0.y > v1.y) ? ev.a : ev.b; if (TipVertices.Contains(upper_vid)) { r = TipRadius; } line.Segment = new Segment3d(v0, v1); line.Radius = r; sampler.Sample(line, line.Radius / 2); } foreach (int vid in GroundVertices) { Vector3d v = Graph.GetVertex(vid); sampler.Sample(new ImplicitSphere3d() { Origin = v - (PostRadius / 2) * Vector3d.AxisY, Radius = GroundRadius }); } ImplicitHalfSpace3d cutPlane = new ImplicitHalfSpace3d() { Origin = Vector3d.Zero, Normal = Vector3d.AxisY }; ImplicitDifference3d cut = new ImplicitDifference3d() { A = sampler.ToImplicit(), B = cutPlane }; MarchingCubes mc = new MarchingCubes() { Implicit = cut, Bounds = graphBox, CubeSize = PostRadius / 3 }; mc.Bounds.Min.y = -2 * mc.CubeSize; mc.Bounds.Min.x -= 2 * mc.CubeSize; mc.Bounds.Min.z -= 2 * mc.CubeSize; mc.Bounds.Max.x += 2 * mc.CubeSize; mc.Bounds.Max.z += 2 * mc.CubeSize; mc.CancelF = this.Cancelled; mc.Generate(); ResultMesh = mc.Mesh; }
// (sequentially) find each triangle that path point lies in, and insert a vertex for // that point into mesh. void insert_corners(HashSet <int> MeshVertsOnCurve) { PrimalQuery2d query = new PrimalQuery2d(PointF); if (UseTriSpatial) { int count = Mesh.TriangleCount + Curve.VertexCount; int bins = 32; if (count < 25) { bins = 8; } else if (count < 100) { bins = 16; } AxisAlignedBox3d bounds3 = Mesh.CachedBounds; AxisAlignedBox2d bounds2 = new AxisAlignedBox2d(bounds3.Min.xy, bounds3.Max.xy); triSpatial = new TriangleBinsGrid2d(bounds2, bins); foreach (int tid in Mesh.TriangleIndices()) { spatial_add_triangle(tid); } } Func <int, Vector2d, bool> inTriangleF = (tid, pos) => { Index3i tv = Mesh.GetTriangle(tid); int query_result = query.ToTriangleUnsigned(pos, tv.a, tv.b, tv.c); return(query_result == -1 || query_result == 0); }; CurveVertices = new int[Curve.VertexCount]; for (int i = 0; i < Curve.VertexCount; ++i) { Vector2d vInsert = Curve[i]; bool inserted = false; // find the triangle that contains this curve point int contain_tid = DMesh3.InvalidID; if (triSpatial != null) { contain_tid = triSpatial.FindContainingTriangle(vInsert, inTriangleF); } else { foreach (int tid in Mesh.TriangleIndices()) { Index3i tv = Mesh.GetTriangle(tid); // [RMS] using unsigned query here because we do not need to care about tri CW/CCW orientation // (right? otherwise we have to explicitly invert mesh. Nothing else we do depends on tri orientation) //int query_result = query.ToTriangle(vInsert, tv.a, tv.b, tv.c); int query_result = query.ToTriangleUnsigned(vInsert, tv.a, tv.b, tv.c); if (query_result == -1 || query_result == 0) { contain_tid = tid; break; } } } // if we found one, insert the point via face-poke or edge-split, // unless it is exactly at existing vertex, in which case we can re-use it if (contain_tid != DMesh3.InvalidID) { Index3i tv = Mesh.GetTriangle(contain_tid); Vector3d bary = MathUtil.BarycentricCoords(vInsert, PointF(tv.a), PointF(tv.b), PointF(tv.c)); // SpatialEpsilon is our zero-tolerance, so merge if we are closer than that bool is_existing_v; int vid = insert_corner_from_bary(i, contain_tid, bary, 0.01, 100 * SpatialEpsilon, out is_existing_v); if (vid > 0) { CurveVertices[i] = vid; if (is_existing_v) { MeshVertsOnCurve.Add(vid); } inserted = true; } } // if we did not find containing triangle, // try matching with any existing vertices. // This can happen if curve point is right on mesh border... if (inserted == false) { foreach (int vid in Mesh.VertexIndices()) { Vector2d v = PointF(vid); if (vInsert.Distance(v) < SpatialEpsilon) { CurveVertices[i] = vid; MeshVertsOnCurve.Add(vid); inserted = true; } } } // TODO: also case where curve point is right on mesh border edge, // and so it ends up being outside all triangles? if (inserted == false) { throw new Exception("MeshInsertUVPolyCurve.insert_corners: curve vertex " + i.ToString() + " is not inside or on any mesh triangle!"); } } }