public static PathStorage PolygonToPathStorage(Polygons polygons) { PathStorage output = new PathStorage(); foreach (Polygon polygon in polygons) { bool first = true; foreach (IntPoint point in polygon) { if (first) { output.Add(point.X, point.Y, ShapePath.FlagsAndCommand.CommandMoveTo); first = false; } else { output.Add(point.X, point.Y, ShapePath.FlagsAndCommand.CommandLineTo); } } output.ClosePolygon(); } output.Add(0, 0, ShapePath.FlagsAndCommand.CommandStop); return output; }
public static void AddAll(this Polygons polygons, Polygons other) { for (int n = 0; n < other.Count; n++) { polygons.Add(other[n]); } }
public AvoidCrossingPerimeters(Polygons bounderyPolygons) { this.bounderyPolygons = bounderyPolygons; minXPosition = new long[bounderyPolygons.Count]; maxXPosition = new long[bounderyPolygons.Count]; indexOfMinX = new int[bounderyPolygons.Count]; indexOfMaxX = new int[bounderyPolygons.Count]; }
public void AddPolygons(Polygons poly) { if (poly.Count == 0) return; PolyInfo pi = new PolyInfo(); pi.polygons = poly; pi.si = style.Clone(); PolyInfoList.Add(pi); }
private void LoadButton_Click(object sender, EventArgs e) { if (openFileDialog.ShowDialog() == DialogResult.OK) { pathText.Text = openFileDialog.FileName; _polygons = DataLoader.LoadPolygons(openFileDialog.OpenFile()); } }
public void Calculate(Polygons polys) { min = new IntPoint(long.MaxValue, long.MaxValue); max = new IntPoint(long.MinValue, long.MinValue); for (int i = 0; i < polys.Count; i++) { for (int j = 0; j < polys[i].Count; j++) { if (min.X > polys[i][j].X) min.X = polys[i][j].X; if (min.Y > polys[i][j].Y) min.Y = polys[i][j].Y; if (max.X < polys[i][j].X) max.X = polys[i][j].X; if (max.Y < polys[i][j].Y) max.Y = polys[i][j].Y; } } }
public static void GenerateLinePaths(Polygons polygonToInfill, ref Polygons infillLinesToPrint, int lineSpacing, int infillExtendIntoPerimeter_um, double rotation, long rotationOffset = 0) { if (polygonToInfill.Count > 0) { Polygons outlines = polygonToInfill.Offset(infillExtendIntoPerimeter_um); if (outlines.Count > 0) { PointMatrix matrix = new PointMatrix(-(rotation + 90)); // we are rotating the part so we rotate by the negative so the lines go the way we expect outlines.ApplyMatrix(matrix); Aabb boundary = new Aabb(outlines); boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing - rotationOffset; int xLineCount = (int)((boundary.max.X - boundary.min.X + (lineSpacing - 1)) / lineSpacing); Polygons unclipedPatern = new Polygons(); long firstX = boundary.min.X / lineSpacing * lineSpacing; for (int lineIndex = 0; lineIndex < xLineCount; lineIndex++) { Polygon line = new Polygon(); line.Add(new IntPoint(firstX + lineIndex * lineSpacing, boundary.min.Y)); line.Add(new IntPoint(firstX + lineIndex * lineSpacing, boundary.max.Y)); unclipedPatern.Add(line); } PolyTree ret = new PolyTree(); Clipper clipper = new Clipper(); clipper.AddPaths(unclipedPatern, PolyType.ptSubject, false); clipper.AddPaths(outlines, PolyType.ptClip, true); clipper.Execute(ClipType.ctIntersection, ret, PolyFillType.pftPositive, PolyFillType.pftEvenOdd); Polygons newSegments = Clipper.OpenPathsFromPolyTree(ret); PointMatrix inversematrix = new PointMatrix((rotation + 90)); newSegments.ApplyMatrix(inversematrix); infillLinesToPrint.AddRange(newSegments); } } }
//------------------------------------------------------------------------------ public static void ReversePolygons(Polygons polys) { polys.ForEach(delegate(Polygon poly) { poly.Reverse(); }); }
public IEnumerable <Polygon> GetBackwardsPolygons(float epsilon = 0.001f) { var origin = Origin; return(Polygons.Where(x => x.Plane.OnPlane(origin, epsilon) > 0)); }
public FestaMapRootPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator, IShowFestaMap showFesta, IMapAssociated mapParam) { _eventAggregator = eventAggregator; _navigationService = navigationService; showFestaUsecase = showFesta; _mapParams = mapParam; InfoWindowClickedCommand = new DelegateCommand <InfoWindowClickedEventArgs>(async(pin) => { var region = pin.Pin.Tag as MapRegion; if (region == null) { return; } if (IsGlobalMap) { if (region.MapObjectType.HasFlag(MapObjectEnum.STAGE)) { await _navigationService.NavigateAsync( nameof(StageEventListRootPageViewModel).GetViewNameFromRule(), StageEventListRootPageViewModel.GetNavigationParameter(region.Id, region.Name)); } else { var type = region.MapObjectType.HasFlag(MapObjectEnum.EXHIBITION) ? PlanningTypeEnum.EXHIBITION : PlanningTypeEnum.STALL; await _navigationService.NavigateAsync( "RegionSpecificPlanningListPage", PlanningListRootPageViewModel.GetNavigationParameter(region.Id, region.Name, type)); } } }); _eventAggregator.GetEvent <PolygonClickedEvent>().Subscribe((pin) => { SelectedPin.Value = pin.Tag as Pin; MoveToRegionRequest.MoveToRegion( MapSpan.FromCenterAndRadius(SelectedPin.Value.Position, Distance.FromMeters(100))); }).AddTo(this.Disposable); showFesta.Pins.ToCollectionChanged <Pin>() .Subscribe(change => { switch (change.Action) { case System.Collections.Specialized.NotifyCollectionChangedAction.Add: Pins?.Add(change.Value); break; case System.Collections.Specialized.NotifyCollectionChangedAction.Move: break; case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: Pins?.Remove(change.Value); break; case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: break; case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: Pins?.Clear(); break; } }).AddTo(this.Disposable); showFesta.Polygons.ToCollectionChanged <Polygon>() .Subscribe(change => { switch (change.Action) { case System.Collections.Specialized.NotifyCollectionChangedAction.Add: Polygons?.Add(change.Value); break; case System.Collections.Specialized.NotifyCollectionChangedAction.Move: break; case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: Polygons?.Remove(change.Value); break; case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: break; case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: Polygons?.Clear(); break; } }).AddTo(this.Disposable); // For iOS _eventAggregator.GetEvent <TabbedPageOpendEvent>().Subscribe((ev) => { if (ev.Name != this.GetType().Name.Replace("ViewModel", "")) { return; } IsShowingUser.Value = true; showFestaUsecase.InitializeAllMapObjects(); if (IsGlobalMap) { MoveToRegionRequest.MoveToRegion( MapSpan.FromCenterAndRadius( new Position(_mapParams.MapCenterLangitude, _mapParams.MapCenterLongitude), Distance.FromMeters(185))); } }).AddTo(this.Disposable); _eventAggregator.GetEvent <LocationPermissionRequestResultEvent>() .Subscribe((ev) => { IsShowingUser.Value = ev.Granted; }); Title.AddTo(this.Disposable); SelectedPin.AddTo(this.Disposable); IsShowingUser.AddTo(this.Disposable); }
public static Polygons ProcessEvenOdd(this Polygons polygons) { Polygons ret = new Polygons(); Clipper clipper = new Clipper(); clipper.AddPaths(polygons, PolyType.ptSubject, true); clipper.Execute(ClipType.ctUnion, ret); return ret; }
public static Polygons DeepCopy(this Polygons polygons) { Polygons deepCopy = new Polygons(); foreach (Polygon poly in polygons) { deepCopy.Add(new Polygon(poly)); } return deepCopy; }
public static Polygons CreateFromString(string polygonsPackedString) { Polygons output = new Polygons(); string[] polygons = polygonsPackedString.Split('|'); foreach (string polygonString in polygons) { Polygon nextPoly = PolygonHelper.CreateFromString(polygonString); if (nextPoly.Count > 0) { output.Add(nextPoly); } } return output; }
//------------------------------------------------------------------------------ public static void PolyTreeToPolygons(PolyTree polytree, Polygons polygons) { polygons.Clear(); polygons.Capacity = polytree.Total; AddPolyNodeToPolygons(polytree, polygons); }
bool LoadFromFile(string filename, Polygons ppg, double scale // = 0 , int xOffset // = 0 , int yOffset) // = 0) { double scaling = Math.Pow(10, scale); ppg.Clear(); if (!Alt.IO.File.Exists(filename)) { return(false); } System.IO.StreamReader sr = new System.IO.StreamReader(filename); if (sr == null) { return(false); } string line; if ((line = sr.ReadLine()) == null) { return(false); } int polyCnt, vertCnt; if (!Int32.TryParse(line, out polyCnt) || polyCnt < 0) { return(false); } ppg.Capacity = polyCnt; for (int i = 0; i < polyCnt; i++) { if ((line = sr.ReadLine()) == null) { return(false); } if (!Int32.TryParse(line, out vertCnt) || vertCnt < 0) { return(false); } Polygon pg = new Polygon(vertCnt); ppg.Add(pg); for (int j = 0; j < vertCnt; j++) { double x, y; if ((line = sr.ReadLine()) == null) { return(false); } char[] delimiters = new char[] { ',', ' ' }; string[] vals = line.Split(delimiters); if (vals.Length < 2) { return(false); } if (!double.TryParse(vals[0], out x)) { return(false); } if (!double.TryParse(vals[1], out y)) { if (vals.Length < 2 || !double.TryParse(vals[2], out y)) { return(false); } } x = x * scaling + xOffset; y = y * scaling + yOffset; pg.Add(new IntPoint((int)Math.Round(x), (int)Math.Round(y))); } } return(true); }
void DrawingPanel_Paint(object sender, GUI.PaintEventArgs e) { GUI.Cursor.Current = GUI.Cursors.WaitCursor; Graphics graphics = e.Graphics; SmoothingMode fillSmoothingMode = graphics is SoftwareGraphics ? SmoothingMode.AntiAlias : SmoothingMode.None; SmoothingMode strokeSmoothingMode = SmoothingMode.AntiAlias; graphics.SmoothingMode = fillSmoothingMode; graphics.Clear(Color.White); GraphicsPath path = new GraphicsPath(); if (rbNonZero.IsChecked) { path.FillMode = FillMode.Winding; } //draw subjects ... foreach (Polygon pg in subjects) { Point[] pts = PolygonToPointArray(pg, scale); path.AddPolygon(pts); pts = null; } Pen myPen = new Pen( //Color.FromArgb(196, 0xC3, 0xC9, 0xCF), Color.FromArgb(128, 0, 0, 255), 0.6); SolidColorBrush myBrush = new SolidColorBrush( //Color.FromArgb(127, 0xDD, 0xDD, 0xF0)); //Color.FromArgb(255, 210, 210, 255)); Color.FromArgb(127, Color.LightBlue)); graphics.SmoothingMode = fillSmoothingMode; graphics.FillPath(myBrush, path); graphics.SmoothingMode = strokeSmoothingMode; graphics.DrawPath(myPen, path); path.Reset(); //draw clips ... if (rbNonZero.IsChecked) { path.FillMode = FillMode.Winding; } foreach (Polygon pg in clips) { Point[] pts = PolygonToPointArray(pg, scale); path.AddPolygon(pts); pts = null; } myPen.Color = Color.FromArgb(128, Color.Red); //0xF9, 0xBE, 0xA6); myBrush.Color = //Color.FromArgb(127, 0xFF, 0xE0, 0xE0); Color.FromArgb(50, 255, 0, 0); graphics.SmoothingMode = fillSmoothingMode; graphics.FillPath(myBrush, path); graphics.SmoothingMode = strokeSmoothingMode; graphics.DrawPath(myPen, path); //do the clipping ... if ((clips.Count > 0 || subjects.Count > 0) && !rbNone.IsChecked) { Polygons solution2 = new Polygons(); Clipper c = new Clipper(0); c.AddPaths(subjects, PolyType.ptSubject, true); c.AddPaths(clips, PolyType.ptClip, true); solution.Clear(); bool succeeded = c.Execute(GetClipType(), solution, GetPolyFillType(), GetPolyFillType()); if (succeeded) { myBrush.Color = Color.Black; path.Reset(); //It really shouldn't matter what FillMode is used for solution //polygons because none of the solution polygons overlap. //However, FillMode.Winding will show any orientation errors where //holes will be stroked (outlined) correctly but filled incorrectly ... path.FillMode = FillMode.Winding; //or for something fancy ... if (nudOffset.Value != 0) { //old solution2 = Clipper.OffsetPolygons(solution, (double)nudOffset.Value * scale, JoinType.jtMiter); ClipperOffset co = new ClipperOffset(2, 0.25); co.AddPaths(solution, JoinType.jtMiter, EndType.etClosedPolygon); co.Execute(ref solution2, (double)nudOffset.Value * scale); } else { solution2 = new Polygons(solution); } foreach (Polygon pg in solution2) { Point[] pts = PolygonToPointArray(pg, scale); if (pts.Length//Count() > 2) { path.AddPolygon(pts); } pts = null; } //myBrush.Color = Color.FromArgb(127, 0x66, 0xEF, 0x7F); myBrush.Color = Color.FromArgb(100, 0, 255, 0); myPen.Color = Color.FromArgb(255, 0, 0x33, 0); myPen.Width = 1; graphics.SmoothingMode = fillSmoothingMode; graphics.FillPath(myBrush, path); graphics.SmoothingMode = strokeSmoothingMode; graphics.DrawPath(myPen, path); //now do some fancy testing ... Font f = new Font("Arial", 8); Brush b = Brushes.Navy; double subj_area = 0, clip_area = 0, int_area = 0, union_area = 0; c.Clear(); c.AddPaths(subjects, PolyType.ptSubject, true); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) { subj_area += Clipper.Area(pg); } c.Clear(); c.AddPaths(clips, PolyType.ptClip, true); c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) { clip_area += Clipper.Area(pg); } c.AddPaths(subjects, PolyType.ptSubject, true); c.Execute(ClipType.ctIntersection, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) { int_area += Clipper.Area(pg); } c.Execute(ClipType.ctUnion, solution2, GetPolyFillType(), GetPolyFillType()); foreach (Polygon pg in solution2) { union_area += Clipper.Area(pg); } StringFormat lftStringFormat = new StringFormat(); lftStringFormat.Alignment = StringAlignment.Near; lftStringFormat.LineAlignment = StringAlignment.Near; StringFormat rtStringFormat = new StringFormat(); rtStringFormat.Alignment = StringAlignment.Far; rtStringFormat.LineAlignment = StringAlignment.Near; RectI rec = new RectI(WorkArea.Width - 114, WorkArea.Height - 116, 104, 106); graphics.FillRectangle(new SolidColorBrush(Color.FromArgb(196, Color.WhiteSmoke)), rec); graphics.DrawRectangle(myPen, rec); rec.Inflate(new SizeI(-2, 0)); // because of Alt.Sketch smaller top offset rec += new PointI(0, 1); rec -= new SizeI(0, 1); graphics.DrawString("Areas", f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 14)); graphics.DrawString("subj: ", f, b, rec, lftStringFormat); graphics.DrawString((subj_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 12)); graphics.DrawString("clip: ", f, b, rec, lftStringFormat); graphics.DrawString((clip_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 12)); graphics.DrawString("intersect: ", f, b, rec, lftStringFormat); graphics.DrawString((int_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 12)); graphics.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 10)); graphics.DrawString("s + c - i: ", f, b, rec, lftStringFormat); graphics.DrawString(((subj_area + clip_area - int_area) / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 10)); graphics.DrawString("---------", f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 10)); graphics.DrawString("union: ", f, b, rec, lftStringFormat); graphics.DrawString((union_area / 100000).ToString("0,0"), f, b, rec, rtStringFormat); rec.Offset(new PointI(0, 10)); graphics.DrawString("---------", f, b, rec, rtStringFormat); } //end if succeeded } //end if something to clip //TEMP pictureBox1.Image = mybitmap; graphics.Dispose(); GUI.Cursor.Current = GUI.Cursors.Default; }
protected override void OnLoad(EventArgs e) { base.OnLoad(e); /*TEMP * saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); * saveFileDialog1.DefaultExt = "svg"; * saveFileDialog1.Filter = "SVG Files (*.svg)|*.svg";*/ // Refresh bRefresh = new Alt.GUI.Temporary.Gwen.Control.Button(m_RightPanel); bRefresh.Text = "Refresh"; bRefresh.Dock = Pos.Bottom; bRefresh.Margin = new Margin(0, 20, 5, GUI.Config.Logo.PixelHeight); bRefresh.Click += new EventHandler(bRefresh_Click); // Save bSave = new Alt.GUI.Temporary.Gwen.Control.Button(m_RightPanel); bSave.Text = "Save as SVG File"; bSave.Dock = Pos.Top; bSave.Margin = new Margin(0, 20, 0, 0); bSave.IsHidden = true; // Drawing Panel Base DrawingPanelHolder = new Base(m_Splitter); m_DrawingPanel = new DoubleBufferedControl(DrawingPanelHolder); m_DrawingPanel.UseTransparentBackground = false; m_DrawingPanel.Dock = Pos.Fill; m_DrawingPanel.Paint += new GUI.PaintEventHandler(DrawingPanel_Paint); m_DrawingPanel.MouseWheel += new GUI.MouseEventHandler(DrawingPanel_MouseWheel); m_DrawingPanel.Resize += new EventHandler(DrawingPanel_Resize); m_DrawingPanel.Margin = new Margin(0, GUI.Config.Logo.PixelHeight, 0, GUI.Config.Logo.PixelHeight); m_DrawingPanel.DrawBorder = true; m_DrawingPanel.BorderColor = Color.DodgerBlue * 0.6; // m_Splitter.SetPanel(1, m_RightPanel); m_Splitter.SetPanel(0, DrawingPanelHolder); m_Splitter.SetHValue(0.83f); // Neet to correct groupBoxes groupBox_Geometry.AutoSizeToContents = false; groupBox_BooleanOperation.AutoSizeToContents = false; groupBox_Options.AutoSizeToContents = false; // Core subjects = new Polygons(); clips = new Polygons(); solution = new Polygons(); this.Focus(); }
private void WriteSupportPolygons(SliceDataStorage storage, GCodePlanner gcodeLayer, int layerIndex, ConfigSettings config, Polygons supportPolygons, SupportType interfaceLayer) { for (int volumeIndex = 0; volumeIndex < storage.volumes.Count; volumeIndex++) { SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex]; for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++) { supportPolygons = supportPolygons.CreateDifference(layer.parts[partIndex].TotalOutline.Offset(config.supportXYDistance_um)); } } //Contract and expand the support polygons so small sections are removed and the final polygon is smoothed a bit. supportPolygons = supportPolygons.Offset(-config.extrusionWidth_um * 1); supportPolygons = supportPolygons.Offset(config.extrusionWidth_um * 1); List <Polygons> supportIslands = supportPolygons.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd); PathOrderOptimizer islandOrderOptimizer = new PathOrderOptimizer(gcode.GetPositionXY()); for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++) { islandOrderOptimizer.AddPolygon(supportIslands[islandIndex][0]); } islandOrderOptimizer.Optimize(); for (int islandIndex = 0; islandIndex < supportIslands.Count; islandIndex++) { Polygons island = supportIslands[islandOrderOptimizer.bestPolygonOrderIndex[islandIndex]]; Polygons supportLines = new Polygons(); if (config.supportLineSpacing_um > 0) { switch (interfaceLayer) { case SupportType.Interface: Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle + 90, config.extrusionWidth_um); break; case SupportType.General: switch (config.supportType) { case ConfigConstants.SUPPORT_TYPE.GRID: Infill.GenerateGridInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; case ConfigConstants.SUPPORT_TYPE.LINES: Infill.GenerateLineInfill(config, island, ref supportLines, config.supportInfillStartingAngle, config.supportLineSpacing_um); break; } break; default: throw new NotImplementedException(); } } if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(island); } switch (interfaceLayer) { case SupportType.Interface: gcodeLayer.WritePolygonsByOptimizer(supportLines, supportInterfaceConfig); break; case SupportType.General: if (config.supportType == ConfigConstants.SUPPORT_TYPE.GRID) { gcodeLayer.WritePolygonsByOptimizer(island, supportNormalConfig); } gcodeLayer.WritePolygonsByOptimizer(supportLines, supportNormalConfig); break; default: throw new NotImplementedException(); } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); } }
private void CalculateInfillData(SliceDataStorage storage, int volumeIndex, int layerIndex, SliceLayerPart part, ref Polygons fillPolygons, ref Polygons topFillPolygons, ref Polygons bridgePolygons) { // generate infill the bottom layer including bridging foreach (Polygons outline in part.SolidBottomOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd)) { if (layerIndex > 0) { double bridgeAngle; if (Bridge.BridgeAngle(outline, storage.volumes[volumeIndex].layers[layerIndex - 1], out bridgeAngle)) { Infill.GenerateLinePaths(outline, ref bridgePolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, bridgeAngle); } else { Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } } else { Infill.GenerateLinePaths(outline, ref fillPolygons, config.firstLayerExtrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } } // generate infill for the top layer foreach (Polygons outline in part.SolidTopOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd)) { Infill.GenerateLinePaths(outline, ref topFillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle); } // generate infill intermediate layers foreach (Polygons outline in part.SolidInfillOutlines.CreateLayerOutlines(PolygonsHelper.LayerOpperation.EvenOdd)) { if (true) // use the old infill method { Infill.GenerateLinePaths(outline, ref fillPolygons, config.extrusionWidth_um, config.infillExtendIntoPerimeter_um, config.infillStartingAngle + 90 * (layerIndex % 2)); } else // use the new concentric infill (not tested enough yet) have to handle some bad casses better { double oldInfillPercent = config.infillPercent; config.infillPercent = 100; Infill.GenerateConcentricInfill(config, outline, ref fillPolygons); config.infillPercent = oldInfillPercent; } } double fillAngle = config.infillStartingAngle; // generate the sparse infill for this part on this layer if (config.infillPercent > 0) { switch (config.infillType) { case ConfigConstants.INFILL_TYPE.LINES: if ((layerIndex & 1) == 1) { fillAngle += 90; } Infill.GenerateLineInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.GRID: Infill.GenerateGridInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.TRIANGLES: Infill.GenerateTriangleInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle); break; case ConfigConstants.INFILL_TYPE.HEXAGON: Infill.GenerateHexagonInfill(config, part.InfillOutlines, ref fillPolygons, fillAngle, layerIndex); break; case ConfigConstants.INFILL_TYPE.CONCENTRIC: Infill.GenerateConcentricInfill(config, part.InfillOutlines, ref fillPolygons); break; default: throw new NotImplementedException(); } } }
//Add a single layer from a single mesh-volume to the GCode private void AddVolumeLayerToGCode(SliceDataStorage storage, GCodePlanner gcodeLayer, int volumeIndex, int layerIndex, int extrusionWidth_um, int fanSpeedPercent) { int prevExtruder = gcodeLayer.getExtruder(); bool extruderChanged = gcodeLayer.SetExtruder(volumeIndex); SliceLayer layer = storage.volumes[volumeIndex].layers[layerIndex]; if (extruderChanged) { addWipeTower(storage, gcodeLayer, layerIndex, prevExtruder, extrusionWidth_um); } if (storage.wipeShield.Count > 0 && storage.volumes.Count > 1) { gcodeLayer.SetAlwaysRetract(true); gcodeLayer.WritePolygonsByOptimizer(storage.wipeShield[layerIndex], skirtConfig); gcodeLayer.SetAlwaysRetract(!config.avoidCrossingPerimeters); } PathOrderOptimizer partOrderOptimizer = new PathOrderOptimizer(new IntPoint()); for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } partOrderOptimizer.AddPolygon(layer.parts[partIndex].Insets[0][0]); } partOrderOptimizer.Optimize(); for (int partIndex = 0; partIndex < partOrderOptimizer.bestPolygonOrderIndex.Count; partIndex++) { if (config.continuousSpiralOuterPerimeter && partIndex > 0) { continue; } SliceLayerPart part = layer.parts[partOrderOptimizer.bestPolygonOrderIndex[partIndex]]; if (config.avoidCrossingPerimeters) { gcodeLayer.SetOuterPerimetersToAvoidCrossing(part.AvoidCrossingBoundery); } else { gcodeLayer.SetAlwaysRetract(true); } Polygons fillPolygons = new Polygons(); Polygons topFillPolygons = new Polygons(); Polygons bridgePolygons = new Polygons(); CalculateInfillData(storage, volumeIndex, layerIndex, part, ref fillPolygons, ref topFillPolygons, ref bridgePolygons); // Write the bridge polgons out first so the perimeter will have more to hold to while bridging the gaps. // It would be even better to slow down the perimeters that are part of bridges but that is a bit harder. if (bridgePolygons.Count > 0) { gcode.WriteFanCommand(config.bridgeFanSpeedPercent); gcodeLayer.WritePolygonsByOptimizer(bridgePolygons, bridgConfig); gcode.WriteFanCommand(fanSpeedPercent); } if (config.numberOfPerimeters > 0) { if (partIndex != lastPartIndex) { // force a retract if changing islands gcodeLayer.ForceRetract(); lastPartIndex = partIndex; } if (config.continuousSpiralOuterPerimeter) { if (layerIndex >= config.numberOfBottomLayers) { inset0Config.spiralize = true; } } // If we are on the very first layer we start with the outside in so that we can stick to the bed better. if (config.outsidePerimetersFirst || layerIndex == 0 || inset0Config.spiralize) { // First the outside (this helps with accuracy) if (part.Insets.Count > 0) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config); } if (!inset0Config.spiralize) { for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig); } } } else // This is so we can do overhanges better (the outside can stick a bit to the inside). { // Print everything but the first perimeter from the outside in so the little parts have more to stick to. for (int perimeterIndex = 1; perimeterIndex < part.Insets.Count; perimeterIndex++) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[perimeterIndex], insetXConfig); } // then 0 if (part.Insets.Count > 0) { gcodeLayer.WritePolygonsByOptimizer(part.Insets[0], inset0Config); } } } gcodeLayer.WritePolygonsByOptimizer(fillPolygons, fillConfig); gcodeLayer.WritePolygonsByOptimizer(topFillPolygons, topFillConfig); //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. if (!config.continuousSpiralOuterPerimeter || layerIndex < config.numberOfBottomLayers) { gcodeLayer.MoveInsideTheOuterPerimeter(extrusionWidth_um * 2); } } gcodeLayer.SetOuterPerimetersToAvoidCrossing(null); }
/// <summary> /// Remove a region /// </summary> /// <param name="index"></param> public void Remove(int index) { Polygons.RemoveAt(index); Removed?.Invoke(this, new EventArgs()); }
public PolyOffsetBuilder(Polygons pts, Polygons solution, double delta, JoinType jointype, double MiterLimit = 2, bool AutoFix = true) { //precondtion: solution != pts if (delta == 0) { solution = pts; return; } this.pts = pts; this.delta = delta; //AutoFix - fixes polygon orientation if necessary and removes //duplicate vertices. Can be set false when you're sure that polygon //orientation is correct and that there are no duplicate vertices. if (AutoFix) { int Len = pts.Count, botI = 0; while (botI < Len && pts[botI].Count == 0) botI++; if (botI == Len) return; //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point //This point (on pts[botI]) must be on an outer polygon ring and if //its orientation is false (counterclockwise) then assume all polygons //need reversing ... IntPoint botPt = pts[botI][0]; for (int i = botI; i < Len; ++i) { if (pts[i].Count == 0) continue; if (UpdateBotPt(pts[i][0], ref botPt)) botI = i; for (int j = pts[i].Count -1; j > 0; j--) { if (PointsEqual(pts[i][j], pts[i][j -1])) pts[i].RemoveAt(j); else if (UpdateBotPt(pts[i][j], ref botPt)) botI = i; } } if (!Orientation(pts[botI])) ReversePolygons(pts); } if (MiterLimit <= 1) MiterLimit = 1; double RMin = 2.0 / (MiterLimit*MiterLimit); normals = new List<DoublePoint>(); double deltaSq = delta*delta; solution.Clear(); solution.Capacity = pts.Count; for (m_i = 0; m_i < pts.Count; m_i++) { int len = pts[m_i].Count; if (len > 1 && pts[m_i][0].X == pts[m_i][len - 1].X && pts[m_i][0].Y == pts[m_i][len - 1].Y) len--; if (len == 0 || (len < 3 && delta <= 0)) continue; else if (len == 1) { Polygon arc; arc = BuildArc(pts[m_i][len - 1], 0, 2 * Math.PI, delta); solution.Add(arc); continue; } //build normals ... normals.Clear(); normals.Capacity = len; for (int j = 0; j < len -1; ++j) normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1])); normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0])); currentPoly = new Polygon(); m_k = len - 1; for (m_j = 0; m_j < len; ++m_j) { switch (jointype) { case JoinType.jtMiter: { m_R = 1 + (normals[m_j].X*normals[m_k].X + normals[m_j].Y*normals[m_k].Y); if (m_R >= RMin) DoMiter(); else DoSquare(MiterLimit); break; } case JoinType.jtRound: DoRound(); break; case JoinType.jtSquare: DoSquare(1); break; } m_k = m_j; } solution.Add(currentPoly); } //finally, clean up untidy corners ... Clipper clpr = new Clipper(); clpr.AddPolygons(solution, PolyType.ptSubject); if (delta > 0) { clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); } else { IntRect r = clpr.GetBounds(); Polygon outer = new Polygon(4); outer.Add(new IntPoint(r.left - 10, r.bottom + 10)); outer.Add(new IntPoint(r.right + 10, r.bottom + 10)); outer.Add(new IntPoint(r.right + 10, r.top - 10)); outer.Add(new IntPoint(r.left - 10, r.top - 10)); clpr.AddPolygon(outer, PolyType.ptSubject); clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); if (solution.Count > 0) { solution.RemoveAt(0); for (int i = 0; i < solution.Count; i++) solution[i].Reverse(); } } }
//------------------------------------------------------------------------------ public static Polygons OffsetPolygons(Polygons poly, double delta) { Polygons result = new Polygons(poly.Count); new PolyOffsetBuilder(poly, result, delta, JoinType.jtSquare, 2.0, true); return result; }
public static bool FindThinLines(Polygon polygon, long overlapMergeAmount_um, long minimumRequiredWidth_um, out Polygons onlyMergeLines, bool pathIsClosed = true) { return(FindThinLines(new Polygons { polygon }, overlapMergeAmount_um, minimumRequiredWidth_um, out onlyMergeLines, pathIsClosed)); }
//------------------------------------------------------------------------------ public bool AddPolygons(Polygons ppg, PolyType polyType) { bool result = false; for (int i = 0; i < ppg.Count; ++i) if (AddPolygon(ppg[i], polyType)) result = true; return result; }
public static bool FindThinLines(Polygons polygons, long overlapMergeAmount_um, long minimumRequiredWidth_um, out Polygons onlyMergeLines, bool pathIsClosed = true) { bool pathHasMergeLines = false; polygons = MakeCloseSegmentsMergable(polygons, overlapMergeAmount_um, pathIsClosed); // make a copy that has every point duplicated (so that we have them as segments). List <Segment> polySegments = Segment.ConvertToSegments(polygons); Altered[] markedAltered = new Altered[polySegments.Count]; int segmentCount = polySegments.Count; // now walk every segment and check if there is another segment that is similar enough to merge them together for (int firstSegmentIndex = 0; firstSegmentIndex < segmentCount; firstSegmentIndex++) { for (int checkSegmentIndex = firstSegmentIndex + 1; checkSegmentIndex < segmentCount; checkSegmentIndex++) { // The first point of start and the last point of check (the path will be coming back on itself). long startDelta = (polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length(); // if the segments are similar enough if (startDelta < overlapMergeAmount_um) { // The last point of start and the first point of check (the path will be coming back on itself). long endDelta = (polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length(); if (endDelta < overlapMergeAmount_um) { // move the first segments points to the average of the merge positions long startEndWidth = Math.Abs((polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length()); long endStartWidth = Math.Abs((polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length()); long width = Math.Min(startEndWidth, endStartWidth); if (width > minimumRequiredWidth_um) { // We need to check if the new start position is on the inside of the curve. We can only add thin lines on the insides of our exisiting curves. IntPoint newStartPosition = (polySegments[firstSegmentIndex].Start + polySegments[checkSegmentIndex].End) / 2; // the start; IntPoint newStartDirection = newStartPosition - polySegments[firstSegmentIndex].Start; IntPoint normalLeft = (polySegments[firstSegmentIndex].End - polySegments[firstSegmentIndex].Start).GetPerpendicularLeft(); long dotProduct = normalLeft.Dot(newStartDirection); if (dotProduct > 0) { pathHasMergeLines = true; polySegments[firstSegmentIndex].Start = newStartPosition; polySegments[firstSegmentIndex].Start.Width = width; polySegments[firstSegmentIndex].End = (polySegments[firstSegmentIndex].End + polySegments[checkSegmentIndex].Start) / 2; // the end polySegments[firstSegmentIndex].End.Width = width; markedAltered[firstSegmentIndex] = Altered.merged; // mark this segment for removal markedAltered[checkSegmentIndex] = Altered.remove; // We only expect to find one match for each segment, so move on to the next segment break; } } } } } } // remove the marked segments for (int segmentIndex = segmentCount - 1; segmentIndex >= 0; segmentIndex--) { // remove every segment that has not been merged if (markedAltered[segmentIndex] != Altered.merged) { polySegments.RemoveAt(segmentIndex); } } // go through the polySegments and create a new polygon for every connected set of segments onlyMergeLines = new Polygons(); Polygon currentPolygon = new Polygon(); onlyMergeLines.Add(currentPolygon); // put in the first point for (int segmentIndex = 0; segmentIndex < polySegments.Count; segmentIndex++) { // add the start point currentPolygon.Add(polySegments[segmentIndex].Start); // if the next segment is not connected to this one if (segmentIndex < polySegments.Count - 1 && polySegments[segmentIndex].End != polySegments[segmentIndex + 1].Start) { // add the end point currentPolygon.Add(polySegments[segmentIndex].End); // create a new polygon currentPolygon = new Polygon(); onlyMergeLines.Add(currentPolygon); } } // add the end point if (polySegments.Count > 0) { currentPolygon.Add(polySegments[polySegments.Count - 1].End); } long cleanDistance_um = overlapMergeAmount_um / 40; //Clipper.CleanPolygons(onlyMergeLines, cleanDistance_um); return(pathHasMergeLines); }
public static Polygons CreateLineIntersections(this Polygons polygons, Polygons other) { Clipper clipper = new Clipper(); clipper.AddPaths(other, PolyType.ptSubject, false); clipper.AddPaths(polygons, PolyType.ptClip, true); PolyTree clippedLines = new PolyTree(); clipper.Execute(ClipType.ctIntersection, clippedLines); return Clipper.OpenPathsFromPolyTree(clippedLines); }
public static bool MergePerimeterOverlaps(Polygon perimeter, long overlapMergeAmount_um, out Polygons separatedPolygons, bool pathIsClosed = true) { separatedPolygons = new Polygons(); long cleanDistance_um = overlapMergeAmount_um / 40; Polygons cleanedPolygs = Clipper.CleanPolygons(new Polygons() { perimeter }, cleanDistance_um); perimeter = cleanedPolygs[0]; if (perimeter.Count == 0) { return(false); } bool pathWasOptomized = false; for (int i = 0; i < perimeter.Count; i++) { perimeter[i] = new IntPoint(perimeter[i]) { Width = overlapMergeAmount_um }; } perimeter = MakeCloseSegmentsMergable(perimeter, overlapMergeAmount_um, pathIsClosed); // make a copy that has every point duplicated (so that we have them as segments). List <Segment> polySegments = Segment.ConvertToSegments(perimeter, pathIsClosed); Altered[] markedAltered = new Altered[polySegments.Count]; int segmentCount = polySegments.Count; // now walk every segment and check if there is another segment that is similar enough to merge them together for (int firstSegmentIndex = 0; firstSegmentIndex < segmentCount; firstSegmentIndex++) { //polySegments[firstSegmentIndex].Start.Width = overlapMergeAmount_um; //polySegments[firstSegmentIndex].End.Width = overlapMergeAmount_um; for (int checkSegmentIndex = firstSegmentIndex + 1; checkSegmentIndex < segmentCount; checkSegmentIndex++) { // The first point of start and the last point of check (the path will be coming back on itself). long startDelta = (polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length(); // if the segments are similar enough if (startDelta < overlapMergeAmount_um) { // The last point of start and the first point of check (the path will be coming back on itself). long endDelta = (polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length(); if (endDelta < overlapMergeAmount_um) { // only considre the merge if the directions of the lines are towards eachother var firstSegmentDirection = polySegments[firstSegmentIndex].End - polySegments[firstSegmentIndex].Start; var checkSegmentDirection = polySegments[checkSegmentIndex].End - polySegments[checkSegmentIndex].Start; if (firstSegmentDirection.Dot(checkSegmentDirection) > 0) { continue; } pathWasOptomized = true; // move the first segments points to the average of the merge positions long startEndWidth = Math.Abs((polySegments[firstSegmentIndex].Start - polySegments[checkSegmentIndex].End).Length()); long endStartWidth = Math.Abs((polySegments[firstSegmentIndex].End - polySegments[checkSegmentIndex].Start).Length()); long width = Math.Min(startEndWidth, endStartWidth) + overlapMergeAmount_um; polySegments[firstSegmentIndex].Start = (polySegments[firstSegmentIndex].Start + polySegments[checkSegmentIndex].End) / 2; // the start polySegments[firstSegmentIndex].Start.Width = width; polySegments[firstSegmentIndex].End = (polySegments[firstSegmentIndex].End + polySegments[checkSegmentIndex].Start) / 2; // the end polySegments[firstSegmentIndex].End.Width = width; markedAltered[firstSegmentIndex] = Altered.merged; // mark this segment for removal markedAltered[checkSegmentIndex] = Altered.remove; // We only expect to find one match for each segment, so move on to the next segment break; } } } } // remove the marked segments for (int segmentIndex = segmentCount - 1; segmentIndex >= 0; segmentIndex--) { if (markedAltered[segmentIndex] == Altered.remove) { polySegments.RemoveAt(segmentIndex); } } // go through the polySegments and create a new polygon for every connected set of segments Polygon currentPolygon = new Polygon(); separatedPolygons.Add(currentPolygon); // put in the first point for (int segmentIndex = 0; segmentIndex < polySegments.Count; segmentIndex++) { // add the start point currentPolygon.Add(polySegments[segmentIndex].Start); // if the next segment is not connected to this one if (segmentIndex < polySegments.Count - 1 && polySegments[segmentIndex].End != polySegments[segmentIndex + 1].Start) { // add the end point currentPolygon.Add(polySegments[segmentIndex].End); // create a new polygon currentPolygon = new Polygon(); separatedPolygons.Add(currentPolygon); } } // add the end point currentPolygon.Add(polySegments[polySegments.Count - 1].End); return(pathWasOptomized); }
public static bool polygonCollidesWithlineSegment(Polygons polys, IntPoint transformed_startPoint, IntPoint transformed_endPoint, PointMatrix transformation_matrix) { foreach (Polygon poly in polys) { if (poly.size() == 0) { continue; } if (PolygonHelper.polygonCollidesWithlineSegment(poly, transformed_startPoint, transformed_endPoint, transformation_matrix)) { return true; } } return false; }
public void WriteQueuedGCode(int layerThickness, int fanSpeedPercent = -1, int bridgeFanSpeedPercent = -1) { GCodePathConfig lastConfig = null; int extruderIndex = gcodeExport.GetExtruderIndex(); for (int pathIndex = 0; pathIndex < paths.Count; pathIndex++) { GCodePath path = paths[pathIndex]; if (extruderIndex != path.extruderIndex) { extruderIndex = path.extruderIndex; gcodeExport.SwitchExtruder(extruderIndex); } else if (path.Retract) { gcodeExport.WriteRetraction(); } if (path.config != travelConfig && lastConfig != path.config) { if (path.config.gcodeComment == "BRIDGE" && bridgeFanSpeedPercent != -1) { gcodeExport.WriteFanCommand(bridgeFanSpeedPercent); } else if (lastConfig?.gcodeComment == "BRIDGE" && bridgeFanSpeedPercent != -1) { gcodeExport.WriteFanCommand(fanSpeedPercent); } gcodeExport.WriteComment("TYPE:{0}".FormatWith(path.config.gcodeComment)); lastConfig = path.config; } double speed = path.config.speed; if (path.config.lineWidth_um != 0) { // Prevent cooling overrides from affecting bridge moves if (path.config.gcodeComment != "BRIDGE") { speed = speed * extrudeSpeedFactor / 100; } } else { speed = speed * travelSpeedFactor / 100; } if (path.points.Count == 1 && path.config != travelConfig && (gcodeExport.GetPositionXY() - path.points[0]).ShorterThen(path.config.lineWidth_um * 2)) { //Check for lots of small moves and combine them into one large line IntPoint nextPosition = path.points[0]; int i = pathIndex + 1; while (i < paths.Count && paths[i].points.Count == 1 && (nextPosition - paths[i].points[0]).ShorterThen(path.config.lineWidth_um * 2)) { nextPosition = paths[i].points[0]; i++; } if (paths[i - 1].config == travelConfig) { i--; } if (i > pathIndex + 2) { nextPosition = gcodeExport.GetPosition(); for (int x = pathIndex; x < i - 1; x += 2) { long oldLen = (nextPosition - paths[x].points[0]).Length(); IntPoint newPoint = (paths[x].points[0] + paths[x + 1].points[0]) / 2; long newLen = (gcodeExport.GetPosition() - newPoint).Length(); if (newLen > 0) { gcodeExport.WriteMove(newPoint, speed, (int)(path.config.lineWidth_um * oldLen / newLen)); } nextPosition = paths[x + 1].points[0]; } long lineWidth_um = path.config.lineWidth_um; if (paths[i - 1].points[0].Width != 0) { lineWidth_um = paths[i - 1].points[0].Width; } gcodeExport.WriteMove(paths[i - 1].points[0], speed, lineWidth_um); pathIndex = i - 1; continue; } } bool spiralize = path.config.spiralize; if (spiralize) { //Check if we are the last spiralize path in the list, if not, do not spiralize. for (int m = pathIndex + 1; m < paths.Count; m++) { if (paths[m].config.spiralize) { spiralize = false; } } } if (spiralize) // if we are still in spiralize mode { //If we need to spiralize then raise the head slowly by 1 layer as this path progresses. double totalLength = 0; long z = gcodeExport.GetPositionZ(); IntPoint currentPosition = gcodeExport.GetPositionXY(); for (int pointIndex = 0; pointIndex < path.points.Count; pointIndex++) { IntPoint nextPosition = path.points[pointIndex]; totalLength += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; } double length = 0.0; currentPosition = gcodeExport.GetPositionXY(); for (int i = 0; i < path.points.Count; i++) { IntPoint nextPosition = path.points[i]; length += (currentPosition - nextPosition).LengthMm(); currentPosition = nextPosition; IntPoint nextExtrusion = path.points[i]; nextExtrusion.Z = (int)(z + layerThickness * length / totalLength + .5); gcodeExport.WriteMove(nextExtrusion, speed, path.config.lineWidth_um); } } else { // This is test code to remove double drawn small perimeter lines. Polygons pathsWithOverlapsRemoved = null; bool pathHadOverlaps = false; bool pathIsClosed = true; if (mergeOverlappingLines && (path.config.gcodeComment == "WALL-OUTER" || path.config.gcodeComment == "WALL-INNER")) { //string perimeterString = Newtonsoft.Json.JsonConvert.SerializeObject(path); if (perimeterStartEndOverlapRatio < 1) { path = TrimPerimeter(path, perimeterStartEndOverlapRatio); //string trimmedString = Newtonsoft.Json.JsonConvert.SerializeObject(path); // it was closed but now it isn't pathIsClosed = false; } if (path.config.lineWidth_um > 0 && path.points.Count > 2) { // have to add in the position we are currently at path.points.Insert(0, gcodeExport.GetPosition()); //string openPerimeterString = Newtonsoft.Json.JsonConvert.SerializeObject(path); pathHadOverlaps = MergePerimeterOverlaps(path.points, path.config.lineWidth_um, out pathsWithOverlapsRemoved, pathIsClosed) && pathsWithOverlapsRemoved.Count > 0; //string trimmedString = Newtonsoft.Json.JsonConvert.SerializeObject(pathsWithOverlapsRemoved); } } if (pathHadOverlaps) { for (int polygonIndex = 0; polygonIndex < pathsWithOverlapsRemoved.Count; polygonIndex++) { Polygon polygon = pathsWithOverlapsRemoved[polygonIndex]; if (polygon.Count == 2) { // make sure the path is ordered with the first point the closest to where we are now IntPoint currentPosition = gcodeExport.GetPosition(); // if the second point is closer swap them if ((polygon[1] - currentPosition).LengthSquared() < (polygon[0] - currentPosition).LengthSquared()) { // swap them IntPoint temp = polygon[0]; polygon[0] = polygon[1]; polygon[1] = temp; } } // move to the start of this polygon gcodeExport.WriteMove(polygon[0], travelConfig.speed, 0); // write all the data for the polygon for (int pointIndex = 1; pointIndex < polygon.Count; pointIndex++) { gcodeExport.WriteMove(polygon[pointIndex], speed, polygon[pointIndex - 1].Width); } } } else { int outputCount = path.points.Count; for (int i = 0; i < outputCount; i++) { long lineWidth_um = path.config.lineWidth_um; if (path.points[i].Width != 0) { lineWidth_um = path.points[i].Width; } gcodeExport.WriteMove(path.points[i], speed, lineWidth_um); } } } } gcodeExport.UpdateTotalPrintTime(); }
public static void GenerateConcentricInfill(ConfigSettings config, Polygons partOutline, Polygons fillPolygons, long extrusionWidthOverride_um = 0) { if (extrusionWidthOverride_um == 0) { extrusionWidthOverride_um = config.ExtrusionWidth_um; } Polygons outlineCopy = new Polygons(partOutline); foreach (Polygon outline in outlineCopy) { if (outline.Count > 0) { outline.Add(outline[0]); } } int linespacing_um = (int)(extrusionWidthOverride_um / (config.InfillPercent / 100)); while (outlineCopy.Count > 0) { for (int outlineIndex = 0; outlineIndex < outlineCopy.Count; outlineIndex++) { Polygon r = outlineCopy[outlineIndex]; fillPolygons.Add(r); } outlineCopy = outlineCopy.Offset(-linespacing_um); foreach (Polygon outline in outlineCopy) { if (outline.Count > 0) { outline.Add(outline[0]); } } } }
private static Polygons GetSkirtBounds(ConfigSettings config, LayerDataStorage storage, bool externalOnly, int distance, int extrusionWidth_um, int brimCount) { Polygons allOutlines = new Polygons(); // Loop over every extruder for (int extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++) { // Only process the first extruder on spiral vase or // skip extruders that have empty layers if (config.ContinuousSpiralOuterPerimeter) { SliceLayer layer0 = storage.Extruders[extrudeIndex].Layers[0]; allOutlines.AddAll(layer0.Islands[0]?.IslandOutline); break; } // Add the layers outline to allOutlines SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0]; allOutlines.AddAll(layer.AllOutlines); } bool hasWipeTower = storage.wipeTower.PolygonLength() > 0; Polygons skirtPolygons = hasWipeTower ? new Polygons(storage.wipeTower) : new Polygons(); if (brimCount > 0) { Polygons brimLoops = new Polygons(); // Loop over the requested brimCount creating and unioning a new perimeter for each island for (int brimIndex = 0; brimIndex < brimCount; brimIndex++) { int offsetDistance = extrusionWidth_um * brimIndex + extrusionWidth_um / 2; Polygons unionedIslandOutlines = new Polygons(); // Grow each island by the current brim distance foreach (var island in allOutlines) { var polygons = new Polygons(); polygons.Add(island); // Union the island brims unionedIslandOutlines = unionedIslandOutlines.CreateUnion(polygons.Offset(offsetDistance)); } // Extend the polygons to account for the brim (ensures convex hull takes this data into account) brimLoops.AddAll(unionedIslandOutlines); } // TODO: This is a quick hack, reuse the skirt data to stuff in the brim. Good enough from proof of concept storage.skirt.AddAll(brimLoops); skirtPolygons = skirtPolygons.CreateUnion(brimLoops); } else { skirtPolygons = skirtPolygons.CreateUnion(allOutlines); } if (storage.support != null) { skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines()); } return(skirtPolygons); }
private static Mesh CreateTop(Polygons path, double topHeight, double scaling) { return(path.CreateVertexStorage(scaling).TriangulateFaces(zHeight: topHeight)); }
public void WriteRaftGCodeIfRequired(GCodeExport gcode, ConfigSettings config) { LayerDataStorage storage = this; if (config.ShouldGenerateRaft()) { GCodePathConfig raftBaseConfig = new GCodePathConfig("raftBaseConfig"); raftBaseConfig.SetData(config.FirstLayerSpeed, config.RaftBaseExtrusionWidth_um, "SUPPORT"); GCodePathConfig raftMiddleConfig = new GCodePathConfig("raftMiddleConfig"); raftMiddleConfig.SetData(config.RaftPrintSpeed, config.RaftInterfaceExtrusionWidth_um, "SUPPORT"); GCodePathConfig raftSurfaceConfig = new GCodePathConfig("raftMiddleConfig"); raftSurfaceConfig.SetData((config.RaftSurfacePrintSpeed > 0) ? config.RaftSurfacePrintSpeed : config.RaftPrintSpeed, config.RaftSurfaceExtrusionWidth_um, "SUPPORT"); // create the raft base { gcode.WriteComment("LAYER:-3"); gcode.WriteComment("RAFT BASE"); GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um); if (config.RaftExtruder >= 0) { // if we have a specified raft extruder use it gcodeLayer.SetExtruder(config.RaftExtruder); } else if (config.SupportExtruder >= 0) { // else preserve the old behavior of using the support extruder if set. gcodeLayer.SetExtruder(config.SupportExtruder); } gcode.setZ(config.RaftBaseThickness_um); gcode.SetExtrusion(config.RaftBaseThickness_um, config.FilamentDiameter_um, config.ExtrusionMultiplier); // write the skirt around the raft gcodeLayer.QueuePolygonsByOptimizer(storage.skirt, raftBaseConfig); List <Polygons> raftIslands = storage.raftOutline.ProcessIntoSeparatIslands(); foreach (var raftIsland in raftIslands) { // write the outline of the raft gcodeLayer.QueuePolygonsByOptimizer(raftIsland, raftBaseConfig); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(raftIsland, raftLines, config.RaftBaseLineSpacing_um, config.InfillExtendIntoPerimeter_um, 0); // write the inside of the raft base gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftBaseConfig); if (config.RetractWhenChangingIslands) { gcodeLayer.ForceRetract(); } } gcodeLayer.WriteQueuedGCode(config.RaftBaseThickness_um); } if (config.RaftFanSpeedPercent > 0) { gcode.WriteFanCommand(config.RaftFanSpeedPercent); } // raft middle layers { gcode.WriteComment("LAYER:-2"); gcode.WriteComment("RAFT MIDDLE"); GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um); gcode.setZ(config.RaftBaseThickness_um + config.RaftInterfaceThicknes_um); gcode.SetExtrusion(config.RaftInterfaceThicknes_um, config.FilamentDiameter_um, config.ExtrusionMultiplier); Polygons raftLines = new Polygons(); Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftInterfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, 45); gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftMiddleConfig); gcodeLayer.WriteQueuedGCode(config.RaftInterfaceThicknes_um); } for (int raftSurfaceIndex = 1; raftSurfaceIndex <= config.RaftSurfaceLayers; raftSurfaceIndex++) { gcode.WriteComment("LAYER:-1"); gcode.WriteComment("RAFT SURFACE"); GCodePlanner gcodeLayer = new GCodePlanner(gcode, config.TravelSpeed, config.MinimumTravelToCauseRetraction_um); gcode.setZ(config.RaftBaseThickness_um + config.RaftInterfaceThicknes_um + config.RaftSurfaceThickness_um * raftSurfaceIndex); gcode.SetExtrusion(config.RaftSurfaceThickness_um, config.FilamentDiameter_um, config.ExtrusionMultiplier); Polygons raftLines = new Polygons(); if (raftSurfaceIndex == config.RaftSurfaceLayers) { // make sure the top layer of the raft is 90 degrees offset to the first layer of the part so that it has minimum contact points. Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftSurfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, config.InfillStartingAngle + 90); } else { Infill.GenerateLinePaths(storage.raftOutline, raftLines, config.RaftSurfaceLineSpacing_um, config.InfillExtendIntoPerimeter_um, 90 * raftSurfaceIndex); } gcodeLayer.QueuePolygonsByOptimizer(raftLines, raftSurfaceConfig); gcodeLayer.WriteQueuedGCode(config.RaftInterfaceThicknes_um); } } }
public Aabb(Polygons polys) { min = new IntPoint(long.MinValue, long.MinValue); max = new IntPoint(long.MinValue, long.MinValue); Calculate(polys); }
public static double TotalArea(this Polygons polygons) { return(polygons.Sum(polygon => polygon.Area())); }
//------------------------------------------------------------------------------ private void BuildResult(Polygons polyg) { polyg.Clear(); polyg.Capacity = m_PolyOuts.Count; for (int i = 0; i < m_PolyOuts.Count; i++) { OutRec outRec = m_PolyOuts[i]; if (outRec.pts == null) continue; OutPt p = outRec.pts; int cnt = PointCount(p); if (cnt < 3) continue; Polygon pg = new Polygon(cnt); for (int j = 0; j < cnt; j++) { pg.Add(p.pt); p = p.prev; } polyg.Add(pg); } }
private static Polygons IntersectWithSparsePolygons(List <LayerIsland> islands, Aabb boundsToConsider, Polygons polysToIntersect) { Polygons polysFromIslands = new Polygons(); for (int islandIndex = 0; islandIndex < islands.Count; islandIndex++) { if (boundsToConsider.Hit(islands[islandIndex].BoundingBox)) { if (islands[islandIndex].InsetToolPaths.Count > 0) { polysFromIslands = polysFromIslands.CreateUnion(islands[islandIndex].SparseInfillPaths); polysFromIslands = Clipper.CleanPolygons(polysFromIslands, cleanDistance_um); } } } polysToIntersect = polysToIntersect.CreateIntersection(polysFromIslands); return(polysToIntersect); }
//------------------------------------------------------------------------------ public static Polygons OffsetPolygons(Polygons poly, double delta, JoinType jointype, double MiterLimit) { Polygons result = new Polygons(poly.Count); new PolyOffsetBuilder(poly, result, delta, jointype, MiterLimit, true); return result; }
private static Polygons RemoveIslandsFromPolygons(List <LayerIsland> islands, Aabb boundsToConsider, Polygons polygonsToSubtractFrom) { for (int islandIndex = 0; islandIndex < islands.Count; islandIndex++) { if (boundsToConsider.Hit(islands[islandIndex].BoundingBox)) { if (islands[islandIndex].InsetToolPaths.Count > 0) { polygonsToSubtractFrom = polygonsToSubtractFrom.CreateDifference(islands[islandIndex].InsetToolPaths[0]); polygonsToSubtractFrom = Clipper.CleanPolygons(polygonsToSubtractFrom, cleanDistance_um); } } } return(polygonsToSubtractFrom); }
//------------------------------------------------------------------------------ public static Polygons SimplifyPolygons(Polygons polys, PolyFillType fillType = PolyFillType.pftEvenOdd) { Polygons result = new Polygons(); Clipper c = new Clipper(); c.AddPolygons(polys, PolyType.ptSubject); c.Execute(ClipType.ctUnion, result, fillType, fillType); return result; }
public void GenerateTopAndBottoms(ConfigSettings config, int layerIndex, long extrusionWidth_um, long outerPerimeterWidth_um, int downLayerCount, int upLayerCount, long infillExtendIntoPerimeter_um) { var clippingOffset = infillExtendIntoPerimeter_um * 2; ExtruderLayers extruder = this; SliceLayer layer = extruder.Layers[layerIndex]; for (int islandIndex = 0; islandIndex < layer.Islands.Count; islandIndex++) { LayerIsland island = layer.Islands[islandIndex]; if (island.InsetToolPaths.Count == 0) { continue; } // this is the entire extrusion width to make sure we are outside of the extrusion line Polygons lastInset = island.InsetToolPaths[island.InsetToolPaths.Count - 1]; Polygons infillRegionPath = lastInset.Offset(-extrusionWidth_um); Polygons sparseInfillPaths = new Polygons(infillRegionPath); // calculate the bottom outlines if (downLayerCount > 0) { Polygons bottomOutlines = new Polygons(infillRegionPath); if (layerIndex - 1 >= 0) { var previousLayer = extruder.Layers[layerIndex - 1]; bottomOutlines = RemoveIslandsFromPolygons(previousLayer.Islands, island.BoundingBox, bottomOutlines); bottomOutlines.RemoveSmallAreas(extrusionWidth_um); } sparseInfillPaths = sparseInfillPaths.CreateDifference(bottomOutlines); sparseInfillPaths = Clipper.CleanPolygons(sparseInfillPaths, cleanDistance_um); island.BottomPaths = bottomOutlines; } // calculate the top outlines if (upLayerCount > 0) { Polygons topOutlines = new Polygons(infillRegionPath); topOutlines = topOutlines.CreateDifference(island.BottomPaths.Offset(clippingOffset)); topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um); if (layerIndex + 1 < extruder.Layers.Count) { // Remove the top layer that is above this one to get only the data that is a top layer on this layer. topOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex + 1].Islands, island.BoundingBox, topOutlines); } topOutlines.RemoveSmallAreas(extrusionWidth_um); sparseInfillPaths = sparseInfillPaths.CreateDifference(topOutlines.Offset(clippingOffset)); sparseInfillPaths = Clipper.CleanPolygons(sparseInfillPaths, cleanDistance_um); island.TopPaths = topOutlines; } if (upLayerCount <= 0 && downLayerCount <= 0) { // Assign infill directly if no top/bottom solid layers island.SparseInfillPaths = sparseInfillPaths; } else { // calculate the solid infill outlines Polygons solidInfillPaths = new Polygons(infillRegionPath); // remove all the top layers solidInfillPaths = solidInfillPaths.CreateDifference(island.BottomPaths.Offset(clippingOffset)); solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um); // remove all the bottom layers solidInfillPaths = solidInfillPaths.CreateDifference(island.TopPaths.Offset(clippingOffset)); solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um); int upEnd = layerIndex + upLayerCount + 1; if (upEnd <= extruder.Layers.Count && layerIndex - downLayerCount >= 0) { // find all the regions that have more top and bottom layers than should be solid (will remain sparse) Polygons regionsThatWillBeSparse = new Polygons(infillRegionPath); int upStart = layerIndex + 2; for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++) { regionsThatWillBeSparse = IntersectWithPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, regionsThatWillBeSparse); regionsThatWillBeSparse = Clipper.CleanPolygons(regionsThatWillBeSparse, cleanDistance_um); } // find all the solid infill bottom layers int downStart = layerIndex - 1; int downEnd = layerIndex - downLayerCount; for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--) { regionsThatWillBeSparse = IntersectWithPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, regionsThatWillBeSparse); regionsThatWillBeSparse = Clipper.CleanPolygons(regionsThatWillBeSparse, cleanDistance_um); } solidInfillPaths = solidInfillPaths.CreateDifference(regionsThatWillBeSparse); solidInfillPaths.RemoveSmallAreas(extrusionWidth_um); solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um); } // remove the solid infill from the sparse infill sparseInfillPaths = sparseInfillPaths.CreateDifference(solidInfillPaths.Offset(clippingOffset)); sparseInfillPaths.RemoveSmallAreas(extrusionWidth_um); sparseInfillPaths = Clipper.CleanPolygons(sparseInfillPaths, cleanDistance_um); island.SparseInfillPaths = sparseInfillPaths; if (config == null || // this is to make our tests test the bridgeOverInfill config.BridgeOverInfill) { // now figure out what part of the solid infill is actually first top layers and switch it to that // we can only have a first top y layer at the bottom of the top layers if (layerIndex == extruder.Layers.Count - upLayerCount) { // all of it is first top layers island.FirstTopPaths = solidInfillPaths; solidInfillPaths = new Polygons(); } else if (layerIndex > 0 && layerIndex < extruder.Layers.Count - upLayerCount) { // Intersect the current solid layer with the previous spars layer // that will be all of the new solid layers that are currently on sparse layer var firstTopPaths = new Polygons(solidInfillPaths); firstTopPaths = IntersectWithSparsePolygons(extruder.Layers[layerIndex - 1].Islands, island.BoundingBox, firstTopPaths); firstTopPaths.RemoveSmallAreas(extrusionWidth_um); firstTopPaths = Clipper.CleanPolygons(firstTopPaths, cleanDistance_um); if (firstTopPaths.Count > 0) { solidInfillPaths = solidInfillPaths.CreateDifference(firstTopPaths.Offset(clippingOffset)); solidInfillPaths.RemoveSmallAreas(extrusionWidth_um); solidInfillPaths = Clipper.CleanPolygons(solidInfillPaths, cleanDistance_um); island.FirstTopPaths = firstTopPaths; } } } island.SolidInfillPaths = solidInfillPaths; } } }
//------------------------------------------------------------------------------ public static void AddPolyNodeToPolygons(PolyNode polynode, Polygons polygons) { if (polynode.Contour.Count > 0) polygons.Add(polynode.Contour); foreach (PolyNode pn in polynode.Childs) AddPolyNodeToPolygons(pn, polygons); }
public static Polygons CreateLineIntersections(this Polygons areaToIntersect, Polygons lines) { Clipper clipper = new Clipper(); clipper.AddPaths(lines, PolyType.ptSubject, false); clipper.AddPaths(areaToIntersect, PolyType.ptClip, true); PolyTree clippedLines = new PolyTree(); clipper.Execute(ClipType.ctIntersection, clippedLines); return(Clipper.OpenPathsFromPolyTree(clippedLines)); }
public static Polygons CreateLineDifference(this Polygons linePolygons, Polygons removePolygons) { Clipper clipper = new Clipper(); clipper.AddPaths(linePolygons, PolyType.ptSubject, false); clipper.AddPaths(removePolygons, PolyType.ptClip, true); PolyTree clippedLines = new PolyTree(); clipper.Execute(ClipType.ctDifference, clippedLines); return Clipper.OpenPathsFromPolyTree(clippedLines); }
public static int size(this Polygons polygons) { return(polygons.Count); }
public static Polygons CreateIntersection(this Polygons polygons, Polygons other) { Polygons ret = new Polygons(); Clipper clipper = new Clipper(); clipper.AddPaths(polygons, PolyType.ptSubject, true); clipper.AddPaths(other, PolyType.ptClip, true); clipper.Execute(ClipType.ctIntersection, ret); return ret; }
private static void ProcessPolyTreeNodeIntoSeparatIslands(this Polygons polygonsIn, PolyNode node, List<Polygons> ret) { for (int n = 0; n < node.ChildCount; n++) { PolyNode child = node.Childs[n]; Polygons polygons = new Polygons(); polygons.Add(child.Contour); for (int i = 0; i < child.ChildCount; i++) { polygons.Add(child.Childs[i].Contour); polygonsIn.ProcessPolyTreeNodeIntoSeparatIslands(child.Childs[i], ret); } ret.Add(polygons); } }
public static Polygons CreateUnion(this Polygons polygons, Polygons other) { Clipper clipper = new Clipper(); clipper.AddPaths(polygons, PolyType.ptSubject, true); clipper.AddPaths(other, PolyType.ptSubject, true); Polygons ret = new Polygons(); clipper.Execute(ClipType.ctUnion, ret, PolyFillType.pftNonZero, PolyFillType.pftNonZero); return ret; }
public static Mesh[] SplitIntoMeshesOnOrthographicZ(Mesh meshToSplit, Vector3 buildVolume, ReportProgressRatio reportProgress) { // check if the part is bigger than the build plate (if it is we need to use that as our size) AxisAlignedBoundingBox partBounds = meshToSplit.GetAxisAlignedBoundingBox(); buildVolume.x = Math.Max(buildVolume.x, partBounds.XSize + 2); buildVolume.y = Math.Max(buildVolume.y, partBounds.YSize + 2); buildVolume.z = Math.Max(buildVolume.z, partBounds.ZSize + 2); // Find all the separate objects that are on the plate // Create a 2D image the size of the printer bed at some scale with the parts draw on it top down double scaleFactor = 5; ImageBuffer partPlate = new ImageBuffer((int)(buildVolume.x * scaleFactor), (int)(buildVolume.y * scaleFactor), 32, new BlenderBGRA()); Vector2 renderOffset = new Vector2(buildVolume.x / 2, buildVolume.y / 2) - new Vector2(partBounds.Center.x, partBounds.Center.y); PolygonMesh.Rendering.OrthographicZProjection.DrawTo(partPlate.NewGraphics2D(), meshToSplit, renderOffset, scaleFactor, RGBA_Bytes.White); bool continueProcessin = true; if (reportProgress != null) { reportProgress(.2, "", out continueProcessin); } //ImageIO.SaveImageData("test part plate 0.png", partPlate); // expand the bounds a bit so that we can collect all the vertices and polygons within each bound Dilate.DoDilate3x3Binary(partPlate, 1); //ImageIO.SaveImageData("test part plate 1.png", partPlate); // trace all the bounds of the objects on the plate PolyTree polyTreeForPlate = FindDistictObjectBounds(partPlate); if (polyTreeForPlate == null) { Mesh[] singleMesh = new Mesh[1]; singleMesh[0] = meshToSplit; return singleMesh; } // get all the discrete areas that are polygons so we can search them Polygons discreteAreas = new Polygons(); GetAreasRecursive(polyTreeForPlate, discreteAreas); if (discreteAreas.Count == 0) { return null; } else if (discreteAreas.Count == 1) { Mesh[] singleMesh = new Mesh[1]; singleMesh[0] = meshToSplit; return singleMesh; } Graphics2D graphics2D = partPlate.NewGraphics2D(); graphics2D.Clear(RGBA_Bytes.Black); Random rand = new Random(); foreach (Polygon polygon in discreteAreas) { graphics2D.Render(PlatingHelper.PolygonToPathStorage(polygon), new RGBA_Bytes(rand.Next(128, 255), rand.Next(128, 255), rand.Next(128, 255))); } if (reportProgress != null) { reportProgress(.5, "", out continueProcessin); } //ImageIO.SaveImageData("test part plate 2.png", partPlate); // add each of the separate bounds polygons to new meshes Mesh[] discreteMeshes = new Mesh[discreteAreas.Count]; for (int i = 0; i < discreteAreas.Count; i++) { discreteMeshes[i] = new Mesh(); } foreach (Face face in meshToSplit.Faces) { bool faceDone = false; // figure out which area one or more of the vertices are in add the face to the right new mesh foreach (FaceEdge faceEdge in face.FaceEdges()) { Vector2 position = new Vector2(faceEdge.firstVertex.Position.x, faceEdge.firstVertex.Position.y); position += renderOffset; position *= scaleFactor; for (int areaIndex = discreteAreas.Count - 1; areaIndex >= 0; areaIndex--) { if (PointInPolygon(discreteAreas[areaIndex], new IntPoint((int)position.x, (int)position.y))) { List<Vertex> faceVertices = new List<Vertex>(); foreach (FaceEdge faceEdgeToAdd in face.FaceEdges()) { Vertex newVertex = discreteMeshes[areaIndex].CreateVertex(faceEdgeToAdd.firstVertex.Position); faceVertices.Add(newVertex); } discreteMeshes[areaIndex].CreateFace(faceVertices.ToArray()); faceDone = true; break; } } if (faceDone) { break; } } } if (reportProgress != null) { reportProgress(.8, "", out continueProcessin); } for (int i = 0; i < discreteMeshes.Count(); i++) { Mesh mesh = discreteMeshes[i]; } return discreteMeshes; }
public static Polygons Offset(this Polygons polygons, long distance) { ClipperOffset offseter = new ClipperOffset(); offseter.AddPaths(polygons, JoinType.jtMiter, EndType.etClosedPolygon); Paths solution = new Polygons(); offseter.Execute(ref solution, distance); return solution; }
private static List <Polygons> AccumulateDownPolygons(ConfigSettings config, List <Polygons> inputPolys, List <Polygons> allPartOutlines) { int numLayers = inputPolys.Count; long nozzleSize = config.ExtrusionWidth_um; long areaToTryAndBe = 20 * 20 * nozzleSize * nozzleSize; // 10 x 10 mm approximately (assuming .5 nozzle) List <Polygons> allDownOutlines = CreateEmptyPolygons(numLayers); for (int layerIndex = numLayers - 2; layerIndex >= 0; layerIndex--) { Polygons aboveRequiredSupport = inputPolys[layerIndex + 1]; // get all the polygons above us Polygons accumulatedAbove = allDownOutlines[layerIndex + 1].CreateUnion(aboveRequiredSupport); // experimental and not working well enough yet if (config.MinimizeSupportColumns) { // reduce the amount of support material used for (int i = accumulatedAbove.Count - 1; i >= 0; i--) { Polygon polygon = accumulatedAbove[i]; double polyArea = polygon.Area(); if (polyArea > areaToTryAndBe) { Polygons offsetPolygons = new Polygons() { polygon }.Offset(-config.ExtrusionWidth_um / 2); accumulatedAbove.RemoveAt(i); foreach (Polygon polyToAdd in offsetPolygons) { accumulatedAbove.Insert(i, polyToAdd); } } else if (polyArea < areaToTryAndBe * .9) { Polygons offsetPolygons = new Polygons() { polygon }.Offset(config.ExtrusionWidth_um / 2); accumulatedAbove.RemoveAt(i); foreach (Polygon polyToAdd in offsetPolygons) { accumulatedAbove.Insert(i, polyToAdd); } } } } // add in the support on this level Polygons curRequiredSupport = inputPolys[layerIndex]; Polygons totalSupportThisLayer = accumulatedAbove.CreateUnion(curRequiredSupport); // remove the solid polygons on this level Polygons remainingAbove = totalSupportThisLayer.CreateDifference(allPartOutlines[layerIndex]); allDownOutlines[layerIndex] = Clipper.CleanPolygons(remainingAbove, cleanDistance_um); } return(allDownOutlines); }
public static bool polygonCollidesWithlineSegment(Polygons polys, IntPoint startPoint, IntPoint endPoint) { IntPoint diff = endPoint - startPoint; PointMatrix transformation_matrix = new PointMatrix(diff); IntPoint transformed_startPoint = transformation_matrix.apply(startPoint); IntPoint transformed_endPoint = transformation_matrix.apply(endPoint); return polygonCollidesWithlineSegment(polys, transformed_startPoint, transformed_endPoint, transformation_matrix); }
public static void GenerateGridInfill(ConfigSettings config, Polygons partOutline, Polygons fillPolygons, double fillAngle, int linespacing_um = 0) { if (linespacing_um == 0) { if (config.InfillPercent <= 0) { throw new Exception("infillPercent must be greater than 0."); } linespacing_um = (int)(config.ExtrusionWidth_um / (config.InfillPercent / 100) * 2); } Infill.GenerateLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle); fillAngle += 90; if (fillAngle > 360) { fillAngle -= 360; } Infill.GenerateLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle); }
/// <summary> /// Return a list of polygons of this polygon split into triangles /// </summary> /// <param name="polygons"></param> /// <returns></returns> public static Polygons Triangulate(this Polygons polygons) { throw new NotImplementedException(); }
public static void GenerateHexagonInfill(ConfigSettings config, Polygons partOutline, Polygons fillPolygons, double fillAngle, int layerIndex) { if (config.InfillPercent <= 0) { throw new Exception("infillPercent must be greater than 0."); } int linespacing_um = (int)(config.ExtrusionWidth_um / (config.InfillPercent / 100) * 3 * .66); Infill.GenerateHexLinePaths(partOutline, fillPolygons, linespacing_um, config.InfillExtendIntoPerimeter_um, fillAngle, layerIndex); }
public static Polygons ConvertToLines(Polygons polygons) { Polygons linePolygons = new Polygons(); foreach(Polygon polygon in polygons) { if (polygon.Count > 2) { for (int vertexIndex = 0; vertexIndex < polygon.Count; vertexIndex++) { linePolygons.Add(new Polygon() { polygon[vertexIndex], polygon[(vertexIndex + 1) % polygon.Count] }); } } else { linePolygons.Add(polygon); } } return linePolygons; }
public static Polygon CreateConvexHull(this Polygons polygons) { return(polygons.SelectMany(s => s).ToList().CreateConvexHull()); }
static private void GetAreasRecursive(PolyNode polyTreeForPlate, Polygons discreteAreas) { if (!polyTreeForPlate.IsHole) { discreteAreas.Add(polyTreeForPlate.Contour); } foreach (PolyNode child in polyTreeForPlate.Childs) { GetAreasRecursive(child, discreteAreas); } }
public static void GenerateHexLinePaths(Polygons in_outline, Polygons result, int lineSpacing, int infillExtendIntoPerimeter_um, double rotationDegrees, int layerIndex) { int extraRotationAngle = 0; if (in_outline.Count > 0) { Polygons outlines = in_outline.Offset(infillExtendIntoPerimeter_um); if (outlines.Count > 0) { int perIncrementOffset = (int)(lineSpacing * Math.Sqrt(3) / 2 + .5); PointMatrix matrix = new PointMatrix(-(rotationDegrees + extraRotationAngle)); // we are rotating the part so we rotate by the negative so the lines go the way we expect outlines.ApplyMatrix(matrix); Aabb boundary = new Aabb(outlines); boundary.min.X = ((boundary.min.X / lineSpacing) - 1) * lineSpacing; boundary.min.Y = ((boundary.min.Y / perIncrementOffset) - 2) * perIncrementOffset; boundary.max.X += lineSpacing; boundary.max.Y += perIncrementOffset; Polygons unclipedPatern = new Polygons(); foreach (IntPoint startPoint in StartPositionIterator(boundary, lineSpacing, layerIndex)) { Polygon attachedLine = new Polygon(); foreach (IntPoint center in IncrementPositionIterator(startPoint, boundary, lineSpacing, layerIndex)) { // what we are adding are the little pluses that define the points // | top // | // /\ center // left/ \ right // IntPoint left = center + new IntPoint(-lineSpacing / 2, -perIncrementOffset / 3); IntPoint right = center + new IntPoint(lineSpacing / 2, -perIncrementOffset / 3); IntPoint top = center + new IntPoint(0, perIncrementOffset * 2 / 3); switch (layerIndex % 3) { case 0: // left to right attachedLine.Add(left); attachedLine.Add(center); attachedLine.Add(center); attachedLine.Add(right); unclipedPatern.Add(new Polygon() { top, center }); break; case 1: // left to top attachedLine.Add(left); attachedLine.Add(center); attachedLine.Add(center); attachedLine.Add(top); unclipedPatern.Add(new Polygon() { center, right }); break; case 2: // top to right attachedLine.Add(top); attachedLine.Add(center); attachedLine.Add(center); attachedLine.Add(right); unclipedPatern.Add(new Polygon() { left, center }); break; } } if (attachedLine.Count > 0) { unclipedPatern.Add(attachedLine); } } PolyTree ret = new PolyTree(); Clipper clipper = new Clipper(); clipper.AddPaths(unclipedPatern, PolyType.ptSubject, false); clipper.AddPaths(outlines, PolyType.ptClip, true); clipper.Execute(ClipType.ctIntersection, ret, PolyFillType.pftPositive, PolyFillType.pftEvenOdd); Polygons newSegments = Clipper.OpenPathsFromPolyTree(ret); PointMatrix inversematrix = new PointMatrix((rotationDegrees + extraRotationAngle)); newSegments.ApplyMatrix(inversematrix); result.AddRange(newSegments); } } }