Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
5203ce801This file is pinned to the selected release. Download the original to open it in your design tool.
using ArtWork;
using GerberLibrary.Core;
using GlmNet;
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.Fonts;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TilingLibrary;
using TilingLibrary.Compatibility;
using Bitmap = SixLabors.ImageSharp.Image<SixLabors.ImageSharp.PixelFormats.Rgba32>;
using Graphics = TilingLibrary.Compatibility.GraphicsInterface;
using Color = SixLabors.ImageSharp.Color;
using PointF = SixLabors.ImageSharp.PointF;
using SizeF = SixLabors.ImageSharp.SizeF;
using Font = SixLabors.Fonts.Font;
using FontStyle = SixLabors.Fonts.FontStyle;
using Pen = TilingLibrary.Compatibility.Primitives.Pen;
using SolidBrush = TilingLibrary.Compatibility.Primitives.SolidBrush;
using Matrix = TilingLibrary.Compatibility.Matrix;
using GraphicsPath = TilingLibrary.Compatibility.Primitives.GraphicsPath;
using DirectBitmap = TilingLibrary.Compatibility.DirectBitmap;
using ImageSharpGraphicsInterface = TilingLibrary.Compatibility.ImageSharpGraphicsInterface;
using FontFamily = TilingLibrary.Compatibility.Primitives.FontFamily;
using StringFormat = TilingLibrary.Compatibility.Primitives.StringFormat;
using Primitives = TilingLibrary.Compatibility.Primitives;
namespace Artwork
{
using Path = List<ClipperLib.IntPoint>;
using Paths = List<List<ClipperLib.IntPoint>>;
public class SolidQuadTreeItem : QuadTreeItem
{
int _x;
int _y;
public int x
{
get
{
return _x;
}
set
{
_x = value;
}
}
public int y
{
get
{
return _y;
}
set
{
_y = value;
}
}
}
public class TINRSArtWorkRenderer
{
public static Color InterpolateColor(Color color1, Color color2, float v)
{
var p1 = color1.ToPixel<Rgba32>();
var p2 = color2.ToPixel<Rgba32>();
float iv = 1.0f - v;
var R = (byte)(p1.R * iv) + (byte)(p2.R * v);
var G = (byte)(p1.G * iv) + (byte)(p2.G * v);
var B = (byte)(p1.B * iv) + (byte)(p2.B * v);
var A = (byte)(p1.A * iv) + (byte)(p2.A * v);
return Color.FromRgba((byte)R, (byte)G, (byte)B, (byte)A);
}
// From: http://stackoverflow.com/a/11448060/368354
public static void SaveAsIcon(List<Bitmap> SourceBitmaps, string FilePath)
{
FileStream FS = new FileStream(FilePath, FileMode.Create);
// ICO header
FS.WriteByte(0); FS.WriteByte(0); // reserved
FS.WriteByte(1); FS.WriteByte(0); // type = icon
FS.WriteByte((byte)SourceBitmaps.Count); FS.WriteByte(0); // number of images
List<MemoryStream> Files = new List<MemoryStream>();
List<long> SizeIdx = new List<long>();
List<long> OffIdx = new List<long>();
foreach (var b in SourceBitmaps)
{
// Image size
// Set to 0 for 256 px width/height
if (b.Width < 256) FS.WriteByte((byte)b.Width); else FS.WriteByte(0);
if (b.Height < 256) FS.WriteByte((byte)b.Height); else FS.WriteByte(0);
// Palette
FS.WriteByte(0);
// Reserved
FS.WriteByte(0);
// Number of color planes
FS.WriteByte(1); FS.WriteByte(0);
// Bits per pixel
FS.WriteByte(32); FS.WriteByte(0);
// Data size, will be written after the data
SizeIdx.Add(FS.Length);
FS.WriteByte(0);
FS.WriteByte(0);
FS.WriteByte(0);
FS.WriteByte(0);
OffIdx.Add(FS.Length);
// Offset to image data
FS.WriteByte(0);
FS.WriteByte(0);
FS.WriteByte(0);
FS.WriteByte(0);
MemoryStream MS = new MemoryStream();
b.SaveAsPng(MS);
MS.Seek(0, SeekOrigin.Begin);
Files.Add(MS);
}
long CurrentOff = FS.Length;
for (int i = 0; i < Files.Count; i++)
{
var F = Files[i];
var OffTgt = OffIdx[i];
var LenTgt = SizeIdx[i];
long Len = F.Length;
FS.Seek(LenTgt, SeekOrigin.Begin);
FS.WriteByte((byte)Len);
FS.WriteByte((byte)(Len >> 8));
FS.WriteByte((byte)(Len >> 16));
FS.WriteByte((byte)(Len >> 24));
FS.Seek(OffTgt, SeekOrigin.Begin);
FS.WriteByte((byte)CurrentOff);
FS.WriteByte((byte)(CurrentOff >> 8));
FS.WriteByte((byte)(CurrentOff >> 16));
FS.WriteByte((byte)(CurrentOff >> 24));
FS.Seek(0, SeekOrigin.End);
F.CopyTo(FS);
CurrentOff = FS.Length;
}
FS.Close();
}
public DelaunayBuilder Delaunay = new DelaunayBuilder();
public QuadTreeNode MaskTree;
public QuadTreeNode ArtTree;
public Tiling.TilingDefinition TD = new Tiling.TilingDefinition();
public List<Tiling.Polygon> SubDivPoly = new List<Tiling.Polygon>();
public static Font GetAdjustedFont(Graphics GraphicRef, string GraphicString, Font OriginalFont, float ContainerWidth, float MaxFontSize, float MinFontSize, bool SmallestOnFail)
{
// We utilize MeasureString which we get via a control instance
for (float AdjustedSize = MaxFontSize; AdjustedSize >= MinFontSize; AdjustedSize--)
{
Font TestFont = SystemFonts.CreateFont(OriginalFont.Name, AdjustedSize);
// Test the string with the new size
var measuredSize = GraphicRef.MeasureString(GraphicString);
SizeF AdjustedSizeNew = new SizeF((float)measuredSize.X, (float)measuredSize.Y);
if (ContainerWidth > Convert.ToInt32(AdjustedSizeNew.Width))
{
// Good font, return it
return TestFont;
}
}
// If you get here there was no fontsize that worked
// return MinimumSize or Original?
if (SmallestOnFail)
{
return SystemFonts.CreateFont(OriginalFont.Name, MinFontSize);
}
else
{
return OriginalFont;
}
}
public static void DrawIcon(int w, int h, Graphics G, string Label, float huerange = -1)
{
Bitmap Output = new Bitmap(w, h);
Graphics g = new ImageSharpGraphicsInterface(Output);
TINRSArtWorkRenderer Rend = new TINRSArtWorkRenderer();
Settings TheSettings = Rend.GetHashSettings(Label, huerange);
TheSettings.InvertSource = true;
//TheSettings.MaxSubDiv = 4;
Bitmap TileGfx = new Bitmap(w, h);
Graphics G3 = new ImageSharpGraphicsInterface(TileGfx);
G3.Clear(Color.Transparent);
float D = 2;
float x1 = D / 2;
float x2 = w - x1;
float y1 = D / 2;
float y2 = h - x1;
Font F = SystemFonts.CreateFont("Panton ExtraBold", h * 0.5f, FontStyle.Bold);
F = GetAdjustedFont(g, Label, F, w * 0.9f, h * 0.9f, 3, false);
var S = g.MeasureString(Label);
var S2 = g.MeasureString("WO");
S.Y = S2.Y;
float BaseScale = w / 128.0f;
GraphicsPath GP = new GraphicsPath();
var fontFamily = new FontFamily() { Name = "Panton ExtraBold" };
GP.AddString(Label, fontFamily, 0, F.Size * 1.333333f, new PointF(w / 2 - (float)S.X / 2, h / 2 - (float)S.Y / 2), new StringFormat());
Bitmap M = new Bitmap(w, h);
Graphics G2 = new ImageSharpGraphicsInterface(M);
G2.Clear(Color.White);
G2.CompositingQuality = Primitives.CompositingQuality.HighQuality;
G2.InterpolationMode = Primitives.GraphicsInterpolationMode.High;
G2.TextRenderingHint = Primitives.TextRenderingHint.AntiAlias;
G2.SmoothingMode = Primitives.SmoothingMode.AntiAlias;
RenderIconBackdrop(G2, Color.Black, TheSettings, x1, x2, y1, y2, 2, Rend);
G2.DrawPath(new Pen(Color.Black, 5), GP.ToImageSharpPath());
// G2.DrawString(Letter.Text, F, new SolidBrush(Color.White), w / 2 - S.X / 2, h / 2 - S.Y / 2);
G2.DrawPath(new Pen(Color.White, Math.Max(2.0f, 6 * BaseScale)), GP.ToImageSharpPath());
G2.FillPath(new SolidBrush(Color.White), GP.ToImageSharpPath());
g.DrawPath(new Pen(TheSettings.BackGroundColor, 5), GP.ToImageSharpPath());
Rend.BuildTree(M, TheSettings);
Rend.BuildStuff(M, TheSettings);
g.CompositingQuality = Primitives.CompositingQuality.HighQuality;
g.InterpolationMode = Primitives.GraphicsInterpolationMode.High;
g.SmoothingMode = Primitives.SmoothingMode.AntiAlias;
g.TextRenderingHint = Primitives.TextRenderingHint.AntiAlias;
float BaseScale2 = 1.0f;
// g.FillPath(new SolidBrush(Color.Teal), GP);
TheSettings = Rend.GetHashSettings(Label, huerange);
RenderIconBackdrop(g, TheSettings.BackGroundColor, TheSettings, x1, x2, y1, y2, 0, Rend);
Rend.DrawTiling(TheSettings, M, G3, Color.FromRgba((byte)0, (byte)0, (byte)0, (byte)40), Color.Black, Math.Max(3, 4.5f * BaseScale2), false);
Rend.DrawTiling(TheSettings, M, G3, TheSettings.BackgroundHighlight, Color.Black, Math.Max(1.4f, 3 * BaseScale2), false);
Rend.DrawTiling(TheSettings, M, G3, Color.FromRgba((byte)255, (byte)255, (byte)0, (byte)100), Color.Black, Math.Max(1.0f, 1.4f * BaseScale2), false);
// Composite TileGfx onto Output using ProcessPixelRows for performance
Output.ProcessPixelRows(TileGfx, (outputAccessor, tileAccessor) =>
{
for (int y = 0; y < outputAccessor.Height; y++)
{
Span<Rgba32> outputRow = outputAccessor.GetRowSpan(y);
Span<Rgba32> tileRow = tileAccessor.GetRowSpan(y);
for (int x = 0; x < outputRow.Length; x++)
{
var c = Color.FromRgba(outputRow[x].R, outputRow[x].G, outputRow[x].B, outputRow[x].A);
if (outputRow[x].A > 0)
{
var tp = Color.FromRgba(tileRow[x].R, tileRow[x].G, tileRow[x].B, tileRow[x].A);
var c2 = InterpolateColor(c, tp, tileRow[x].A / 255.0f);
var c2Pixel = c2.ToPixel<Rgba32>();
outputRow[x] = new Rgba32(c2Pixel.R, c2Pixel.G, c2Pixel.B, outputRow[x].A);
}
}
}
});
g.DrawPath(new Pen(Color.FromRgba((byte)0, (byte)0, (byte)0, (byte)60), 3), GP.ToImageSharpPath());
g.FillPath(new SolidBrush(Color.FromRgba((byte)255, (byte)255, (byte)255, (byte)255)), GP.ToImageSharpPath());
G.DrawImage(Output, 0, 0, w, h);
}
private static void RenderIconBackdrop(Graphics g, Color C, Settings TheSettings, float x1, float x2, float y1, float y2, int offset, TINRSArtWorkRenderer R)
{
// R.TD.Create(TheSettings.TileType); // Requires checking Tiling.cs changes checking if Create exists or what arguments it takes. Assuming no change needed here if other errors dont complain about this.
R.TD.Create(TheSettings.TileType);
var M = R.TD.NormalizeSize();
var T = R.TD.DivisionSet[0];
float w = x2 - x1;
float h = y2 - y1;
for (float s = 0.3f; s < .5; s += 0.05f)
{
List<PointF> P = new List<PointF>();
P.Add(new PointF((T.A.x - M.x) * w * s + w / 2, (T.A.y - M.y) * h * s + h / 2));
P.Add(new PointF((T.B.x - M.x) * w * s + w / 2, (T.B.y - M.y) * h * s + h / 2));
P.Add(new PointF((T.C.x - M.x) * w * s + w / 2, (T.C.y - M.y) * h * s + h / 2));
float ox = (float)TheSettings.Rand.NextDouble()* w/4;
float oy = (float)TheSettings.Rand.NextDouble()* h/4;
float baserotation = 360.0f * (float)TheSettings.Rand.NextDouble();
for (int i = 0; i < 5; i++)
{
Matrix Mm = new Matrix();
Mm.RotateAt(360.0f * baserotation + (360.0f*i)/5.0f, new PointF(w / 2, h / 2));
Mm.Translate(ox, oy);
var Pa = P.ToArray();
Mm.TransformPoints(Pa);
//g.FillPolygon(new SolidBrush(C), Pa);
Matrix Mm2 = new Matrix();
Mm2.RotateAt(360.0f * baserotation - (360.0f * i) / 5.0f, new PointF(w / 2, h / 2));
Mm2.Translate(ox, oy);
//Mm2.Scale(-1, 1);
var Pa2 = P.ToArray();
Mm2.TransformPoints(Pa2);
g.FillPolygon(new SolidBrush(C), Pa2);
}
}
// g.FillEllipse(new SolidBrush(C), new RectangleF(x1 + 7, y1 + 7 , x2 - x1, y2 - y1));
}
public static void SaveMultiIcon(string outputfile, string label, float huerange = -1)
{
//Bitmap B1 = new Bitmap(16, 16, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
//Graphics.FromImage(B1).Clear(Color.Transparent); ;
//B1.MakeTransparent(Color.Transparent);
//Artwork.TINRSArtWorkRenderer.DrawIcon(B1.Width, B1.Height, Graphics.FromImage(B1), label, huerange);
Bitmap B2 = new Bitmap(32, 32);
new ImageSharpGraphicsInterface(B2).Clear(Color.Transparent);
Artwork.TINRSArtWorkRenderer.DrawIcon(B2.Width, B2.Height, new ImageSharpGraphicsInterface(B2), label, huerange);
Bitmap B2b = new Bitmap(48, 48);
new ImageSharpGraphicsInterface(B2b).Clear(Color.Transparent);
Artwork.TINRSArtWorkRenderer.DrawIcon(B2b.Width, B2b.Height, new ImageSharpGraphicsInterface(B2b), label, huerange);
Bitmap B3 = new Bitmap(64, 64);
new ImageSharpGraphicsInterface(B3).Clear(Color.Transparent);
Artwork.TINRSArtWorkRenderer.DrawIcon(B3.Width, B3.Height, new ImageSharpGraphicsInterface(B3), label, huerange);
Bitmap B4 = new Bitmap(128, 128);
new ImageSharpGraphicsInterface(B4).Clear(Color.Transparent);
Artwork.TINRSArtWorkRenderer.DrawIcon(B4.Width, B4.Height, new ImageSharpGraphicsInterface(B4), label, huerange);
List<Bitmap> IcoBMPs = new List<Bitmap>() { B2, B2b, B3, B4 };
Artwork.TINRSArtWorkRenderer.SaveAsIcon(IcoBMPs, outputfile);
}
public void DrawTiling(Settings S, Bitmap MaskBitmap, Graphics G, Color FGColor, Color BGColor, float linewidth, bool Clear = true)
{
G.InterpolationMode = Primitives.GraphicsInterpolationMode.High;
G.SmoothingMode = Primitives.SmoothingMode.AntiAlias;
Color FG = FGColor;
Color BG = BGColor;
if (S.InvertOutput)
{
FG = BGColor;
BG = FGColor;
}
if (S.Mode == Settings.ArtMode.QuadTree)
{
if (ArtTree != null)
{
if (Clear) G.Clear(BG);
G.RotateTransform(S.DegreesOff);
// e.Graphics.TranslateTransform(0, -140);
ArtTree.DrawArt(G, FG);
}
}
if (S.Mode == Settings.ArtMode.Delaunay)
{
Delaunay.Render(G, FG, BG);
}
if (S.Mode == Settings.ArtMode.Tiling )
{
if (Clear) G.Clear(BG);
Pen P = new Pen(FG, linewidth);
for (int j = 0; j < SubDivPoly.Count; j++)
{
var a = SubDivPoly[j];
PointF[] ThePoints = new PointF[a.Vertices.Count];
for (int i = 0; i < a.Vertices.Count; i++)
{
ThePoints[i] = new PointF((float)a.Vertices[i].x,(float)a.Vertices[i].y);
}
G.DrawPolygon(P, ThePoints);
}
}
}
public static Color GetHashColor(string text)
{
return MakeColor(HashHue(text));
}
public static Color GetHashHighlight(string text)
{
return MakeHighlight(HashHue(text));
}
public static Color MakeColor(double H)
{
int r, g, b;
// GerberLibrary.MathHelpers.HsvToRgb(H, 1.0, 0.5, out r, out g, out b);
return Helpers.Refraction(((float)H / 360.0f)*0.3f + 0.2f);
// return Color.FromArgb(r, g, b);
}
public static Color MakeHighlight(double H)
{
double DH = ((H + 60) % 120) - 60;
H += DH * 0.4;
return Helpers.Refraction(((float)H / 360.0f) * 0.3f + 0.2f);
// GerberLibrary.MathHelpers.HsvToRgb(H, 1.0, 0.7, out r, out g, out b);
// return Color.FromArgb(r, g, b);
}
private static double HashHue(string text)
{
double H = 0;
for (int i = 0; i < text.Length; i++)
{
H += ((text[i] - 'A') % 26) * (360.0 / 26);
H = H % 360;
}
return H;
}
public Settings GetHashSettings(string text, float huerange = -1)
{
Settings S = new Settings();
S.MarcelPlating = false;
S.BackGroundColor = GetHashColor(text);
S.BackgroundHighlight = GetHashHighlight(text);
if (huerange > -1)
{
S.BackGroundColor = MakeColor(huerange * 360.0f);
S.BackgroundHighlight = MakeHighlight(huerange * 360.0f);
}
S.TileType = Tiling.TilingType.RegularTriangle;
S.MaxSubDiv = 6;
for (int i = 0; i < text.Length; i++)
{
S.DegreesOff += (int)(text[i] * 112.123);
S.DegreesOff = S.DegreesOff % 360;
}
S.Rand = new Random((int)(S.DegreesOff * 100.0));
return S;
}
public void BuildTree(Bitmap Mask, Settings TheSettings)
{
int i = Math.Max(Mask.Width, Mask.Height);
int R = 1;
while (R < i) R *= 2;
MaskTree = new QuadTreeNode() { xstart = 0, ystart = 0, xend = R, yend = R };
float ThresholdLevel = TheSettings.Threshold * 0.01f;
// Use ImageSharp ProcessPixelRows for fast pixel access
Mask.ProcessPixelRows(accessor =>
{
for (int yy = 0; yy < accessor.Height; yy++)
{
Span<Rgba32> row = accessor.GetRowSpan(yy);
for (int xx = 0; xx < row.Length; xx++)
{
var pixel = row[xx];
Color C = Color.FromRgba(pixel.R, pixel.G, pixel.B, pixel.A);
bool doit = false;
if (TheSettings.InvertSource)
{
doit = C.GetBrightness() > ThresholdLevel;
}
else
{
doit = C.GetBrightness() < ThresholdLevel;
}
if (doit)
{
MaskTree.Insert(xx, yy, new SolidQuadTreeItem() { x = (int)xx, y = (int)yy }, 8);
}
}
}
});
/*
for (int x = 0; x < Mask.Width; x++)
{
for (int y = 0; y < Mask.Height; y++)
{
var C = Mask.GetPixel(x, y);
bool doit = false;
if (TheSettings.InvertSource)
{
doit = C.GetBrightness() > ThresholdLevel;
}
else
{
doit = C.GetBrightness() < ThresholdLevel;
}
if (doit)
{
MaskTree.Insert(x, y, new SolidQuadTreeItem() { x = (int)x, y = (int)y }, 8);
}
}
}
*/
}
/// <summary>
/// Builds artwork geometry from the mask using current settings.
/// This overload maintains backward compatibility.
/// </summary>
public int BuildStuff(Bitmap aMask, Settings TheSettings)
{
return BuildStuff(aMask, TheSettings, CancellationToken.None, null);
}
/// <summary>
/// Builds artwork geometry from the mask using current settings.
/// Supports cancellation and progress reporting for background execution.
/// </summary>
/// <param name="aMask">The mask bitmap</param>
/// <param name="TheSettings">Rendering settings</param>
/// <param name="cancellationToken">Token to cancel the operation</param>
/// <param name="progress">Progress reporter for UI updates</param>
/// <returns>Elapsed time in milliseconds</returns>
public int BuildStuff(Bitmap aMask, Settings TheSettings, CancellationToken cancellationToken, IProgress<RenderProgress> progress)
{
DirectBitmap Mask = new DirectBitmap(aMask.Width, aMask.Height);
// Copy the input bitmap to the DirectBitmap
aMask.ProcessPixelRows(Mask.Image, (sourceAccessor, destAccessor) =>
{
for (int y = 0; y < sourceAccessor.Height; y++)
{
sourceAccessor.GetRowSpan(y).CopyTo(destAccessor.GetRowSpan(y));
}
});
int i = Math.Max(Mask.Width, Mask.Height);
int R = 1;
while (R < i) R *= 2;
ArtTree = null;
float ThresholdLevel = TheSettings.Threshold * 0.01f;
switch (TheSettings.Mode)
{
case Settings.ArtMode.QuadTree:
{
DateTime rR = DateTime.Now;
ArtTree = new QuadTreeNode() { xstart = -1000, ystart = -1000, xend = R, yend = R };
float hoek = (float)((6.283 * TheSettings.DegreesOff) / 360.0);
int progressInterval = Math.Max(1, Mask.Width / 20); // Report every 5%
progress?.Report(new RenderProgress(0, "Building QuadTree..."));
for (int x = 0; x < Mask.Width; x++)
{
// Check for cancellation at each column
cancellationToken.ThrowIfCancellationRequested();
// Report progress every 5%
if (x % progressInterval == 0)
{
int percent = (x * 100) / Mask.Width;
progress?.Report(new RenderProgress(percent, $"Building QuadTree ({percent}%)..."));
}
for (int y = 0; y < Mask.Height; y++)
{
var C = Mask.GetPixelFast(x, y);
bool doit = false;
if (TheSettings.InvertSource)
{
doit = C.GetBrightness() > ThresholdLevel;
}
else
{
doit = C.GetBrightness() < ThresholdLevel;
}
if (doit)
{
double cx = Math.Cos(hoek) * x + Math.Sin(hoek) * y;
double cy = Math.Sin(hoek) * -x + Math.Cos(hoek) * y;
ArtTree.Insert((int)cx, (int)cy, new SolidQuadTreeItem() { x = (int)cx, y = (int)cy }, TheSettings.MaxSubDiv);
}
}
}
progress?.Report(new RenderProgress(100, "QuadTree complete") { IsComplete = true });
var Elapsed = DateTime.Now - rR;
return (int)Elapsed.TotalMilliseconds;
}
case Settings.ArtMode.Delaunay:
{
DateTime rR = DateTime.Now;
ArtTree = new QuadTreeNode() { xstart = -1000, ystart = -1000, xend = R, yend = R };
float hoek = (float)((6.283 * TheSettings.DegreesOff) / 360.0);
int progressInterval = Math.Max(1, Mask.Width / 20);
progress?.Report(new RenderProgress(0, "Building Delaunay..."));
for (int x = 0; x < Mask.Width; x++)
{
cancellationToken.ThrowIfCancellationRequested();
if (x % progressInterval == 0)
{
int percent = (x * 80) / Mask.Width; // 0-80% for pixel processing
progress?.Report(new RenderProgress(percent, $"Building Delaunay ({percent}%)..."));
}
for (int y = 0; y < Mask.Height; y++)
{
var C = Mask.GetPixelFast(x, y);
bool doit = false;
if (TheSettings.InvertSource)
{
doit = C.GetBrightness() > ThresholdLevel;
}
else
{
doit = C.GetBrightness() < ThresholdLevel;
}
if (doit)
{
double cx = Math.Cos(hoek) * x + Math.Sin(hoek) * y;
double cy = Math.Sin(hoek) * -x + Math.Cos(hoek) * y;
ArtTree.Insert((int)cx, (int)cy, new SolidQuadTreeItem() { x = (int)cx, y = (int)cy }, TheSettings.MaxSubDiv);
}
}
}
cancellationToken.ThrowIfCancellationRequested();
progress?.Report(new RenderProgress(85, "Computing Delaunay triangulation..."));
Delaunay.Build(ArtTree, TheSettings.DegreesOff);
progress?.Report(new RenderProgress(100, "Delaunay complete") { IsComplete = true });
var Elapsed = DateTime.Now - rR;
return (int)Elapsed.TotalMilliseconds;
};
case Settings.ArtMode.Tiling:
{
progress?.Report(new RenderProgress(0, "Creating tiling..."));
TD.Create(TheSettings.TileType);
var P = TD.CreateBaseTriangle(TheSettings.BaseTile, 1000);
var P2 = TD.CreateBaseTriangle(TheSettings.BaseTile, 1000);
P.Rotate(TheSettings.DegreesOff);
P.AlterToFit(Mask.Width, Mask.Height);
P2.Rotate(TheSettings.DegreesOff);
P2.AlterToFit(Mask.Width, Mask.Height);
if (TheSettings.Symmetry)
{
P.ShiftToEdge(Mask.Width / 2, Mask.Height / 2);
P2.ShiftToEdge(Mask.Width / 2, Mask.Height / 2);
P2.Flip(Mask.Width / 2, Mask.Height / 2);
if (TheSettings.SuperSymmetry)
{
P2.MirrorAround(Mask.Width / 2, Mask.Height / 2);
}
}
cancellationToken.ThrowIfCancellationRequested();
progress?.Report(new RenderProgress(10, "Subdividing polygons..."));
DateTime rR = DateTime.Now;
SubDivPoly = TD.SubdivideAdaptive(P, TheSettings.MaxSubDiv, MaskTree, TheSettings.alwayssubdivide, cancellationToken);
if (TheSettings.Symmetry)
{
cancellationToken.ThrowIfCancellationRequested();
progress?.Report(new RenderProgress(30, "Subdividing symmetry..."));
SubDivPoly.AddRange(TD.SubdivideAdaptive(P2, TheSettings.MaxSubDiv, MaskTree, TheSettings.alwayssubdivide, cancellationToken));
}
// Report intermediate polygons for progressive rendering
progress?.Report(new RenderProgress(50, "Subdivision complete") { IntermediatePolygons = SubDivPoly.ToList() });
if (TheSettings.xscalesmallerlevel != 0)
{
float midx = Mask.Width / 2.0f;
float width = Mask.Width;
float offs = TheSettings.xscalecenter * 0.01f * width;
foreach (var A in SubDivPoly)
{
var M = A.Mid();
float scaler = 1.0f - ((float)(M.x - offs) / width) * TheSettings.xscalesmallerlevel * 0.01f;
//scaler = Math.Max(0, Math.Min(1.0f, scaler));
A.ScaleDown(TheSettings.scalingMode, scaler);
}
}
if (TheSettings.scalesmallerfactor != 1.0f)
{
foreach (var A in SubDivPoly)
{
A.ScaleDown(Settings.TriangleScaleMode.Balanced, TheSettings.scalesmallerfactor);
}
}
if (TheSettings.scalesmaller != 0)
{
float scaler = Math.Abs(TheSettings.scalesmaller);
if (TheSettings.scalesmaller > 0)
{
scaler = scaler / 10.0f;
}
else
{
scaler = -scaler / 10.0f;
}
foreach (var A in SubDivPoly)
{
if (A.depth - TheSettings.scalesmallerlevel <= 1)
{
}
else
{
A.ScaleDown(TheSettings.scalingMode, (1 + scaler * (1.0f / (A.depth - TheSettings.scalesmallerlevel))));
}
}
}
if (TheSettings.distanceToMaskScale != 0)
{
float scaler = Math.Abs(TheSettings.distanceToMaskScale);
if (TheSettings.distanceToMaskScale > 0)
{
scaler = scaler / 10.0f;
}
else
{
scaler = -scaler / 10.0f;
}
float aThresholdLevel = TheSettings.Threshold * 0.01f;
if (TheSettings.DistanceMaskFile.Length > 0)
{
Bitmap B = Image.Load<Rgba32>(TheSettings.DistanceMaskFile);
DirectBitmap DMask = new DirectBitmap(aMask.Width, aMask.Height);
// Resize and copy B into DMask
B.Mutate(x => x.Resize(aMask.Width, aMask.Height));
B.ProcessPixelRows(DMask.Image, (sourceAccessor, destAccessor) =>
{
for (int y = 0; y < Math.Min(sourceAccessor.Height, destAccessor.Height); y++)
{
sourceAccessor.GetRowSpan(y).CopyTo(destAccessor.GetRowSpan(y));
}
});
foreach (var A in SubDivPoly)
{
var m = A.Mid();
float sum = GetPixelSum(m, DMask, TheSettings.distanceToMaskRange, aThresholdLevel, TheSettings.InvertSource);
//if (sum > 1) sum = 1;
A.ScaleDown(TheSettings.scalingMode, (scaler * sum));
}
}
else
{
foreach (var A in SubDivPoly)
{
var m = A.Mid();
float sum = GetPixelSum(m, Mask, TheSettings.distanceToMaskRange, aThresholdLevel, TheSettings.InvertSource);
//if (sum > 1) sum = 1;
A.ScaleDown(TheSettings.scalingMode, (scaler * sum));
}
}
}
if (TheSettings.MarcelPlating)
{
List<Tiling.Polygon> MarcelShapes = new List<Tiling.Polygon>();
foreach (var A in SubDivPoly)
{
MarcelShape MS = new MarcelShape();
MarcelShape MS2 = new MarcelShape();
foreach (var v in A.Vertices)
{
MS2.Vertices.Add(new ClipperLib.IntPoint((long)((v.x+1000)*1000), (long)((v.y+1000)*1000)));
}
MS.ShrinkFromShape(MS2.Vertices, TheSettings.Gap /2 + TheSettings.Rounding/2 );
Paths Ps = new Paths();
Ps.AddRange(MS.BuildOutlines(TheSettings.Rounding / 2.0f));
if (TheSettings.BallRadius > 0) Ps.AddRange(MS.BuildHoles(TheSettings.BallRadius));
foreach(var p in Ps)
{
Tiling.Polygon Poly = new Tiling.Polygon();
Poly.Vertices.AddRange(from a in p select new vec2((a.X) * 0.001f-1000, (a.Y ) * 0.001f-1000));
MarcelShapes.Add(Poly);
}
}
SubDivPoly.Clear();
SubDivPoly = MarcelShapes;
}
var Elapsed = DateTime.Now - rR;
return (int)Elapsed.TotalMilliseconds;
}
};
return 0;
}
private float GetPixelSum(vec2 m, DirectBitmap mask, float distanceToMaskRange, float ThresholdLevel, bool invert)
{
float sum = 0;
if (distanceToMaskRange == 0) distanceToMaskRange = 0.001f;
float wrange = distanceToMaskRange * mask.Width * 0.5f;
float hrange = distanceToMaskRange * mask.Width * 0.5f;
float total = 0;
float[] cp = new float[40];
float[] sp = new float[40];
for (int p = 0; p < 40; p++)
{
double P = (p * Math.PI * 2) / 40.0f;
sp[p] = (float)Math.Sin(P) * wrange;
cp[p] = (float)Math.Cos(P) * wrange ;
}
for (int ring = 1;ring<10;ring++)
{
float RW = ring / 10.0f;
for (int p = 0; p < 40; p++)
{
int x = (int)(cp[p] * RW + m.x);
int y = (int)(sp[p] * RW + m.y);
total++;
if (x >= 0 && x < mask.Width)
{
if (y >= 0 && y < mask.Height)
{
var C = mask.GetPixelFast(x, y);
float br = C.GetBrightness();
bool doit = false;
if (invert)
{
doit = br > ThresholdLevel;
}
else
{
doit = C.GetBrightness() < ThresholdLevel;
}
if (doit)
{
sum++;
}
}
}
}
}
return 1000*sum / total;
}
}
}