public DCurve3 ToCurve() { DCurve3 curve = MeshUtil.ExtractLoopV(Mesh, Vertices); curve.Closed = true; return(curve); }
/// <summary> /// Resample curve so that: /// - if opening angle at vertex is > sharp_thresh, we emit two more vertices at +/- corner_t, where the t is used in prev/next lerps /// - if opening angle is > flat_thresh, we skip the vertex entirely (simplification) /// This is mainly useful to get nicer polylines to use as the basis for (eg) creating 3D tubes, rendering, etc /// /// [TODO] skip tiny segments? /// </summary> public DCurve3 ResampleSharpTurns(double sharp_thresh = 90, double flat_thresh = 189, double corner_t = 0.01) { int NV = vertices.Count; DCurve3 resampled = new DCurve3() { Closed = this.Closed }; double prev_t = 1.0 - corner_t; for (int k = 0; k < NV; ++k) { double open_angle = Math.Abs(OpeningAngleDeg(k)); if (open_angle > flat_thresh && k > 0) { // ignore skip this vertex } else if (open_angle > sharp_thresh) { resampled.AppendVertex(vertices[k]); } else { Vector3d n = vertices[(k + 1) % NV]; Vector3d p = vertices[k == 0 ? NV - 1 : k - 1]; resampled.AppendVertex(Vector3d.Lerp(p, vertices[k], prev_t)); resampled.AppendVertex(vertices[k]); resampled.AppendVertex(Vector3d.Lerp(vertices[k], n, corner_t)); } } return(resampled); }
public TubeGenerator(DCurve3 tubePath, Polygon2d tubeShape) { Vertices = new List <Vector3d>(tubePath.Vertices); Polygon = new Polygon2d(tubeShape); ClosedLoop = tubePath.Closed; Capped = !ClosedLoop; }
public InPlaceIterativeCurveSmooth(DCurve3 curve, float alpha = 0.25f) { Curve = curve; Start = 0; End = Curve.VertexCount; Alpha = alpha; }
public ImplicitCurve3d(DCurve3 curve, double radius) { Curve = curve; Radius = radius; Box = curve.GetBoundingBox(); Box.Expand(Radius); spatial = new DCurve3BoxTree(curve); }
/// <summary> /// Creates g3.DCurve from Vector3[] /// </summary> /// <param name="curve">DCurve</param> /// <param name="verteces">Vextor3[]</param> /// <param name="bClosed">whether the line is closed</param> public static void Vector3(this g3.DCurve3 curve, Vector3[] verteces, bool bClosed) { curve.ClearVertices(); curve.Closed = bClosed; foreach (Vector3 vertex in verteces) { curve.AppendVertex(vertex); } }
public DCurve3BoxTree(DCurve3 curve) { if (curve.Closed == false) { throw new NotImplementedException("not done yet"); } Curve = curve; build_sequential(curve); }
public void Make(DCurve3 c) { int nV = vertices.Count; for (int i = 0; i < nV; ++i) { c.AppendVertex(vertices[i]); } c.Closed = closed; }
public static DCurve3 ExtractLoopV(IMesh mesh, IEnumerable <int> vertices) { DCurve3 curve = new DCurve3(); foreach (int vid in vertices) { curve.AppendVertex(mesh.GetVertex(vid)); } curve.Closed = true; return(curve); }
public static void Store(DCurve3 curve, BinaryWriter writer) { writer.Write(curve.Closed); writer.Write(curve.VertexCount); for (int i = 0; i < curve.VertexCount; ++i) { writer.Write(curve[i].x); writer.Write(curve[i].y); writer.Write(curve[i].z); } }
public static void PreserveBoundaryLoops(MeshConstraints cons, DMesh3 mesh) { MeshBoundaryLoops loops = new MeshBoundaryLoops(mesh); foreach (EdgeLoop loop in loops) { DCurve3 loopC = MeshUtil.ExtractLoopV(mesh, loop.Vertices); DCurveProjectionTarget target = new DCurveProjectionTarget(loopC); ConstrainVtxLoopTo(cons, mesh, loop.Vertices, target); } }
public DCurve3 ToCurve(DMesh3 sourceMesh = null) { if (sourceMesh == null) { sourceMesh = Mesh; } DCurve3 curve = MeshUtil.ExtractLoopV(sourceMesh, Vertices); curve.Closed = false; return(curve); }
public static DCurve3 ExtractLoopV(IMesh mesh, int[] vertices) { DCurve3 curve = new DCurve3(); for (int i = 0; i < vertices.Length; ++i) { curve.AppendVertex(mesh.GetVertex(vertices[i])); } curve.Closed = true; return(curve); }
public static void Restore(DCurve3 curve, BinaryReader reader) { curve.Closed = reader.ReadBoolean(); int count = reader.ReadInt32(); for (int i = 0; i < count; ++i) { double x = reader.ReadDouble(); double y = reader.ReadDouble(); double z = reader.ReadDouble(); curve.AppendVertex(new Vector3d(x, y, z)); } }
public MeshTrimLoop(DMesh3 mesh, DCurve3 trimline, Vector3d vSeedPt, DMeshAABBTree3 spatial = null) { if (spatial != null && spatial.Mesh == mesh) { throw new ArgumentException("MeshTrimLoop: input spatial DS must have its own copy of mesh"); } Mesh = mesh; TrimLine = new DCurve3(trimline); if (spatial != null) { Spatial = spatial; } seed_pt = vSeedPt; }
public MeshFacesFromLoop(DMesh3 Mesh, DCurve3 SpaceCurve, ISpatial Spatial, int tSeed) { this.Mesh = Mesh; int N = SpaceCurve.VertexCount; InitialLoopT = new int[N]; for (int i = 0; i < N; ++i) { InitialLoopT[i] = Spatial.FindNearestTriangle(SpaceCurve[i]); } find_path(); find_interior_from_seed(tSeed); }
/// <summary> /// Decompose graph into simple polylines and polygons. /// </summary> public static Curves ExtractCurves(DGraph3 graph) { Curves c = new Curves(); c.Loops = new List <DCurve3>(); c.Paths = new List <DCurve3>(); HashSet <int> used = new HashSet <int>(); // find boundary and junction vertices HashSet <int> boundaries = new HashSet <int>(); HashSet <int> junctions = new HashSet <int>(); foreach (int vid in graph.VertexIndices()) { if (graph.IsBoundaryVertex(vid)) { boundaries.Add(vid); } if (graph.IsJunctionVertex(vid)) { junctions.Add(vid); } } // walk paths from boundary vertices foreach (int start_vid in boundaries) { int vid = start_vid; int eid = graph.GetVtxEdges(vid)[0]; if (used.Contains(eid)) { continue; } DCurve3 path = new DCurve3() { Closed = false }; path.AppendVertex(graph.GetVertex(vid)); while (true) { used.Add(eid); Index2i next = NextEdgeAndVtx(eid, vid, graph); eid = next.a; vid = next.b; path.AppendVertex(graph.GetVertex(vid)); if (boundaries.Contains(vid) || junctions.Contains(vid)) { break; // done! } } c.Paths.Add(path); } // ok we should be done w/ boundary verts now... boundaries.Clear(); foreach (int start_vid in junctions) { foreach (int outgoing_eid in graph.VtxEdgesItr(start_vid)) { if (used.Contains(outgoing_eid)) { continue; } int vid = start_vid; int eid = outgoing_eid; DCurve3 path = new DCurve3() { Closed = false }; path.AppendVertex(graph.GetVertex(vid)); while (true) { used.Add(eid); Index2i next = NextEdgeAndVtx(eid, vid, graph); eid = next.a; vid = next.b; path.AppendVertex(graph.GetVertex(vid)); if (eid == int.MaxValue || junctions.Contains(vid)) { break; // done! } } // we could end up back at our start junction vertex! if (vid == start_vid) { path.RemoveVertex(path.VertexCount - 1); path.Closed = true; c.Loops.Add(path); // need to mark incoming edge as used...but is it valid now? //Util.gDevAssert(eid != int.MaxValue); if (eid != int.MaxValue) { used.Add(eid); } } else { c.Paths.Add(path); } } } // all that should be left are continuous loops... foreach (int start_eid in graph.EdgeIndices()) { if (used.Contains(start_eid)) { continue; } int eid = start_eid; Index2i ev = graph.GetEdgeV(eid); int vid = ev.a; DCurve3 poly = new DCurve3() { Closed = true }; poly.AppendVertex(graph.GetVertex(vid)); while (true) { used.Add(eid); Index2i next = NextEdgeAndVtx(eid, vid, graph); eid = next.a; vid = next.b; poly.AppendVertex(graph.GetVertex(vid)); if (eid == int.MaxValue || junctions.Contains(vid)) { throw new Exception("how did this happen??"); } if (used.Contains(eid)) { break; } } poly.RemoveVertex(poly.VertexCount - 1); c.Loops.Add(poly); } return(c); }
/// <summary> /// Decompose graph into simple polylines and polygons. /// </summary> public static Curves ExtractCurves(DGraph3 graph, bool bWantLoopIndices = false, Func <int, bool> CurveOrientationF = null) { var c = new Curves(); c.Loops = new List <DCurve3>(); c.Paths = new List <DCurve3>(); if (bWantLoopIndices) { c.LoopEdges = new List <List <int> >(); c.PathEdges = new List <List <int> >(); } var used = new HashSet <int>(); // find boundary and junction vertices var boundaries = new HashSet <int>(); var junctions = new HashSet <int>(); foreach (int vid in graph.VertexIndices()) { if (graph.IsBoundaryVertex(vid)) { boundaries.Add(vid); } if (graph.IsJunctionVertex(vid)) { junctions.Add(vid); } } // walk paths from boundary vertices foreach (int start_vid in boundaries) { int vid = start_vid; int eid = graph.GetVtxEdges(vid)[0]; if (used.Contains(eid)) { continue; } bool reverse = (CurveOrientationF != null) ? CurveOrientationF(eid) : false; var path = new DCurve3() { Closed = false }; List <int> pathE = (bWantLoopIndices) ? new List <int>() : null; path.AppendVertex(graph.GetVertex(vid)); if (pathE != null) { pathE.Add(eid); } while (true) { used.Add(eid); Index2i next = NextEdgeAndVtx(eid, vid, graph); eid = next.a; vid = next.b; path.AppendVertex(graph.GetVertex(vid)); if (boundaries.Contains(vid) || junctions.Contains(vid)) { break; // done! } if (pathE != null) { pathE.Add(eid); } } if (reverse) { path.Reverse(); } c.Paths.Add(path); if (pathE != null) { Util.gDevAssert(pathE.Count == path.VertexCount - 1); if (reverse) { pathE.Reverse(); } c.PathEdges.Add(pathE); } } // ok we should be done w/ boundary verts now... //boundaries.Clear(); c.BoundaryV = boundaries; foreach (int start_vid in junctions) { foreach (int outgoing_eid in graph.VtxEdgesItr(start_vid)) { if (used.Contains(outgoing_eid)) { continue; } int vid = start_vid; int eid = outgoing_eid; bool reverse = (CurveOrientationF != null) ? CurveOrientationF(eid) : false; var path = new DCurve3() { Closed = false }; List <int> pathE = (bWantLoopIndices) ? new List <int>() : null; path.AppendVertex(graph.GetVertex(vid)); if (pathE != null) { pathE.Add(eid); } while (true) { used.Add(eid); Index2i next = NextEdgeAndVtx(eid, vid, graph); eid = next.a; vid = next.b; path.AppendVertex(graph.GetVertex(vid)); if (eid == int.MaxValue || junctions.Contains(vid)) { break; // done! } if (pathE != null) { pathE.Add(eid); } } // we could end up back at our start junction vertex! if (vid == start_vid) { path.RemoveVertex(path.VertexCount - 1); path.Closed = true; if (reverse) { path.Reverse(); } c.Loops.Add(path); if (pathE != null) { Util.gDevAssert(pathE.Count == path.VertexCount); if (reverse) { pathE.Reverse(); } c.LoopEdges.Add(pathE); } // need to mark incoming edge as used...but is it valid now? //Util.gDevAssert(eid != int.MaxValue); if (eid != int.MaxValue) { used.Add(eid); } } else { if (reverse) { path.Reverse(); } c.Paths.Add(path); if (pathE != null) { Util.gDevAssert(pathE.Count == path.VertexCount - 1); if (reverse) { pathE.Reverse(); } c.PathEdges.Add(pathE); } } } } c.JunctionV = junctions; // all that should be left are continuous loops... foreach (int start_eid in graph.EdgeIndices()) { if (used.Contains(start_eid)) { continue; } int eid = start_eid; Index2i ev = graph.GetEdgeV(eid); int vid = ev.a; bool reverse = (CurveOrientationF != null) ? CurveOrientationF(eid) : false; var poly = new DCurve3() { Closed = true }; List <int> polyE = (bWantLoopIndices) ? new List <int>() : null; poly.AppendVertex(graph.GetVertex(vid)); if (polyE != null) { polyE.Add(eid); } while (true) { used.Add(eid); Index2i next = NextEdgeAndVtx(eid, vid, graph); eid = next.a; vid = next.b; poly.AppendVertex(graph.GetVertex(vid)); if (polyE != null) { polyE.Add(eid); } if (eid == int.MaxValue || junctions.Contains(vid)) { throw new Exception("how did this happen??"); } if (used.Contains(eid)) { break; } } poly.RemoveVertex(poly.VertexCount - 1); if (reverse) { poly.Reverse(); } c.Loops.Add(poly); if (polyE != null) { polyE.RemoveAt(polyE.Count - 1); Util.gDevAssert(polyE.Count == poly.VertexCount); if (reverse) { polyE.Reverse(); } c.LoopEdges.Add(polyE); } } return(c); }
// build tree of boxes as sequential array void build_sequential(DCurve3 curve) { int NV = curve.VertexCount; int N = NV; int boxCount = 0; layers = 0; layer_counts = new List <int>(); // count how many boxes in each layer, building up from initial segments int bi = 0; while (N > 1) { int layer_boxes = (N / 2) + (N % 2 == 0 ? 0 : 1); boxCount += layer_boxes; N = layer_boxes; layer_counts.Add(layer_boxes); bi += layer_boxes; layers++; } boxes = new Box3d[boxCount]; bi = 0; // make first layer for (int si = 0; si < NV; si += 2) { Vector3d v1 = curve[(si + 1) % NV]; Segment3d seg1 = new Segment3d(curve[si], v1); Box3d box = new Box3d(seg1); if (si < NV - 1) { Segment3d seg2 = new Segment3d(v1, curve[(si + 2) % NV]); Box3d box2 = new Box3d(seg2); box = Box3d.Merge(ref box, ref box2); } boxes[bi++] = box; } // repeatedly build layers until we hit a single box N = bi; int prev_layer_start = 0; bool done = false; while (done == false) { int layer_start = bi; for (int k = 0; k < N; k += 2) { Box3d mbox = Box3d.Merge(ref boxes[prev_layer_start + k], ref boxes[prev_layer_start + k + 1]); boxes[bi++] = mbox; } N = (N / 2) + (N % 2 == 0 ? 0 : 1); prev_layer_start = layer_start; if (N == 1) { done = true; } } }
public DCurveProjectionTarget(DCurve3 curve) { this.Curve = curve; }
public void Close_Flat() { double minlen, maxlen, avglen; MeshQueries.EdgeLengthStats(Mesh, out minlen, out maxlen, out avglen, 1000); double target_edge_len = (TargetEdgeLen <= 0) ? avglen : TargetEdgeLen; // massage around boundary loop List <int> refinedBorderEdges; cleanup_boundary(Mesh, InitialBorderLoop, avglen, out refinedBorderEdges, 3); // find new border loop. try to find new loop containing edges from loop we refined in cleanup_boundary, // if that fails just use largest loop. MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh); int iloop = loops.FindLoopContainingEdge(refinedBorderEdges[0]); if (iloop == -1) { iloop = loops.MaxVerticesLoopIndex; } EdgeLoop fill_loop = loops.Loops[iloop]; int extrude_group = (ExtrudeGroup == -1) ? Mesh.AllocateTriangleGroup() : ExtrudeGroup; int fill_group = (FillGroup == -1) ? Mesh.AllocateTriangleGroup() : FillGroup; // decide on projection plane //AxisAlignedBox3d loopbox = fill_loop.GetBounds(); //Vector3d topPt = loopbox.Center; //if ( bIsUpper ) { // topPt.y = loopbox.Max.y + 0.25 * dims.y; //} else { // topPt.y = loopbox.Min.y - 0.25 * dims.y; //} //Frame3f plane = new Frame3f((Vector3f)topPt); // extrude loop to this plane MeshExtrudeLoop extrude = new MeshExtrudeLoop(Mesh, fill_loop); extrude.PositionF = (v, n, i) => { return(FlatClosePlane.ProjectToPlane((Vector3f)v, 1)); }; extrude.Extrude(extrude_group); MeshValidation.IsBoundaryLoop(Mesh, extrude.NewLoop); Debug.Assert(Mesh.CheckValidity()); // smooth the extrude loop MeshLoopSmooth loop_smooth = new MeshLoopSmooth(Mesh, extrude.NewLoop); loop_smooth.ProjectF = (v, i) => { return(FlatClosePlane.ProjectToPlane((Vector3f)v, 1)); }; loop_smooth.Alpha = 0.5f; loop_smooth.Rounds = 100; loop_smooth.Smooth(); Debug.Assert(Mesh.CheckValidity()); // fill result SimpleHoleFiller filler = new SimpleHoleFiller(Mesh, extrude.NewLoop); filler.Fill(fill_group); Debug.Assert(Mesh.CheckValidity()); // make selection for remesh region MeshFaceSelection remesh_roi = new MeshFaceSelection(Mesh); remesh_roi.Select(extrude.NewTriangles); remesh_roi.Select(filler.NewTriangles); remesh_roi.ExpandToOneRingNeighbours(); remesh_roi.ExpandToOneRingNeighbours(); remesh_roi.LocalOptimize(true, true); int[] new_roi = remesh_roi.ToArray(); // get rid of extrude group FaceGroupUtil.SetGroupToGroup(Mesh, extrude_group, 0); /* clean up via remesh * - constrain loop we filled to itself */ RegionRemesher r = new RegionRemesher(Mesh, new_roi); DCurve3 top_curve = MeshUtil.ExtractLoopV(Mesh, extrude.NewLoop.Vertices); DCurveProjectionTarget curve_target = new DCurveProjectionTarget(top_curve); int[] top_loop = (int[])extrude.NewLoop.Vertices.Clone(); r.Region.MapVerticesToSubmesh(top_loop); MeshConstraintUtil.ConstrainVtxLoopTo(r.Constraints, r.Mesh, top_loop, curve_target); DMeshAABBTree3 spatial = new DMeshAABBTree3(Mesh); spatial.Build(); MeshProjectionTarget target = new MeshProjectionTarget(Mesh, spatial); r.SetProjectionTarget(target); bool bRemesh = true; if (bRemesh) { r.Precompute(); r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = target_edge_len; r.MaxEdgeLength = 2 * target_edge_len; r.EnableSmoothing = true; r.SmoothSpeedT = 1.0f; for (int k = 0; k < 40; ++k) { r.BasicRemeshPass(); } r.SetProjectionTarget(null); r.SmoothSpeedT = 0.25f; for (int k = 0; k < 10; ++k) { r.BasicRemeshPass(); } Debug.Assert(Mesh.CheckValidity()); r.BackPropropagate(); } // smooth around the join region to clean up ugliness smooth_region(Mesh, r.Region.BaseBorderV, 3); }
public DCurveProjection(DCurve3 curve) { this.Curve = curve; }
public DCurve3BoxTree(DCurve3 curve) { Curve = curve; build_sequential(curve); }
// build tree of boxes as sequential array void build_sequential(DCurve3 curve) { int NV = curve.VertexCount; int N = (curve.Closed) ? NV : NV - 1; int boxCount = 0; layers = 0; layer_counts = new List <int>(); // count how many boxes in each layer, building up from initial segments int bi = 0; while (N > 1) { int layer_boxes = (N / 2) + (N % 2 == 0 ? 0 : 1); boxCount += layer_boxes; N = layer_boxes; layer_counts.Add(layer_boxes); bi += layer_boxes; layers++; } // [RMS] this case happens if N = 1, previous loop is skipped and we have to // hardcode initialization to this redundant box if (layers == 0) { layers = 1; boxCount = 1; layer_counts = new List <int>() { 1 }; } boxes = new Box3d[boxCount]; bi = 0; // make first layer int NStop = (curve.Closed) ? NV : NV - 1; for (int si = 0; si < NStop; si += 2) { Vector3d v1 = curve[(si + 1) % NV]; var seg1 = new Segment3d(curve[si], v1); var box = new Box3d(seg1); if (si < NV - 1) { var seg2 = new Segment3d(v1, curve[(si + 2) % NV]); var box2 = new Box3d(seg2); box = Box3d.Merge(ref box, ref box2); } boxes[bi++] = box; } // repeatedly build layers until we hit a single box N = bi; if (N == 1) { return; } int prev_layer_start = 0; bool done = false; while (done == false) { int layer_start = bi; for (int k = 0; k < N; k += 2) { var mbox = Box3d.Merge(ref boxes[prev_layer_start + k], ref boxes[prev_layer_start + k + 1]); boxes[bi++] = mbox; } N = (N / 2) + (N % 2 == 0 ? 0 : 1); prev_layer_start = layer_start; if (N == 1) { done = true; } } }
public DCurve3(DCurve3 copy) { vertices = new List <Vector3d>(copy.vertices); Closed = copy.Closed; Timestamp = 0; }
public LaplacianCurveDeformer(DCurve3 curve) { Curve = curve; }