using OpenNest.Controls; using OpenNest.Converters; using OpenNest.Geometry; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Windows.Forms; namespace OpenNest { public class LayoutPart : IPart { private static Font programIdFont; private static Color selectedColor; private static Pen selectedPen; private static Brush selectedBrush; private static Pen leadInPen; private Color color; private Brush brush; private Pen pen; private List _offsetPolygonPoints; private double _cachedOffsetSpacing; private double _cachedOffsetTolerance; private double _cachedOffsetRotation = double.NaN; private Vector? _labelPoint; private PointF _labelScreenPoint; public readonly Part BasePart; static LayoutPart() { programIdFont = new Font(SystemFonts.DefaultFont, FontStyle.Bold | FontStyle.Underline); SelectedColor = Color.FromArgb(90, 150, 200, 255); leadInPen = new Pen(Color.OrangeRed, 1.5f); } private LayoutPart(Part part) { this.BasePart = part; if (part.BaseDrawing.Color.IsEmpty) part.BaseDrawing.Color = Color.FromArgb(130, 204, 130); Color = part.BaseDrawing.Color; } internal bool IsDirty { get; set; } public bool IsSelected { get; set; } public GraphicsPath Path { get; private set; } public GraphicsPath LeadInPath { get; private set; } public Color Color { get { return color; } set { color = value; if (brush != null) brush.Dispose(); brush = new SolidBrush(value); if (pen != null) pen.Dispose(); pen = new Pen(ControlPaint.Dark(value)); } } public void Draw(Graphics g) { if (IsSelected) { g.FillPath(selectedBrush, Path); g.DrawPath(selectedPen, Path); } else { g.FillPath(brush, Path); g.DrawPath(pen, Path); } if (LeadInPath != null) g.DrawPath(leadInPen, LeadInPath); } public void Draw(Graphics g, string id) { if (IsSelected) { g.FillPath(selectedBrush, Path); g.DrawPath(selectedPen, Path); } else { g.FillPath(brush, Path); g.DrawPath(pen, Path); } if (LeadInPath != null) g.DrawPath(leadInPen, LeadInPath); using var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; g.DrawString(id, programIdFont, Brushes.Black, _labelScreenPoint.X, _labelScreenPoint.Y, sf); } public GraphicsPath OffsetPath { get; private set; } private Vector ComputeLabelPoint() { var entities = ConvertProgram.ToGeometry(BasePart.BaseDrawing.Program); var nonRapid = entities.Where(e => e.Layer != SpecialLayers.Rapid).ToList(); var shapes = ShapeBuilder.GetShapes(nonRapid); if (shapes.Count == 0) { var bbox = BasePart.BaseDrawing.Program.BoundingBox(); return new Vector(bbox.Location.X + bbox.Length / 2, bbox.Location.Y + bbox.Width / 2); } var profile = new ShapeProfile(nonRapid); var outer = profile.Perimeter.ToPolygonWithTolerance(0.1); List holes = null; if (profile.Cutouts.Count > 0) { holes = new List(); foreach (var cutout in profile.Cutouts) holes.Add(cutout.ToPolygonWithTolerance(0.1)); } return PolyLabel.Find(outer, holes); } public void Update(DrawControl plateView) { if (BasePart.HasManualLeadIns) { BasePart.Program.GetGraphicsPaths(BasePart.Location, out var cutPath, out var leadPath); cutPath.Transform(plateView.Matrix); leadPath.Transform(plateView.Matrix); Path = cutPath; LeadInPath?.Dispose(); LeadInPath = leadPath; } else { Path = GraphicsHelper.GetGraphicsPath(BasePart.Program, BasePart.Location); Path.Transform(plateView.Matrix); LeadInPath?.Dispose(); LeadInPath = null; } _labelPoint ??= ComputeLabelPoint(); var rotatedLabel = _labelPoint.Value.Rotate(BasePart.Rotation); var labelPt = new PointF( (float)(rotatedLabel.X + BasePart.Location.X), (float)(rotatedLabel.Y + BasePart.Location.Y)); var pts = new[] { labelPt }; plateView.Matrix.TransformPoints(pts); _labelScreenPoint = pts[0]; IsDirty = false; } public void UpdateOffset(double spacing, double tolerance, Matrix matrix) { if (_offsetPolygonPoints == null || spacing != _cachedOffsetSpacing || tolerance != _cachedOffsetTolerance || BasePart.Rotation != _cachedOffsetRotation) { _offsetPolygonPoints = ComputeOffsetPolygons(spacing, tolerance); _cachedOffsetSpacing = spacing; _cachedOffsetTolerance = tolerance; _cachedOffsetRotation = BasePart.Rotation; } RebuildOffsetPath(matrix); } public void InvalidateOffset() { _offsetPolygonPoints = null; } private List ComputeOffsetPolygons(double spacing, double tolerance) { var result = new List(); var entities = ConvertProgram.ToGeometry(BasePart.Program); var profile = new ShapeProfile( entities.Where(e => e.Layer != SpecialLayers.Rapid).ToList()); AddOffsetPolygon(result, profile.Perimeter.OffsetOutward(spacing), tolerance); foreach (var cutout in profile.Cutouts) AddOffsetPolygon(result, cutout.OffsetInward(spacing), tolerance); return result; } private static void AddOffsetPolygon(List result, Shape offsetEntity, double tolerance) { if (offsetEntity == null) return; var polygon = offsetEntity.ToPolygonWithTolerance(tolerance); polygon.RemoveSelfIntersections(); if (polygon.Vertices.Count < 2) return; var pts = new PointF[polygon.Vertices.Count]; for (var j = 0; j < pts.Length; j++) pts[j] = new PointF((float)polygon.Vertices[j].X, (float)polygon.Vertices[j].Y); result.Add(pts); } private void RebuildOffsetPath(Matrix matrix) { OffsetPath?.Dispose(); if (_offsetPolygonPoints == null || _offsetPolygonPoints.Count == 0) { OffsetPath = null; return; } var path = new GraphicsPath(); var dx = (float)BasePart.Location.X; var dy = (float)BasePart.Location.Y; foreach (var pts in _offsetPolygonPoints) { var offsetPts = new PointF[pts.Length]; for (var i = 0; i < pts.Length; i++) offsetPts[i] = new PointF(pts[i].X + dx, pts[i].Y + dy); path.AddLines(offsetPts); path.StartFigure(); } path.Transform(matrix); OffsetPath = path; } public static LayoutPart Create(Part part, PlateView plateView) { var layoutPart = new LayoutPart(part); layoutPart.Update(plateView); return layoutPart; } public static Color SelectedColor { get { return selectedColor; } set { selectedColor = value; if (selectedBrush != null) selectedBrush.Dispose(); selectedBrush = new SolidBrush(value); if (selectedPen != null) selectedPen.Dispose(); selectedPen = new Pen(ControlPaint.Dark(value)); } } public Vector Location { get { return BasePart.Location; } set { BasePart.Location = value; IsDirty = true; } } public double Rotation { get { return BasePart.Rotation; } } public void Rotate(double angle) { BasePart.Rotate(angle); IsDirty = true; } public void Rotate(double angle, Vector origin) { BasePart.Rotate(angle, origin); IsDirty = true; } public void Offset(double x, double y) { BasePart.Offset(x, y); IsDirty = true; } public void Offset(Vector voffset) { BasePart.Offset(voffset); IsDirty = true; } public Box BoundingBox { get { return BasePart.BoundingBox; } } public double Left { get { return BasePart.Left; } } public double Right { get { return BasePart.Right; } } public double Top { get { return BasePart.Top; } } public double Bottom { get { return BasePart.Bottom; } } public void UpdateBounds() { BasePart.UpdateBounds(); } public void Update() { Color = BasePart.BaseDrawing.Color; } } }