/* * GNU AFFERO GENERAL PUBLIC LICENSE * Version 3, 19 November 2007 * Copyright (C) 2007 Free Software Foundation, Inc. * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using UVtools.Core.Extensions; namespace UVtools.Core.Layers; [XmlInclude(typeof(IssueOfContours))] [XmlInclude(typeof(IssueOfPoints))] public class MainIssue : IReadOnlyList { public enum IssueType : byte { Island, Overhang, ResinTrap, SuctionCup, TouchingBound, PrintHeight, EmptyLayer, Debug //HoleSandwich, } public bool IsIsland => Type == IssueType.Island; public bool IsOverhang => Type == IssueType.Overhang; public bool IsResinTrap => Type == IssueType.ResinTrap; public bool IsSuctionCup => Type == IssueType.SuctionCup; public bool IsTouchingBound => Type == IssueType.TouchingBound; public bool IsPrintHeight => Type == IssueType.PrintHeight; public bool IsEmptyLayer => Type == IssueType.EmptyLayer; public bool IsDebug => Type == IssueType.Debug; /// /// Gets the issue type associated /// public IssueType Type { get; init; } /// /// Gets the layer where issue is present and starts /// [JsonIgnore] public Layer StartLayer => Childs[0].Layer; /// /// Gets the layer where issue ends /// [JsonIgnore] public Layer EndLayer => Childs[^1].Layer; /// /// Gets the layer index /// public uint StartLayerIndex => StartLayer.Index; /// /// Gets the layer index /// public uint EndLayerIndex => EndLayer.Index; /// /// Gets the number of layers in this range /// public uint LayerRangeCount => 1 + EndLayerIndex - StartLayerIndex; public string LayerInfoStr => StartLayerIndex == EndLayerIndex ? $"{StartLayerIndex}" : $"{StartLayerIndex}-{EndLayerIndex} ({LayerRangeCount})"; /// /// Gets the total height that represents this issue /// public float TotalHeight => Layer.RoundHeight(StartLayer.LayerHeight + EndLayer.PositionZ - StartLayer.PositionZ); /// /// Gets the bounding rectangle of the area /// public Rectangle BoundingRectangle { get; init; } /// /// Gets the area of the issue /// public uint PixelCount { get; init; } /// /// Gets the area of the issue /// public double Area { get; init; } /// /// Gets the area character, either ² or ³ /// public char AreaChar => LayerRangeCount > 1 ? '³' : '²'; /// /// Gets all issues inside this main issue /// public Issue[] Childs { get; init; } = Array.Empty(); public MainIssue() { } public MainIssue(IssueType type, Rectangle boundingRectangle = default) { Type = type; BoundingRectangle = boundingRectangle; Area = boundingRectangle.Area(); } public MainIssue(IssueType type, Issue issue) : this(type, issue.BoundingRectangle) { Childs = new[] { issue }; issue.Parent = this; PixelCount = issue.PixelsCount; Area = issue switch { IssueOfPoints => issue.PixelsCount, IssueOfContours => issue.Area, _ => issue.Area }; } public MainIssue(IssueType type, IEnumerable issues) : this(type) { foreach (var issue in issues) { issue.Parent = this; var layerHeightInPixels = issue.Layer.SlicerFile.MillimetersToPixelsF(issue.Layer.LayerHeight, 20); Area += issue.Area * layerHeightInPixels; PixelCount += issue.PixelsCount; if (issue.BoundingRectangle.IsEmpty) continue; if (BoundingRectangle.IsEmpty) { BoundingRectangle = issue.BoundingRectangle; continue; } BoundingRectangle = Rectangle.Union(BoundingRectangle, issue.BoundingRectangle); } if (Childs.Length == 1) { Area = Childs[0].Area; Childs = issues.ToArray(); } else { Childs = issues.OrderBy(issue => issue.LayerIndex).ToArray(); } Area = Math.Round(Area, 3); } private void Sort() { if (Childs.Length < 1) return; Array.Sort(Childs, (issue, issue1) => issue.LayerIndex.CompareTo(issue1.LayerIndex)); } public bool IsIssueInBetween(int layerIndex) => layerIndex >= StartLayerIndex && layerIndex <= EndLayerIndex; public bool IsIssueInBetween(uint layerIndex) => layerIndex >= StartLayerIndex && layerIndex <= EndLayerIndex; public bool IsIssueInBetween(Layer layer) => IsIssueInBetween(layer.Index); public IEnumerator GetEnumerator() { return ((IEnumerable)Childs).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Childs.GetEnumerator(); } public int Count => Childs.Length; public Issue this[int index] => Childs[index]; protected bool Equals(MainIssue other) { return Type == other.Type && BoundingRectangle.Equals(other.BoundingRectangle) && PixelCount == other.PixelCount && Area.Equals(other.Area) && Childs.SequenceEqual(other.Childs); } public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((MainIssue)obj); } public override int GetHashCode() { return HashCode.Combine((int)Type, BoundingRectangle, PixelCount, Area, Childs); } }