PolyLine2d to_polyline(EdgeSpan span, Frame3f polyFrame) { int NV = span.VertexCount; PolyLine2d poly = new PolyLine2d(); for (int k = 0; k < NV; ++k) { poly.AppendVertex(polyFrame.ToPlaneUV((Vector3f)span.GetVertex(k), 2)); } return(poly); }
public void Constructor_FromPolyLine() { // Arrange var polyline = new PolyLine2d(CreateSimpleVector2dArray()); // Act var curve = new FillCurve <FillSegment>(polyline); // Assert Assert.AreEqual(2, curve.Elements.Count); }
public static void Restore(PolyLine2d polyline, BinaryReader reader) { int count = reader.ReadInt32(); for (int i = 0; i < count; ++i) { double x = reader.ReadDouble(); double y = reader.ReadDouble(); polyline.AppendVertex(new Vector2D(x, y)); } }
public static CPolyPath ConvertToClipper(PolyLine2d pline, double nIntScale) { int N = pline.VertexCount; CPolyPath clipper_path = new CPolyPath(N); for (int i = 0; i < N; ++i) { Vector2d v = pline[i]; clipper_path.Add(new IntPoint(nIntScale * v.x, nIntScale * v.y)); } return(clipper_path); }
public static void test_svg() { Polygon2d poly = Polygon2d.MakeCircle(100.0f, 10); PolyLine2d pline = new PolyLine2d(); pline.AppendVertex(Vector2d.Zero); pline.AppendVertex(200 * Vector2d.AxisX); pline.AppendVertex(200 * Vector2d.One); Circle2d circ = new Circle2d(33 * Vector2d.One, 25); Segment2d seg = new Segment2d(Vector2d.Zero, -50 * Vector2d.AxisY); SVGWriter writer = new SVGWriter(); writer.AddPolygon(poly, SVGWriter.Style.Filled("lime", "black", 0.25f)); writer.AddPolyline(pline, SVGWriter.Style.Outline("orange", 2.0f)); writer.AddCircle(circ, SVGWriter.Style.Filled("yellow", "red", 5.0f)); writer.AddLine(seg, SVGWriter.Style.Outline("blue", 10.0f)); int astep = 29; Vector2d c = new Vector2d(-200, 100); for (int k = 1; k <= 12; ++k) { Arc2d arc = new Arc2d(c + k * 45 * Vector2d.AxisX, 20, 0, k * astep); writer.AddArc(arc); writer.AddBox(arc.Bounds, SVGWriter.Style.Outline("red", 0.5f)); } c.y += 50; for (int k = 1; k <= 12; ++k) { Arc2d arc = new Arc2d(c + k * 45 * Vector2d.AxisX, 20, k * astep, (k + 5) * astep); writer.AddArc(arc); writer.AddBox(arc.Bounds, SVGWriter.Style.Outline("red", 0.5f)); } c.y += 50; for (int k = 1; k <= 12; ++k) { Arc2d arc = new Arc2d(c + k * 45 * Vector2d.AxisX, 20, k * astep, (k + 10) * astep); writer.AddArc(arc); writer.AddBox(arc.Bounds, SVGWriter.Style.Outline("red", 0.5f)); } c.y += 50; for (int k = 1; k <= 12; ++k) { Arc2d arc = new Arc2d(c + k * 45 * Vector2d.AxisX, 20, k * astep, (k + 10) * astep); arc.Reverse(); writer.AddArc(arc); writer.AddBox(arc.Bounds, SVGWriter.Style.Outline("red", 0.5f)); } writer.Write(TestUtil.GetTestOutputPath("test.svg")); }
public void AddClippedPathTest() { PlanarSlice slice = new PlanarSlice(); PolyLine2d line = new PolyLine2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(-1, 0) }); slice.AddClippedPath(line); Assert.AreEqual(slice.ClippedPaths[0], line); }
public void ResolveEmbeddedPathWidthExceptionTest() { PlanarSlice slice = new PlanarSlice(); PolyLine2d line = new PolyLine2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(-1, 0) }); slice.AddEmbeddedPath(line); Assert.ThrowsException <Exception>(() => slice.Resolve()); }
bool self_intersects(PolyLine2d poly) { var seg = new Segment2d(poly.Start, poly.End); int NS = poly.VertexCount - 2; for (int i = 1; i < NS; ++i) { if (poly.Segment(i).Intersects(ref seg)) { return(true); } } return(false); }
protected virtual Polygon2d make_thickened_path(PolyLine2d path, double width) { PolyLine2d pos = new PolyLine2d(path), neg = new PolyLine2d(path); pos.VertexOffset(width / 2); neg.VertexOffset(-width / 2); neg.Reverse(); pos.AppendVertices(neg); Polygon2d poly = new Polygon2d(pos.Vertices); if (poly.IsClockwise) { poly.Reverse(); } return(poly); }
public static PolyLine2d ConvertFromClipperPath(CPolyPath clipper_poly, double nIntScale) { double scale = 1.0 / (double)nIntScale; int N = clipper_poly.Count; PolyLine2d poly = new PolyLine2d(); for (int i = 0; i < N; ++i) { IntPoint p = clipper_poly[i]; Vector2d v = new Vector2d((double)p.X * scale, (double)p.Y * scale); poly.AppendVertex(v); } return(poly); }
virtual public void PreRender() { if (in_shutdown()) { return; } if (parameters_dirty) { PolyLine2d spiral = PolyLine2d.MakeBoxSpiral(Vector2d.Zero, length, PathWidth + spacing); DMesh3 mesh = new DMesh3(); List <int> bottom_verts = new List <int>(); List <int> top_verts = new List <int>(); for (int i = 0; i < spiral.VertexCount; ++i) { Vector2d x = spiral[i]; Vector3d vb = new Vector3d(x.x, 0, x.y); bottom_verts.Add(mesh.AppendVertex(vb)); top_verts.Add(mesh.AppendVertex(vb + Height * Vector3d.AxisY)); } MeshEditor editor = new MeshEditor(mesh); editor.StitchSpan(bottom_verts, top_verts); PreviewSO.ReplaceMesh(mesh, true); Vector3d translate = scene_bounds.Point(1, -1, 1); translate.x += spiral.Bounds.Width + PathWidth; Frame3f sceneF = Frame3f.Identity.Translated((Vector3f)translate); PreviewSO.SetLocalFrame(sceneF, CoordSpace.SceneCoords); parameters_dirty = false; } }
private static bool compute_plane_curves(DMesh3 mesh, DMeshAABBTree3 spatial, double z, bool is_solid, out Polygon2d[] loops, out PolyLine2d[] curves) { Func <Vector3d, double> planeF = (v) => { return(v.z - z); }; // find list of triangles that intersect this z-value PlaneIntersectionTraversal planeIntr = new PlaneIntersectionTraversal(mesh, z); spatial.DoTraversal(planeIntr); List <int> triangles = planeIntr.triangles; // compute intersection iso-curves, which produces a 3D graph of undirected edges MeshIsoCurves iso = new MeshIsoCurves(mesh, planeF) { WantGraphEdgeInfo = true }; iso.Compute(triangles); DGraph3 graph = iso.Graph; if (graph.EdgeCount == 0) { loops = new Polygon2d[0]; curves = new PolyLine2d[0]; return(false); } // if this is a closed solid, any open spurs in the graph are errors if (is_solid) { DGraph3Util.ErodeOpenSpurs(graph); } // [RMS] debug visualization //DGraph2 graph2 = new DGraph2(); //Dictionary<int, int> mapV = new Dictionary<int, int>(); //foreach (int vid in graph.VertexIndices()) // mapV[vid] = graph2.AppendVertex(graph.GetVertex(vid).xy); //foreach (int eid in graph.EdgeIndices()) // graph2.AppendEdge(mapV[graph.GetEdge(eid).a], mapV[graph.GetEdge(eid).b]); //SVGWriter svg = new SVGWriter(); //svg.AddGraph(graph2, SVGWriter.Style.Outline("black", 0.05f)); //foreach (int vid in graph2.VertexIndices()) { // if (graph2.IsJunctionVertex(vid)) // svg.AddCircle(new Circle2d(graph2.GetVertex(vid), 0.25f), SVGWriter.Style.Outline("red", 0.1f)); // else if (graph2.IsBoundaryVertex(vid)) // svg.AddCircle(new Circle2d(graph2.GetVertex(vid), 0.25f), SVGWriter.Style.Outline("blue", 0.1f)); //} //svg.Write(string.Format("c:\\meshes\\EXPORT_SLICE_{0}.svg", z)); // extract loops and open curves from graph DGraph3Util.Curves c = DGraph3Util.ExtractCurves(graph, false, iso.ShouldReverseGraphEdge); loops = new Polygon2d[c.Loops.Count]; for (int li = 0; li < loops.Length; ++li) { DCurve3 loop = c.Loops[li]; loops[li] = new Polygon2d(); foreach (Vector3d v in loop.Vertices) { loops[li].AppendVertex(v.xy); } } curves = new PolyLine2d[c.Paths.Count]; for (int pi = 0; pi < curves.Length; ++pi) { DCurve3 span = c.Paths[pi]; curves[pi] = new PolyLine2d(); foreach (Vector3d v in span.Vertices) { curves[pi].AppendVertex(v.xy); } } return(true); }
public void Add(PolyLine2d path, int gid = -1) { Graph.AppendPolyline(path, gid); }
/// <summary> /// Collision paths are fixed thickened paths we need to clip against /// </summary> public void AddCollisionConstraint(PolyLine2d path) { CollisionGraph.AppendPolyline(path); }
/// <summary> /// remove portions of polyline that are inside set of solids /// </summary> public static List <PolyLine2d> ClipAgainstPolygon(List <GeneralPolygon2d> solids, PolyLine2d polyline, bool bIntersect = false) { return(ClipAgainstPolygon((IEnumerable <GeneralPolygon2d>)solids, polyline, bIntersect)); }
/// <summary> /// remove portions of polyline that are inside set of solids /// </summary> public static List <PolyLine2d> ClipAgainstPolygon(IEnumerable <GeneralPolygon2d> solids, PolyLine2d polyline, bool bIntersect = false) { double nIntScale = Math.Max(GetIntScale(solids), GetIntScale(polyline.Vertices)); List <PolyLine2d> result = new List <PolyLine2d>(); Clipper clip = new Clipper(); PolyTree tree = new PolyTree(); try { foreach (GeneralPolygon2d poly in solids) { CPolygonList clipper_poly = ConvertToClipper(poly, nIntScale); clip.AddPaths(clipper_poly, PolyType.ptClip, true); } CPolyPath path = ConvertToClipper(polyline, nIntScale); clip.AddPath(path, PolyType.ptSubject, false); if (bIntersect) { clip.Execute(ClipType.ctIntersection, tree); } else { clip.Execute(ClipType.ctDifference, tree); } for (int ci = 0; ci < tree.ChildCount; ++ci) { if (tree.Childs[ci].IsOpen == false) { continue; } PolyLine2d clippedPath = ConvertFromClipperPath(tree.Childs[ci].Contour, nIntScale); // clipper just cuts up the polylines, we still have to figure out containment ourselves. // Currently just checking based on point around middle of polyline... // [TODO] can we get clipper to not return the inside ones? Vector2d qp = (clippedPath.VertexCount > 2) ? clippedPath[clippedPath.VertexCount / 2] : clippedPath.Segment(0).Center; bool inside = false; foreach (var poly in solids) { if (poly.Contains(qp)) { inside = true; break; } } if (inside == bIntersect) { result.Add(clippedPath); } } } catch /*(Exception e)*/ { // [TODO] what to do here? //System.Diagnostics.Debug.WriteLine("ClipperUtil.ClipAgainstPolygon: Clipper threw exception: " + e.Message); return(new List <PolyLine2d>()); } return(result); }
public void ResolveTest() { PlanarSlice slice = new PlanarSlice(); Polygon2d p2d = new Polygon2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(0, 1), new Vector2d(1, 1) }); GeneralPolygon2d poly = new GeneralPolygon2d(p2d); Polygon2d p2d2 = new Polygon2d(new List <Vector2d>() { new Vector2d(-1, 0), new Vector2d(0, -1), new Vector2d(-1, -1) }); GeneralPolygon2d poly2 = new GeneralPolygon2d(p2d2); slice.AddPolygons(new List <GeneralPolygon2d>() { poly, poly2 }); Assert.AreEqual(slice.InputSolids[0], poly); Assert.AreEqual(slice.InputSolids[1], poly2); PolyLine2d line = new PolyLine2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(-1, 0) }); slice.AddEmbeddedPath(line); slice.EmbeddedPathWidth = 1; Polygon2d supportp2d = new Polygon2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(0, 1), new Vector2d(1, 1) }); GeneralPolygon2d supportpoly = new GeneralPolygon2d(supportp2d); Polygon2d supportp2d2 = new Polygon2d(new List <Vector2d>() { new Vector2d(-1, 0), new Vector2d(0, -1), new Vector2d(-1, -1) }); GeneralPolygon2d supportpoly2 = new GeneralPolygon2d(supportp2d2); slice.AddSupportPolygons(new List <GeneralPolygon2d>() { supportpoly, supportpoly2 }); Polygon2d cavp2d = new Polygon2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(0, 1), new Vector2d(1, 1) }); GeneralPolygon2d cavpoly = new GeneralPolygon2d(cavp2d); Polygon2d cavp2d2 = new Polygon2d(new List <Vector2d>() { new Vector2d(-1, 0), new Vector2d(0, -1), new Vector2d(-1, -1) }); GeneralPolygon2d cavpoly2 = new GeneralPolygon2d(cavp2d2); slice.AddCavityPolygons(new List <GeneralPolygon2d>() { cavpoly, cavpoly2 }); Polygon2d cropp2d = new Polygon2d(new List <Vector2d>() { new Vector2d(1, 0), new Vector2d(0, 1), new Vector2d(1, 1) }); GeneralPolygon2d croppoly = new GeneralPolygon2d(cropp2d); Polygon2d cropp2d2 = new Polygon2d(new List <Vector2d>() { new Vector2d(-1, 0), new Vector2d(0, -1), new Vector2d(-1, -1) }); GeneralPolygon2d croppoly2 = new GeneralPolygon2d(cropp2d2); slice.AddCropRegions(new List <GeneralPolygon2d>() { croppoly, croppoly2 }); slice.Resolve(); slice.BuildSpatialCaches(); double dist = slice.DistanceSquared(new Vector2d(5, 5)); Assert.AreEqual(41, dist); }
public static void test_arrangement_demo() { DMesh3 mesh = TestUtil.LoadTestInputMesh("spheres_and_planes.obj"); MeshTransforms.Scale(mesh, 8); AxisAlignedBox3d meshBounds = mesh.CachedBounds; Vector3d origin = meshBounds.Center; double simplify_thresh = 5.0; Frame3f plane = new Frame3f(origin, Vector3d.AxisY); MeshPlaneCut cut = new MeshPlaneCut(mesh, plane.Origin, plane.Z); cut.Cut(); Arrangement2d builder = new Arrangement2d(new AxisAlignedBox2d(1024.0)); // insert all cut edges HashSet <Vector2d> srcpts = new HashSet <Vector2d>(); foreach (EdgeLoop loop in cut.CutLoops) { Polygon2d poly = new Polygon2d(); foreach (int vid in loop.Vertices) { poly.AppendVertex(mesh.GetVertex(vid).xz); } poly.Simplify(simplify_thresh, 0.01, true); foreach (Vector2d v in poly.Vertices) { srcpts.Add(v); } builder.Insert(poly); } foreach (EdgeSpan span in cut.CutSpans) { PolyLine2d pline = new PolyLine2d(); foreach (int vid in span.Vertices) { pline.AppendVertex(mesh.GetVertex(vid).xz); } pline.Simplify(simplify_thresh, 0.01, true); foreach (Vector2d v in pline) { srcpts.Add(v); } builder.Insert(pline); } SVGWriter svg = new SVGWriter(); svg.AddGraph(builder.Graph); var vtx_style = SVGWriter.Style.Outline("red", 1.0f); foreach (int vid in builder.Graph.VertexIndices()) { Vector2d v = builder.Graph.GetVertex(vid); if (srcpts.Contains(v) == false) { svg.AddCircle(new Circle2d(v, 2), vtx_style); } } svg.Write(TestUtil.GetTestOutputPath("arrangement.svg")); }
public FillPolyline2d(PolyLine2d p) : base(p) { CustomThickness = 0; }
public void AddEmbeddedPath(PolyLine2d pline) { EmbeddedPaths.Add(pline); }
/// <summary> /// Slice the meshes and return the slice stack. /// </summary> public PlanarSliceStack Compute() { if (Meshes.Count == 0) { return(new PlanarSliceStack()); } Interval1d zrange = Interval1d.Empty; foreach (var meshinfo in Meshes) { zrange.Contain(meshinfo.bounds.Min.z); zrange.Contain(meshinfo.bounds.Max.z); } if (SetMinZValue != double.MinValue) { zrange.a = SetMinZValue; } int nLayers = (int)(zrange.Length / LayerHeightMM); if (nLayers > MaxLayerCount) { throw new Exception("MeshPlanarSlicer.Compute: exceeded layer limit. Increase .MaxLayerCount."); } // make list of slice heights (could be irregular) List <double> heights = new List <double>(); for (int i = 0; i < nLayers + 1; ++i) { double t = zrange.a + (double)i * LayerHeightMM; if (SliceLocation == SliceLocations.EpsilonBase) { t += 0.01 * LayerHeightMM; } else if (SliceLocation == SliceLocations.MidLine) { t += 0.5 * LayerHeightMM; } heights.Add(t); } int NH = heights.Count; // process each *slice* in parallel PlanarSlice[] slices = new PlanarSlice[NH]; for (int i = 0; i < NH; ++i) { slices[i] = SliceFactoryF(heights[i], i); slices[i].EmbeddedPathWidth = OpenPathDefaultWidthMM; } // assume Resolve() takes 2x as long as meshes... TotalCompute = (Meshes.Count * NH) + (2 * NH); Progress = 0; // compute slices separately for each mesh for (int mi = 0; mi < Meshes.Count; ++mi) { if (Cancelled()) { break; } DMesh3 mesh = Meshes[mi].mesh; PrintMeshOptions mesh_options = Meshes[mi].options; // [TODO] should we hang on to this spatial? or should it be part of assembly? DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh, true); AxisAlignedBox3d bounds = Meshes[mi].bounds; bool is_cavity = mesh_options.IsCavity; bool is_crop = mesh_options.IsCropRegion; bool is_support = mesh_options.IsSupport; bool is_closed = (mesh_options.IsOpen) ? false : mesh.IsClosed(); var useOpenMode = (mesh_options.OpenPathMode == PrintMeshOptions.OpenPathsModes.Default) ? DefaultOpenPathMode : mesh_options.OpenPathMode; // each layer is independent so we can do in parallel gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } double z = heights[i]; if (z < bounds.Min.z || z > bounds.Max.z) { return; } // compute cut Polygon2d[] polys; PolyLine2d[] paths; compute_plane_curves(mesh, spatial, z, is_closed, out polys, out paths); // if we didn't hit anything, try again with jittered plane // [TODO] this could be better... if ((is_closed && polys.Length == 0) || (is_closed == false && polys.Length == 0 && paths.Length == 0)) { compute_plane_curves(mesh, spatial, z + LayerHeightMM * 0.25, is_closed, out polys, out paths); } if (is_closed) { // construct planar complex and "solids" // (ie outer polys and nested holes) PlanarComplex complex = new PlanarComplex(); foreach (Polygon2d poly in polys) { complex.Add(poly); } PlanarComplex.FindSolidsOptions options = PlanarComplex.FindSolidsOptions.Default; options.WantCurveSolids = false; options.SimplifyDeviationTolerance = 0.001; options.TrustOrientations = true; options.AllowOverlappingHoles = true; PlanarComplex.SolidRegionInfo solids = complex.FindSolidRegions(options); if (is_support) { add_support_polygons(slices[i], solids.Polygons, mesh_options); } else if (is_cavity) { add_cavity_polygons(slices[i], solids.Polygons, mesh_options); } else if (is_crop) { add_crop_region_polygons(slices[i], solids.Polygons, mesh_options); } else { add_solid_polygons(slices[i], solids.Polygons, mesh_options); } } else if (useOpenMode != PrintMeshOptions.OpenPathsModes.Ignored) { foreach (PolyLine2d pline in paths) { if (useOpenMode == PrintMeshOptions.OpenPathsModes.Embedded) { slices[i].AddEmbeddedPath(pline); } else { slices[i].AddClippedPath(pline); } } // [TODO] // - does not really handle clipped polygons properly, there will be an extra break somewhere... foreach (Polygon2d poly in polys) { PolyLine2d pline = new PolyLine2d(poly, true); if (useOpenMode == PrintMeshOptions.OpenPathsModes.Embedded) { slices[i].AddEmbeddedPath(pline); } else { slices[i].AddClippedPath(pline); } } } Interlocked.Increment(ref Progress); }); // end of parallel.foreach } // end mesh iter // resolve planar intersections, etc gParallel.ForEach(Interval1i.Range(NH), (i) => { if (Cancelled()) { return; } slices[i].Resolve(); Interlocked.Add(ref Progress, 2); }); // discard spurious empty slices int last = slices.Length - 1; while (slices[last].IsEmpty && last > 0) { last--; } int first = 0; if (DiscardEmptyBaseSlices) { while (slices[first].IsEmpty && first < slices.Length) { first++; } } PlanarSliceStack stack = SliceStackFactoryF(); for (int k = first; k <= last; ++k) { stack.Add(slices[k]); } if (SupportMinZTips) { stack.AddMinZTipSupportPoints(MinZTipMaxDiam, MinZTipExtraLayers); } return(stack); }
public void AddClippedPath(PolyLine2d pline) { ClippedPaths.Add(pline); }
public static void test_tube_generator() { Polygon2d circle_path = Polygon2d.MakeCircle(50, 64); PolyLine2d arc_path = new PolyLine2d(circle_path.Vertices.Take(circle_path.VertexCount / 2)); Polygon2d irreg_path = new Polygon2d(); for (int k = 0; k < circle_path.VertexCount; ++k) { irreg_path.AppendVertex(circle_path[k]); k += k / 2; } PolyLine2d irreg_arc_path = new PolyLine2d(irreg_path.Vertices.Take(circle_path.VertexCount - 1)); Polygon2d square_profile = Polygon2d.MakeCircle(7, 32); square_profile.Translate(4 * Vector2d.One); //square_profile[0] = 20 * square_profile[0].Normalized; bool no_shared = true; WriteGeneratedMesh( new TubeGenerator(circle_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_loop_standarduv.obj"); WriteGeneratedMesh( new TubeGenerator(irreg_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_irregloop_standarduv.obj"); WriteGeneratedMesh( new TubeGenerator(arc_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_arc_standarduv.obj"); WriteGeneratedMesh( new TubeGenerator(irreg_arc_path, Frame3f.Identity, square_profile) { WantUVs = true, NoSharedVertices = no_shared }, "tubegen_irregarc_standarduv.obj"); // append tube border around each hole of input mesh DMesh3 inMesh = TestUtil.LoadTestInputMesh("n_holed_bunny.obj"); Polygon2d bdrycirc = Polygon2d.MakeCircle(0.25, 6); MeshBoundaryLoops loops = new MeshBoundaryLoops(inMesh); foreach (EdgeLoop loop in loops) { DCurve3 curve = loop.ToCurve().ResampleSharpTurns(); TubeGenerator gen = new TubeGenerator(curve, bdrycirc) { NoSharedVertices = false }; MeshEditor.Append(inMesh, gen.Generate().MakeDMesh()); } TestUtil.WriteTestOutputMesh(inMesh, "boundary_tubes.obj"); }