/* * 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.Collections; using System.Collections.Generic; using System.Drawing; using System.Runtime.CompilerServices; namespace UVtools.Core { #region LayerIssue Class public class IslandDetectionConfiguration { /// /// Gets or sets if the detection is enabled /// public bool Enabled { get; set; } = true; /// /// Gets or sets a list of layers to check for islands, absent layers will not be checked. /// Set to null to check every layer /// public List WhiteListLayers { get; set; } = null; /// /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white /// Set to 0 to disable this operation /// public byte BinaryThreshold { get; set; } = 1; /// /// Gets the required area size (x*y) to consider process a island (0-255) /// public byte RequiredAreaToProcessCheck { get; set; } = 1; /// /// Gets the required brightness for check a pixel under a island (0-255) /// public byte RequiredPixelBrightnessToProcessCheck { get; set; } = 10; /// /// Gets the required number of pixels to support a island and discard it as a issue (0-255) /// public byte RequiredPixelsToSupport { get; set; } = 10; /// /// Gets the required brightness of supporting pixels to count as a valid support (0-255) /// public byte RequiredPixelBrightnessToSupport { get; set; } = 150; /// /// Gets the setting for whether or not diagonal bonds are considered when evaluation islands. /// If true, all 8 neighbors of a pixel (including diagonals) will be considered when finding /// individual components on the layer, if false only 4 neighbors (right, left, above, below) /// will be considered.. /// public bool AllowDiagonalBonds { get; set; } = false; } /// /// Overhang configuration /// public class OverhangDetectionConfiguration { /// /// Gets or sets if the detection is enabled /// public bool Enabled { get; set; } = true; /// /// Gets or sets a list of layers to check for overhangs, absent layers will not be checked. /// Set to null to check every layer /// public List WhiteListLayers { get; set; } = null; /// /// Gets or sets if should take in consideration the islands, if yes a island can't be a overhang at same time, otherwise islands and overhangs can be shared /// public bool IndependentFromIslands { get; set; } = true; /// /// After compute overhangs, masses with a number of pixels bellow this number will be discarded (Not a overhang) /// public byte RequiredPixelsToConsider { get; set; } = 1; /// /// Previous layer will be subtracted from current layer, after will erode by this value. /// The survived pixels are potential overhangs. /// public byte ErodeIterations { get; set; } = 40; } public class ResinTrapDetectionConfiguration { /// /// Gets or sets if the detection is enabled /// public bool Enabled { get; set; } = true; /// /// Gets or sets the binary threshold, all pixels below this value will turn in black, otherwise white /// Set to 0 to disable this operation /// public byte BinaryThreshold { get; set; } = 127; /// /// Gets the required area size (x*y) to consider process a hollow area (0-255) /// public byte RequiredAreaToProcessCheck { get; set; } = 1; /// /// Gets the number of black pixels required to consider a drain /// public byte RequiredBlackPixelsToDrain { get; set; } = 10; /// /// Gets the maximum pixel brightness to be a drain pixel (0-150) /// public byte MaximumPixelBrightnessToDrain { get; set; } = 30; } public class TouchingBoundDetectionConfiguration { /// /// Gets if the detection is enabled /// public bool Enabled { get; set; } = true; /// /// Gets the maximum pixel brightness to be a touching bound /// public byte MaximumPixelBrightness { get; set; } = 200; } public class LayerIssue : IEnumerable { public enum IssueType : byte { Island, Overhang, ResinTrap, TouchingBound, EmptyLayer, //HoleSandwich, } /// /// Gets the parent layer /// public Layer Layer { get; } /// /// Gets the layer index /// public uint LayerIndex => Layer.Index; /// /// Gets the issue type associated /// public IssueType Type { get; } /// /// Gets the pixels points containing the issue /// public Point[] Pixels { get; } public int PixelsCount => Pixels?.Length ?? 0; /// /// Gets the bounding rectangle of the pixel area /// public Rectangle BoundingRectangle { get; } /// /// Gets the X coordinate for the first point, -1 if doesn't exists /// public int X => HaveValidPoint ? Pixels[0].X : -1; /// /// Gets the Y coordinate for the first point, -1 if doesn't exists /// public int Y => HaveValidPoint ? Pixels[0].Y : -1; /// /// Gets the XY point for first point /// public Point FirstPoint => HaveValidPoint ? Pixels[0] : new Point(-1, -1); public string FirstPointStr => $"{FirstPoint.X}, {FirstPoint.Y}"; /// /// Gets the number of pixels on this issue /// public uint Size { get { if (Type == IssueType.ResinTrap && !BoundingRectangle.IsEmpty) { return (uint)(BoundingRectangle.Width * BoundingRectangle.Height); } if (ReferenceEquals(Pixels, null)) return 0; return (uint)Pixels.Length; } } /// /// Check if this issue have a valid start point to show /// public bool HaveValidPoint => !ReferenceEquals(Pixels, null) && Pixels.Length > 0; public LayerIssue(Layer layer, IssueType type, Point[] pixels = null, Rectangle boundingRectangle = new Rectangle()) { Layer = layer; Type = type; Pixels = pixels; BoundingRectangle = boundingRectangle; } public Point this[uint index] => Pixels[index]; public Point this[int index] => Pixels[index]; public IEnumerator GetEnumerator() { return ((IEnumerable)Pixels).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public override string ToString() { return $"{nameof(Type)}: {Type}, Layer: {Layer.Index}, {nameof(X)}: {X}, {nameof(Y)}: {Y}, {nameof(Size)}: {Size}"; } } #endregion #region LayerHollowArea public class LayerHollowArea : IEnumerable { public enum AreaType : byte { Unknown = 0, Trap, Drain } /// /// Gets area pixels /// public Point[] Contour { get; } public Rectangle BoundingRectangle { get; } public AreaType Type { get; set; } = AreaType.Unknown; public bool Processed { get; set; } #region Indexers public Point this[uint index] { get => index < Contour.Length ? Contour[index] : Point.Empty; set => Contour[index] = value; } public Point this[int index] { get => index < Contour.Length ? Contour[index] : Point.Empty; set => Contour[index] = value; } public Point this[uint x, uint y] { get { for (uint i = 0; i < Contour.Length; i++) { if (Contour[i].X == x && Contour[i].Y == y) return Contour[i]; } return Point.Empty; } } public Point this[int x, int y] => this[(uint)x, (uint)y]; public Point this[Point point] => this[point.X, point.Y]; #endregion public IEnumerator GetEnumerator() { return ((IEnumerable)Contour).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public LayerHollowArea() { } public LayerHollowArea(Point[] contour, Rectangle boundingRectangle, AreaType type = AreaType.Unknown) { Contour = contour; BoundingRectangle = boundingRectangle; Type = type; } } #endregion }