public void PrintStats(string label = "") { System.Console.WriteLine("PlanarComplex Stats {0}", label); List <SmoothLoopElement> Loops = new List <SmoothLoopElement>(LoopsItr()); List <SmoothCurveElement> Curves = new List <SmoothCurveElement>(CurvesItr()); AxisAlignedBox2d bounds = Bounds(); System.Console.WriteLine(" Bounding Box w: {0} h: {1} range {2} ", bounds.Width, bounds.Height, bounds); List <ComplexEndpoint2d> vEndpoints = new List <ComplexEndpoint2d>(EndpointsItr()); System.Console.WriteLine(" Closed Loops {0} Open Curves {1} Open Endpoints {2}", Loops.Count, Curves.Count, vEndpoints.Count); int nSegments = CountType(typeof(Segment2d)); int nArcs = CountType(typeof(Arc2d)); int nCircles = CountType(typeof(Circle2d)); int nNURBS = CountType(typeof(NURBSCurve2)); int nEllipses = CountType(typeof(Ellipse2d)); int nEllipseArcs = CountType(typeof(EllipseArc2d)); int nSeqs = CountType(typeof(ParametricCurveSequence2)); System.Console.WriteLine(" [Type Counts] // {0} multi-curves", nSeqs); System.Console.WriteLine(" segments {0,4} arcs {1,4} circles {2,4}", nSegments, nArcs, nCircles); System.Console.WriteLine(" nurbs {0,4} ellipses {1,4} ellipse-arcs {2,4}", nNURBS, nEllipses, nEllipseArcs); }
/// <summary> /// Fallback to deal with very tiny polygons that disappear when insetting. /// This happens at Z-minima-tips, which can be a problem because it may leave /// gaps between layers. For tips we draw a tiny circle. /// For elongated shapes we...?? currently do something dumb. /// Probably should use robust thinning! /// </summary> public virtual void HandleTinyPolygon() { //(InsetFromInputPolygon) ? //ClipperUtil.ComputeOffsetPolygon(Polygon, -ToolWidth / 2, true) : AxisAlignedBox2d bounds = Polygon.Bounds; if (bounds.MaxDim < ToolWidth) { GeneralPolygon2d min_poly = new GeneralPolygon2d(Polygon2d.MakeCircle(ToolWidth / 4, 6)); min_poly.Outer.Translate(bounds.Center); FillCurveSet2d paths = ShellPolysToPaths(new List <GeneralPolygon2d>() { min_poly }, 0); Shells.Add(paths); } else { FillCurveSet2d paths = ShellPolysToPaths(new List <GeneralPolygon2d>() { Polygon }, 0); Shells.Add(paths); } InnerPolygons = new List <GeneralPolygon2d>(); }
public static void test_tiling() { Vector2d origin = Vector2d.Zero; double radius = 22; Circle2d circ = new Circle2d(origin, radius); AxisAlignedBox2d elemBounds = circ.Bounds; //elemBounds.Max.x += radius / 2; AxisAlignedBox2d packBounds = new AxisAlignedBox2d(0, 0, 800, 400); double spacing = 5; Polygon2d boundsPoly = new Polygon2d(); for (int i = 0; i < 4; ++i) { boundsPoly.AppendVertex(packBounds.GetCorner(i)); } //List<Vector2d> packed = TilingUtil.BoundedRegularTiling2(elemBounds, packBounds, spacing); List <Vector2d> packed = TilingUtil.BoundedCircleTiling2(elemBounds, packBounds, spacing); System.Console.WriteLine("packed {0}", packed.Count); SVGWriter writer = new SVGWriter(); foreach (Vector2d t in packed) { writer.AddCircle(new Circle2d(origin + t, radius), SVGWriter.Style.Outline("black", 1.0f)); } writer.AddPolygon(boundsPoly, SVGWriter.Style.Outline("red", 2.0f)); writer.Write(TestUtil.GetTestOutputPath("test.svg")); }
void OnExpose(object sender, ExposeEventArgs args) { DrawingArea area = (DrawingArea)sender; Cairo.Context cairoContext = Gdk.CairoHelper.Create(area.GdkWindow); int width = area.Allocation.Width; int height = area.Allocation.Height; PixelDimensions = new Vector2i(width, height); AxisAlignedBox2d bounds = DrawingBounds; double sx = (double)width / bounds.Width; double sy = (double)height / bounds.Height; float scale = (float)Math.Min(sx, sy); // we apply this translate after scaling to pixel coords Vector2f pixC = Zoom * scale * (Vector2f)bounds.Center; Vector2f translate = new Vector2f(width / 2, height / 2) - pixC; using (var bitmap = new SKBitmap(width, height, SkiaUtil.ColorType(), SKAlphaType.Premul)) { IntPtr len; using (var skSurface = SKSurface.Create(bitmap.Info.Width, bitmap.Info.Height, SkiaUtil.ColorType(), SKAlphaType.Premul, bitmap.GetPixels(out len), bitmap.Info.RowBytes)) { var canvas = skSurface.Canvas; // update scene xform Func <Vector2d, Vector2f> ViewTransformF = (pOrig) => { Vector2f pNew = (Vector2f)pOrig - (Vector2f)bounds.Center; pNew = Zoom * scale * pNew; pNew += (Vector2f)pixC; pNew += translate + Zoom * PixelTranslate; pNew.y = canvas.ClipBounds.Height - pNew.y; return(pNew); }; DrawScene(canvas, ViewTransformF); Cairo.Surface cairoSurf = new Cairo.ImageSurface( bitmap.GetPixels(out len), Cairo.Format.Argb32, bitmap.Width, bitmap.Height, bitmap.Width * 4); cairoSurf.MarkDirty(); cairoContext.SetSourceSurface(cairoSurf, 0, 0); cairoContext.Paint(); cairoSurf.Dispose(); } } cairoContext.Dispose(); //return true; }
public AxisAlignedBox2d Bounds() { AxisAlignedBox2d box = AxisAlignedBox2d.Empty; foreach (Element e in vElements) { box.Contain(e.Bounds()); } return(box); }
public static Polygon2d ToPolygon(this AxisAlignedBox2d _box) { var p0 = _box.Min; var p1 = new Vector2d(_box.Max.x, _box.Min.y); var p2 = _box.Max; var p3 = new Vector2d(_box.Min.x, _box.Max.y); return(new Polygon2d(new List <Vector2d>() { p0, p1, p2, p3 })); }
public MakerbotAssembler(GCodeBuilder useBuilder, SingleMaterialFFFSettings settings) : base(useBuilder, settings.Machine) { Settings = settings as SingleMaterialFFFSettings; PositionBounds = new AxisAlignedBox2d(settings.Machine.BedSizeXMM, settings.Machine.BedSizeYMM); PositionBounds.Translate(-PositionBounds.Center); // [RMS] currently bed dimensions are hardcoded in header setup, and this trips bounds-checker. // So, disable this checking for now. EnableBoundsChecking = false; TravelGCode = 1; }
public MakerbotAssembler(GCodeBuilder useBuilder, SingleMaterialFFFSettings settings) : base(useBuilder, settings.Machine) { if (settings is MakerbotSettings == false) { throw new Exception("MakerbotAssembler: incorrect settings type!"); } Settings = settings as MakerbotSettings; PositionBounds = new AxisAlignedBox2d(settings.Machine.BedSizeXMM, settings.Machine.BedSizeYMM); PositionBounds.Translate(-PositionBounds.Center); TravelGCode = 1; }
public AxisAlignedBox2d GetBounds() { if (vertices.Count == 0) { return(AxisAlignedBox2d.Empty); } AxisAlignedBox2d box = new AxisAlignedBox2d(vertices[0]); for (int i = 1; i < vertices.Count; ++i) { box.Contain(vertices[i]); } return(box); }
/// <summary> /// Add explicit support points for any small floating polygons. /// These can be used at toolpathing time to ensure support for /// such areas, which otherwise might be lost (or insufficiently /// supported) by the standard techniques to detect support regions. /// </summary> public void AddMinZTipSupportPoints(double tipDiamThresh = 2.0, int nExtraLayers = 0) { List <Vector3d> tips = new List <Vector3d>(); SpinLock tiplock = new SpinLock(); int N = Slices.Count; gParallel.ForEach(Interval1i.FromToInclusive(1, N - 1), (li) => { PlanarSlice slice = Slices[li]; PlanarSlice prev = Slices[li - 1]; foreach (GeneralPolygon2d poly in slice.InputSolids) { AxisAlignedBox2d bounds = poly.Bounds; if (bounds.MaxDim > tipDiamThresh) { continue; } Vector2d c = bounds.Center; bool contained = false; foreach (var poly2 in prev.InputSolids) { if (poly2.Contains(c)) { contained = true; break; } } if (contained) { continue; } bool entered = false; tiplock.Enter(ref entered); tips.Add(new Vector3d(c.x, c.y, li)); tiplock.Exit(); } }); foreach (var tip in tips) { int layer_i = (int)tip.z; int add_to = Math.Min(N - 1, layer_i + nExtraLayers); for (int i = layer_i; i < add_to; ++i) { Slices[i].InputSupportPoints.Add(tip.xy); } } }
public AABBTree(IList <Triangle2d> mesh) { _box = mesh.GetBBox(); mesh = (_box.Height > _box.Width ? mesh.OrderBy(tra => tra.V0.y) : mesh.OrderBy(tra => tra.V0.x)).ToList(); if (mesh.Count != 1) { _isALeef = false; var midIndex = mesh.Count / 2; _left = new AABBTree(mesh.Take(midIndex).ToList()); _right = new AABBTree(mesh.Skip(midIndex).Take(mesh.Count - midIndex).ToList()); } else { _isALeef = true; _mesh = mesh.First(); } }
/// <summary> /// Precompute spatial caching information. This is not thread-safe. /// (Currently just list of bboxes for each solid/path.) /// </summary> public void BuildSpatialCaches() { int NS = Solids.Count; solid_bounds = new AxisAlignedBox2d[NS]; for (int i = 0; i < NS; ++i) { solid_bounds[i] = Solids[i].Bounds; } int NP = Paths.Count; path_bounds = new AxisAlignedBox2d[NP]; for (int i = 0; i < NP; ++i) { path_bounds[i] = Paths[i].Bounds; } spatial_caches_available = true; }
public static void CalculateUVs(this DMesh3 dMesh) { dMesh.EnableVertexUVs(Vector2f.Zero); OrthogonalPlaneFit3 orth = new OrthogonalPlaneFit3(dMesh.Vertices()); Frame3f frame = new Frame3f(orth.Origin, orth.Normal); AxisAlignedBox3d bounds = dMesh.CachedBounds; AxisAlignedBox2d boundsInFrame = new AxisAlignedBox2d(); for (int i = 0; i < 8; i++) { boundsInFrame.Contain(frame.ToPlaneUV((Vector3f)bounds.Corner(i), 3)); } Vector2f min = (Vector2f)boundsInFrame.Min; float width = (float)boundsInFrame.Width; float height = (float)boundsInFrame.Height; for (int i = 0; i < dMesh.VertexCount; i++) { Vector2f UV = frame.ToPlaneUV((Vector3f)dMesh.GetVertex(i), 3); UV.x = (UV.x - min.x) / width; UV.y = (UV.y - min.y) / height; dMesh.SetVertexUV(i, UV); } }
/// <summary> /// This function is supposed to take a set of spans in a plane and sort them /// into regions that can be filled with a polygon. Currently kind of clusters /// based on intersecting bboxes. Does not work. /// /// I think fundamentally it needs to look back at the input mesh, to see what /// is connected/not-connected. Or possibly use polygon winding number? Need /// to somehow define what the holes are... /// </summary> List <List <EdgeSpan> > sort_planar_spans(List <EdgeSpan> allspans, Vector3d normal) { var result = new List <List <EdgeSpan> >(); var polyFrame = new Frame3f(Vector3d.Zero, normal); int N = allspans.Count; var plines = new List <PolyLine2d>(); foreach (EdgeSpan span in allspans) { plines.Add(to_polyline(span, polyFrame)); } bool[] bad_poly = new bool[N]; for (int k = 0; k < N; ++k) { bad_poly[k] = false; // self_intersects(plines[k]); } bool[] used = new bool[N]; for (int k = 0; k < N; ++k) { if (used[k]) { continue; } bool is_bad = bad_poly[k]; AxisAlignedBox2d bounds = plines[k].Bounds; used[k] = true; var set = new List <int>() { k }; for (int j = k + 1; j < N; ++j) { if (used[j]) { continue; } AxisAlignedBox2d boundsj = plines[j].Bounds; if (bounds.Intersects(boundsj)) { used[j] = true; is_bad = is_bad || bad_poly[j]; bounds.Contain(boundsj); set.Add(j); } } if (is_bad == false) { var span_set = new List <EdgeSpan>(); foreach (int idx in set) { span_set.Add(allspans[idx]); } result.Add(span_set); } } return(result); }
public static Vector2d TranslatePointsCoordinationInsideTheBoundingBox(Vector2d point, AxisAlignedBox2d bounds) { Vector2d tmpPoint = new Vector2d(); tmpPoint.x = bounds.Min.x + (bounds.Max.x - bounds.Min.x) * point.x; tmpPoint.y = bounds.Min.y + (bounds.Max.y - bounds.Min.y) * point.y; return(tmpPoint); }
// exports svg w/ different containments of point set (created by slicing mesh) public static void containment_demo_svg() { DMesh3 mesh = TestUtil.LoadTestInputMesh("bunny_solid.obj"); MeshTransforms.Scale(mesh, 4); AxisAlignedBox3d meshBounds = mesh.CachedBounds; Vector3d origin = meshBounds.Center; origin -= 0.2 * meshBounds.Height * Vector3d.AxisY; Frame3f plane = new Frame3f(origin, new Vector3d(1, 3, 0).Normalized); MeshPlaneCut cut = new MeshPlaneCut(mesh, plane.Origin, plane.Z); cut.Cut(); AxisAlignedBox2d polyBounds = AxisAlignedBox2d.Empty; List <Polygon2d> polys = new List <Polygon2d>(); foreach (EdgeLoop loop in cut.CutLoops) { Polygon2d poly = new Polygon2d(); foreach (int vid in loop.Vertices) { poly.AppendVertex(mesh.GetVertex(vid).xz); } poly.Rotate(new Matrix2d(90, true), Vector2d.Zero); polys.Add(poly); polyBounds.Contain(poly.Bounds); } SVGWriter svg = new SVGWriter(); var polyStyle = SVGWriter.Style.Outline("red", 1.0f); var contStyle = SVGWriter.Style.Outline("black", 1.0f); for (int k = 0; k < 3; ++k) { double shift = (k == 2) ? 1.4f : 1.1f; Vector2d tx = (k - 1) * (polyBounds.Width * shift) * Vector2d.AxisX; List <Vector2d> pts = new List <Vector2d>(); foreach (Polygon2d poly in polys) { var p2 = new Polygon2d(poly).Translate(tx); pts.AddRange(p2.Vertices); svg.AddPolygon(p2, polyStyle); } if (k == 0) { ConvexHull2 hull = new ConvexHull2(pts, 0.001, QueryNumberType.QT_DOUBLE); svg.AddPolygon(hull.GetHullPolygon(), contStyle); } else if (k == 1) { ContMinBox2 contbox = new ContMinBox2(pts, 0.001, QueryNumberType.QT_DOUBLE, false); svg.AddPolygon(new Polygon2d(contbox.MinBox.ComputeVertices()), contStyle); } else if (k == 2) { ContMinCircle2 contcirc = new ContMinCircle2(pts); svg.AddCircle(contcirc.Result, contStyle); } } svg.Write(TestUtil.GetTestOutputPath("contain_demos.svg")); }
void OnExpose(object sender, ExposeEventArgs args) { DrawingArea area = (DrawingArea)sender; Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow); int width = area.Allocation.Width; int height = area.Allocation.Height; AxisAlignedBox3d bounds3 = Stack.Bounds; AxisAlignedBox2d bounds = (bounds3 == AxisAlignedBox3d.Empty) ? new AxisAlignedBox2d(0, 0, 500, 500) : new AxisAlignedBox2d(bounds3.Min.x, bounds3.Min.y, bounds3.Max.x, bounds3.Max.y); double sx = (double)width / bounds.Width; double sy = (double)height / bounds.Height; float scale = (float)Math.Min(sx, sy); // we apply this translate after scaling to pixel coords Vector2f pixC = Zoom * scale * (Vector2f)bounds.Center; Vector2f translate = new Vector2f(width / 2, height / 2) - pixC; using (var bitmap = new SKBitmap(width, height, SkiaUtil.ColorType(), SKAlphaType.Premul)) { IntPtr len; using (var skSurface = SKSurface.Create(bitmap.Info.Width, bitmap.Info.Height, SkiaUtil.ColorType(), SKAlphaType.Premul, bitmap.GetPixels(out len), bitmap.Info.RowBytes)) { var canvas = skSurface.Canvas; canvas.Clear(SkiaUtil.Color(240, 240, 240, 255)); Func <Vector2d, Vector2f> xformF = (pOrig) => { Vector2f pNew = (Vector2f)pOrig; pNew -= (Vector2f)bounds.Center; pNew = Zoom * scale * pNew; pNew += (Vector2f)pixC; pNew += translate + Zoom * Translate; pNew.y = canvas.LocalClipBounds.Height - pNew.y; return(pNew); }; Func <Vector2d, SKPoint> mapToSkiaF = (pOrig) => { Vector2f p = xformF(pOrig); return(new SKPoint(p.x, p.y)); }; using (var paint = new SKPaint()) { SKBitmap sliceImg = get_slice_image(currentLayer); float w = sliceImg.Width / CurrentDPIMM, h = sliceImg.Height / CurrentDPIMM; w *= Zoom * scale; h *= Zoom * scale; SKPoint sliceCenter = mapToSkiaF(Vector2d.Zero); SKRect drawRect = new SKRect( sliceCenter.X - w / 2, sliceCenter.Y - h / 2, sliceCenter.X + w / 2, sliceCenter.Y + h / 2); canvas.DrawBitmap(sliceImg, drawRect, paint); paint.IsAntialias = true; paint.Style = SKPaintStyle.Stroke; PlanarSlice slice = Stack.Slices[currentLayer]; paint.Color = SkiaUtil.Color(255, 0, 0, 255);; paint.StrokeWidth = 2; foreach (GeneralPolygon2d poly in slice.Solids) { SKPath path = SkiaUtil.ToSKPath(poly, mapToSkiaF); canvas.DrawPath(path, paint); } } Cairo.Surface surface = new Cairo.ImageSurface( bitmap.GetPixels(out len), Cairo.Format.Argb32, bitmap.Width, bitmap.Height, bitmap.Width * 4); surface.MarkDirty(); cr.SetSourceSurface(surface, 0, 0); cr.Paint(); } } //return true; }
protected override void SolveInstance(IGH_DataAccess DA) { DMesh3_goo goo = null; double lattice_radius = 0.05; double lattice_spacing = 0.4; double shell_thickness = 0.05; int mesh_resolution = 64; DA.GetData(0, ref goo); DA.GetData(1, ref lattice_radius); DA.GetData(2, ref lattice_spacing); DA.GetData(3, ref shell_thickness); DA.GetData(4, ref mesh_resolution); DMesh3 mesh = new DMesh3(goo.Value); var shellMeshImplicit = g3ghUtil.MeshToImplicit(mesh, 128, shell_thickness); double max_dim = mesh.CachedBounds.MaxDim; AxisAlignedBox3d bounds = new AxisAlignedBox3d(mesh.CachedBounds.Center, max_dim / 2); bounds.Expand(2 * lattice_spacing); AxisAlignedBox2d element = new AxisAlignedBox2d(lattice_spacing); AxisAlignedBox2d bounds_xy = new AxisAlignedBox2d(bounds.Min.xy, bounds.Max.xy); AxisAlignedBox2d bounds_xz = new AxisAlignedBox2d(bounds.Min.xz, bounds.Max.xz); AxisAlignedBox2d bounds_yz = new AxisAlignedBox2d(bounds.Min.yz, bounds.Max.yz); List <BoundedImplicitFunction3d> Tiling = new List <BoundedImplicitFunction3d>(); foreach (g3.Vector2d uv in TilingUtil.BoundedRegularTiling2(element, bounds_xy, 0)) { Segment3d seg = new Segment3d(new g3.Vector3d(uv.x, uv.y, bounds.Min.z), new g3.Vector3d(uv.x, uv.y, bounds.Max.z)); Tiling.Add(new ImplicitLine3d() { Segment = seg, Radius = lattice_radius }); } foreach (g3.Vector2d uv in TilingUtil.BoundedRegularTiling2(element, bounds_xz, 0)) { Segment3d seg = new Segment3d(new g3.Vector3d(uv.x, bounds.Min.y, uv.y), new g3.Vector3d(uv.x, bounds.Max.y, uv.y)); Tiling.Add(new ImplicitLine3d() { Segment = seg, Radius = lattice_radius }); } foreach (g3.Vector2d uv in TilingUtil.BoundedRegularTiling2(element, bounds_yz, 0)) { Segment3d seg = new Segment3d(new g3.Vector3d(bounds.Min.x, uv.x, uv.y), new g3.Vector3d(bounds.Max.x, uv.x, uv.y)); Tiling.Add(new ImplicitLine3d() { Segment = seg, Radius = lattice_radius }); } ImplicitNaryUnion3d lattice = new ImplicitNaryUnion3d() { Children = Tiling }; ImplicitIntersection3d lattice_clipped = new ImplicitIntersection3d() { A = lattice, B = shellMeshImplicit }; g3.MarchingCubes c = new g3.MarchingCubes(); c.Implicit = lattice_clipped; c.RootMode = g3.MarchingCubes.RootfindingModes.LerpSteps; c.RootModeSteps = 5; c.Bounds = lattice_clipped.Bounds(); c.CubeSize = c.Bounds.MaxDim / mesh_resolution; c.Bounds.Expand(3 * c.CubeSize); c.Generate(); MeshNormals.QuickCompute(c.Mesh); DA.SetData(0, c.Mesh); }
// Finds set of "solid" regions - eg boundary loops with interior holes. // Result has outer loops being clockwise, and holes counter-clockwise public SolidRegionInfo FindSolidRegions(double fSimplifyDeviationTol = 0.1, bool bWantCurveSolids = true) { List <SmoothLoopElement> validLoops = new List <SmoothLoopElement>(LoopsItr()); int N = validLoops.Count; // precompute bounding boxes int maxid = 0; foreach (var v in validLoops) { maxid = Math.Max(maxid, v.ID + 1); } AxisAlignedBox2d[] bounds = new AxisAlignedBox2d[maxid]; foreach (var v in validLoops) { bounds[v.ID] = v.Bounds(); } // copy polygons, simplify if desired double fClusterTol = 0.0; // don't do simple clustering, can lose corners double fDeviationTol = fSimplifyDeviationTol; Polygon2d[] polygons = new Polygon2d[maxid]; foreach (var v in validLoops) { Polygon2d p = new Polygon2d(v.polygon); if (fClusterTol > 0 || fDeviationTol > 0) { p.Simplify(fClusterTol, fDeviationTol); } polygons[v.ID] = p; } // sort by bbox containment to speed up testing (does it??) validLoops.Sort((x, y) => { return(bounds[x.ID].Contains(bounds[y.ID]) ? -1 : 1); }); // containment sets bool[] bIsContained = new bool[N]; Dictionary <int, List <int> > ContainSets = new Dictionary <int, List <int> >(); Dictionary <int, List <int> > ContainedParents = new Dictionary <int, List <int> >(); // construct containment sets for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; Polygon2d polyi = polygons[loopi.ID]; for (int j = 0; j < N; ++j) { if (i == j) { continue; } SmoothLoopElement loopj = validLoops[j]; Polygon2d polyj = polygons[loopj.ID]; // cannot be contained if bounds are not contained if (bounds[loopi.ID].Contains(bounds[loopj.ID]) == false) { continue; } // any other early-outs?? if (polyi.Contains(polyj)) { if (ContainSets.ContainsKey(i) == false) { ContainSets.Add(i, new List <int>()); } ContainSets[i].Add(j); bIsContained[j] = true; if (ContainedParents.ContainsKey(j) == false) { ContainedParents.Add(j, new List <int>()); } ContainedParents[j].Add(i); } } } List <GeneralPolygon2d> polysolids = new List <GeneralPolygon2d>(); List <PlanarSolid2d> solids = new List <PlanarSolid2d>(); HashSet <SmoothLoopElement> used = new HashSet <SmoothLoopElement>(); Dictionary <SmoothLoopElement, int> LoopToOuterIndex = new Dictionary <SmoothLoopElement, int>(); List <int> ParentsToProcess = new List <int>(); // The following is a lot of code but it is very similar, just not clear how // to refactor out the common functionality // 1) we find all the top-level uncontained polys and add them to the final polys list // 2a) for any poly contained in those parent-polys, that is not also contained in anything else, // add as hole to that poly // 2b) remove all those used parents & holes from consideration // 2c) now find all the "new" top-level polys // 3) repeat 2a-c until done all polys // 4) any remaining polys must be interior solids w/ no holes // **or** weird leftovers like intersecting polys... // add all top-level uncontained polys for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; if (bIsContained[i]) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } GeneralPolygon2d g = new GeneralPolygon2d { Outer = outer_poly }; PlanarSolid2d s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } int idx = polysolids.Count; LoopToOuterIndex[loopi] = idx; used.Add(loopi); if (ContainSets.ContainsKey(i)) { ParentsToProcess.Add(i); } polysolids.Add(g); if (bWantCurveSolids) { solids.Add(s); } } // keep iterating until we processed all parent loops while (ParentsToProcess.Count > 0) { List <int> ContainersToRemove = new List <int>(); // now for all top-level polys that contain children, add those children // as long as they do not have multiple contain-parents foreach (int i in ParentsToProcess) { SmoothLoopElement parentloop = validLoops[i]; int outer_idx = LoopToOuterIndex[parentloop]; List <int> children = ContainSets[i]; foreach (int childj in children) { SmoothLoopElement childLoop = validLoops[childj]; Debug.Assert(used.Contains(childLoop) == false); // skip multiply-contained children List <int> parents = ContainedParents[childj]; if (parents.Count > 1) { continue; } Polygon2d hole_poly = polygons[childLoop.ID]; IParametricCurve2d hole_loop = (bWantCurveSolids) ? childLoop.source.Clone() : null; if (hole_poly.IsClockwise) { hole_poly.Reverse(); if (bWantCurveSolids) { hole_loop.Reverse(); } } try { polysolids[outer_idx].AddHole(hole_poly); if (hole_loop != null) { solids[outer_idx].AddHole(hole_loop); } } catch { // don't add this hole - must intersect or something? // We should have caught this earlier! } used.Add(childLoop); if (ContainSets.ContainsKey(childj)) { ContainersToRemove.Add(childj); } } ContainersToRemove.Add(i); } // remove all containers that are no longer valid foreach (int ci in ContainersToRemove) { ContainSets.Remove(ci); // have to remove from each ContainedParents list List <int> keys = new List <int>(ContainedParents.Keys); foreach (int j in keys) { if (ContainedParents[j].Contains(ci)) { ContainedParents[j].Remove(ci); } } } ParentsToProcess.Clear(); // ok now find next-level uncontained parents... for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; if (used.Contains(loopi)) { continue; } if (ContainSets.ContainsKey(i) == false) { continue; } List <int> parents = ContainedParents[i]; if (parents.Count > 0) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } GeneralPolygon2d g = new GeneralPolygon2d { Outer = outer_poly }; PlanarSolid2d s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } int idx = polysolids.Count; LoopToOuterIndex[loopi] = idx; used.Add(loopi); if (ContainSets.ContainsKey(i)) { ParentsToProcess.Add(i); } polysolids.Add(g); if (bWantCurveSolids) { solids.Add(s); } } } // any remaining loops must be top-level for (int i = 0; i < N; ++i) { SmoothLoopElement loopi = validLoops[i]; if (used.Contains(loopi)) { continue; } Polygon2d outer_poly = polygons[loopi.ID]; IParametricCurve2d outer_loop = (bWantCurveSolids) ? loopi.source.Clone() : null; if (outer_poly.IsClockwise == false) { outer_poly.Reverse(); if (bWantCurveSolids) { outer_loop.Reverse(); } } GeneralPolygon2d g = new GeneralPolygon2d { Outer = outer_poly }; PlanarSolid2d s = new PlanarSolid2d(); if (bWantCurveSolids) { s.SetOuter(outer_loop, true); } polysolids.Add(g); if (bWantCurveSolids) { solids.Add(s); } } return(new SolidRegionInfo() { Polygons = polysolids, Solids = (bWantCurveSolids) ? solids : null }); }
public static void Main(string[] args) { GtkUtil.CheckWindowsGtk(); ExceptionManager.UnhandledException += delegate(UnhandledExceptionArgs expArgs) { Console.WriteLine(expArgs.ExceptionObject.ToString()); expArgs.ExitApplication = true; }; Gtk.Application.Init(); MainWindow = new Window("gsSlicerViewer"); MainWindow.SetDefaultSize(900, 600); MainWindow.SetPosition(WindowPosition.Center); MainWindow.DeleteEvent += delegate { Gtk.Application.Quit(); }; string sPath = "../../../sample_files/disc_single_layer.gcode"; //string sPath = "../../../sample_files/disc_0p6mm.gcode"; //string sPath = "../../../sample_files/square_linearfill.gcode"; //string sPath = "../../../sample_files/thin_hex_test_part.gcode"; //string sPath = "../../../sample_files/box_infill_50.gcode"; //string sPath = "../../../sample_files/tube_adapter.gcode"; //string sPath = "../../../sample_files/ring_2p2_makerbot.gcode"; //string sPath = "/Users/rms/Desktop/print_experiment/cura_ring_2p2.gcode"; //string sPath = "/Users/rms/Desktop/print_experiment/slic3r_ring_2p2.gcode"; DMesh3 readMesh = null; //GCodeFile genGCode = MakerbotTests.SimpleFillTest(); //GCodeFile genGCode = MakerbotTests.SimpleShellsTest(); //GCodeFile genGCode = MakerbotTests.InfillBoxTest(); //GeneralPolygon2d poly = GetPolygonFromMesh("../../../sample_files/bunny_open.obj"); //GCodeFile genGCode = MakerbotTests.ShellsPolygonTest(poly); //GCodeFile genGCode = MakerbotTests.StackedPolygonTest(poly, 2); //GCodeFile genGCode = MakerbotTests.StackedScaledPolygonTest(poly, 20, 0.5); readMesh = StandardMeshReader.ReadMesh("../../../sample_files/cotan_cylinder.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/bunny_solid_2p5cm.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/bunny_solid_5cm_min.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/basic_step.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/slab_5deg.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/sphere_angles_1cm.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/inverted_cone_1.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/tube_adapter.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/tube_1.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/50x50x1_box.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/crop_bracket.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/thinwall2.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/box_and_cylsheet.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/box_and_opensheet.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/radial_fins.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/radial_fins_larger.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/bunny_hollow_5cm.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/notch_test_1.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/square_minus_shapes.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/variable_thins.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/arrow_posx.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/blobby_shape.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/socket_ros_simplified.obj"); //readMesh = StandardMeshReader.ReadMesh("c:\\scratch\\bunny_fixed_flat.obj"); //MeshUtil.ScaleMesh(readMesh, Frame3f.Identity, 1.1f*Vector3f.One); //readMesh = StandardMeshReader.ReadMesh("c:\\scratch\\ARCHFORM_EXPORT_stage_12_lower.stl"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/unsupported_slab_5deg.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/overhang_slab_1.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/edge_overhang.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/support_tilted_cone.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/support_mintip.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/support_mintip_vtx.obj"); //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/tilted_thin_slab.obj"); //readMesh = StandardMeshReader.ReadMesh("C:\\meshes\\user_bugs\\inverted_part.obj"); //readMesh = StandardMeshReader.ReadMesh("C:\\meshes\\user_bugs\\tail_tiny_support_dots.obj"); //readMesh = StandardMeshReader.ReadMesh("C:\\meshes\\user_bugs\\cone_inner.obj"); // interesting test case for clipselfoverlaps and scheduler //readMesh = StandardMeshReader.ReadMesh("../../../sample_files/Slim_Type1.stl"); DMesh3 cavityMesh = null; DMesh3 supportMesh = null; //supportMesh = StandardMeshReader.ReadMesh("../../../sample_files/edge_overhang_support.obj"); // rotate to be z-up //MeshTransforms.Rotate(readMesh, Vector3d.Zero, Quaternionf.AxisAngleD(Vector3f.AxisX, 90)); //if ( supportMesh != null ) // MeshTransforms.Rotate(supportMesh, Vector3d.Zero, Quaternionf.AxisAngleD(Vector3f.AxisX, 90)); //readMesh = CalibrationModelGenerator.MakePrintStepSizeTest(10.0f, 10.0f, 0.1, 1.0, 10); //DMesh3[] meshComponents = MeshConnectedComponents.Separate(readMesh); DMesh3[] meshComponents = new DMesh3[] { readMesh }; PrintMeshAssembly meshes = new PrintMeshAssembly(); meshes.AddMeshes(meshComponents); if (cavityMesh != null) { meshes.AddMesh(cavityMesh, PrintMeshOptions.Cavity()); } if (supportMesh != null) { meshes.AddMesh(supportMesh, PrintMeshOptions.Support()); } AxisAlignedBox3d bounds = meshes.TotalBounds; AxisAlignedBox2d bounds2 = new AxisAlignedBox2d(bounds.Center.xy, bounds.Width / 2, bounds.Height / 2); View = new SliceViewCanvas(); MainWindow.Add(View); if (readMesh != null) { // generate gcode file for mesh sPath = GenerateGCodeForMeshes(meshes); } if (SHOW_RELOADED_GCODE_PATHS) { LoadGeneratedGCodeFile(sPath); } //GenerateGCodeForSliceFile("c:\\scratch\\output.gslice"); MainWindow.KeyReleaseEvent += Window_KeyReleaseEvent; // support drag-drop Gtk.TargetEntry[] target_table = new TargetEntry[] { new TargetEntry("text/uri-list", 0, 0), }; Gtk.Drag.DestSet(MainWindow, DestDefaults.All, target_table, Gdk.DragAction.Copy); MainWindow.DragDataReceived += MainWindow_DragDataReceived;; MainWindow.ShowAll(); Gtk.Application.Run(); }
public static void test_uv_insert_string() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_xy_25x25.obj"); mesh.EnableVertexUVs(Vector2f.Zero); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); PolygonFont2d font = PolygonFont2d.ReadFont("c:\\scratch\\font.bin"); //List<GeneralPolygon2d> letter = new List<GeneralPolygon2d>(font.Characters.First().Value.Polygons); //double targetWidth = 20.0f; List <GeneralPolygon2d> letter = font.GetCharacter('a'); double targetWidth = 10.0f; AxisAlignedBox2d bounds = font.MaxBounds; Vector2d center = bounds.Center; Vector2d scale2d = (targetWidth / font.MaxBounds.Width) * new Vector2d(1, 1); for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = new GeneralPolygon2d(letter[li]); gp.Scale(scale2d, center); gp.Translate(-center); letter[li] = gp; } List <MeshFaceSelection> letter_interiors = new List <MeshFaceSelection>(); bool bSimplify = true; for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = letter[li]; MeshInsertUVPolyCurve outer = new MeshInsertUVPolyCurve(mesh, gp.Outer); Util.gDevAssert(outer.Validate() == ValidationStatus.Ok); outer.Apply(); if (bSimplify) { outer.Simplify(); } List <MeshInsertUVPolyCurve> holes = new List <MeshInsertUVPolyCurve>(gp.Holes.Count); for (int hi = 0; hi < gp.Holes.Count; ++hi) { MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, gp.Holes[hi]); Util.gDevAssert(insert.Validate() == ValidationStatus.Ok); insert.Apply(); if (bSimplify) { insert.Simplify(); } holes.Add(insert); } // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; EdgeLoop outer_loop = outer.Loops[0]; for (int i = 0; i < outer_loop.EdgeCount; ++i) { if (!mesh.IsEdge(outer_loop.Edges[i])) { continue; } Index2i et = mesh.GetEdgeT(outer_loop.Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = gp.Outer.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = gp.Outer.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // make list of all outer & hole edges HashSet <int> loopEdges = new HashSet <int>(outer_loop.Edges); foreach (var insertion in holes) { foreach (int eid in insertion.Loops[0].Edges) { loopEdges.Add(eid); } } // flood-fill inside loop from seed triangle MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); letter_interiors.Add(sel); } // extrude regions Func <Vector3d, Vector3f, int, Vector3d> OffsetF = (v, n, i) => { return(v + Vector3d.AxisZ); }; foreach (var interior in letter_interiors) { MeshExtrudeFaces extrude = new MeshExtrudeFaces(mesh, interior); extrude.ExtrudedPositionF = OffsetF; extrude.Extrude(); } TestUtil.WriteTestOutputMesh(mesh, "insert_uv_string.obj"); }
public static List <GeneralPolygon2d> ConvertFromClipper(CPolygonList clipper_polys, double nIntScale) { List <GeneralPolygon2d> result = new List <GeneralPolygon2d>(); try { // convert clipper polys to Polygon2d List <Polygon2d> polys = new List <Polygon2d>(); int N = clipper_polys.Count; for (int i = 0; i < N; ++i) { Polygon2d poly = ConvertFromClipper(clipper_polys[i], nIntScale); if (poly != null) { polys.Add(poly); } } // sort polygons into outer/holes // [TODO] clipper can figure this out for us...perhaps faster?? // find the 'outer' polygons. Here we are assuming // that outer polygons are CCW... bool[] done = new bool[N]; Array.Clear(done, 0, N); for (int i = 0; i < N; ++i) { if (polys[i].IsClockwise == false) { GeneralPolygon2d gp = new GeneralPolygon2d(); gp.Outer = polys[i]; result.Add(gp); done[i] = true; } } // compute bboxes AxisAlignedBox2d[] outerBounds = new AxisAlignedBox2d[result.Count]; for (int i = 0; i < result.Count; ++i) { outerBounds[i] = result[i].Outer.GetBounds(); } // remaining polygons are holes. Figure out which outer // they belong too. Only difficult if there is more than one option. for (int i = 0; i < N; ++i) { if (done[i]) { continue; } if (result.Count == 1) { result[0].AddHole(polys[i], false); done[i] = true; continue; } AxisAlignedBox2d box = polys[i].GetBounds(); for (int j = 0; j < result.Count; ++j) { if (outerBounds[j].Contains(box) == false) { continue; } if (result[j].Outer.Contains(polys[i])) { result[j].AddHole(polys[i], false); } done[i] = true; } // uh-oh...now what? perhaps should force a full N-pair test/sort if this happens? if (done[i] == false) { System.Diagnostics.Debug.WriteLine("ClipperUtil.ConvertFromClipper: could not find parent for polygon " + i.ToString()); } } } catch /*( Exception e )*/ { //System.Diagnostics.Debug.WriteLine("ClipperUtil.ConvertFromClipper: caught exception: " + e.Message); } return(result); }
public bool Compute() { AxisAlignedBox2d bounds = Polygon.Bounds; ScaleGridIndexer2 index = new ScaleGridIndexer2() { CellSize = TileSize }; Vector2i min = index.ToGrid(bounds.Min) - Vector2i.One; Vector2i max = index.ToGrid(bounds.Max); List <Tile> Tiles = new List <Tile>(); for (int y = min.y; y <= max.y; ++y) { for (int x = min.x; x <= max.x; ++x) { Tile t = new Tile(); t.index = new Vector2i(x, y); Vector2d tile_min = index.FromGrid(t.index); Vector2d tile_max = index.FromGrid(t.index + Vector2i.One); AxisAlignedBox2d tile_box = new AxisAlignedBox2d(tile_min, tile_max); tile_box.Expand(TileOverlap); t.poly = new Polygon2d(new Vector2d[] { tile_box.GetCorner(0), tile_box.GetCorner(1), tile_box.GetCorner(2), tile_box.GetCorner(3) }); Tiles.Add(t); } } gParallel.ForEach(Tiles, (tile) => { tile.regions = ClipperUtil.PolygonBoolean(Polygon, new GeneralPolygon2d(tile.poly), ClipperUtil.BooleanOp.Intersection); }); List <ICurvesFillPolygon> all_fills = new List <ICurvesFillPolygon>(); foreach (Tile t in Tiles) { if (t.regions.Count == 0) { continue; } t.fills = new ICurvesFillPolygon[t.regions.Count]; for (int k = 0; k < t.regions.Count; ++k) { t.fills[k] = TileFillGeneratorF(t.regions[k], t.index); if (t.fills[k] != null) { all_fills.Add(t.fills[k]); } } } gParallel.ForEach(all_fills, (fill) => { fill.Compute(); }); FillCurves = new List <FillCurveSet2d>(); foreach (ICurvesFillPolygon fill in all_fills) { List <FillCurveSet2d> result = fill.GetFillCurves(); if (result != null && result.Count > 0) { FillCurves.AddRange(result); } } return(true); }
public bool Solve(int MaxRounds = 1024) { AxisAlignedBox2d TargetBounds = Target.Bounds; Box2d fitBounds = new Box2d(Fit.GetBounds()); InputTranslate = -fitBounds.Center; int N = Fit.VertexCount; Polygon2d FitCentered = new Polygon2d(Fit); for (int k = 0; k < N; ++k) { FitCentered[k] = Fit[k] + InputTranslate; } fitBounds = new Box2d(FitCentered.GetBounds()); Polygon2d FitXForm = new Polygon2d(FitCentered); Random r = new Random(31337); int ri = 0; bool solved = false; while (ri++ < MaxRounds && solved == false) { Vector2d center = random_point_in_target(r, TargetBounds.SampleT, Target.Contains); Matrix2d rotate = Matrix2d.Identity; if (EnableRotate) { Util.gDevAssert(EnableRandomRotations == false); double rotAngle = ValidRotationsDeg[r.Next() % ValidRotationsDeg.Length]; rotate.SetToRotationDeg(rotAngle); } for (int k = 0; k < N; ++k) { FitXForm[k] = rotate * (ApplyScale * FitCentered[k]) + center; } if (!Target.Outer.Contains(FitXForm)) { continue; } if (Target.Intersects(FitXForm)) { continue; } FitBounds = fitBounds; FitBounds.RotateAxes(rotate); FitBounds.Translate(center); FitRotation = rotate; FitPolygon = FitXForm; solved = true; } return(solved); }