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 GerberLibrary.Core.Algorithms;
using GerberLibrary.Core.Primitives;
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static GerberLibrary.PolyLineSet;
using Pen = GerberLibrary.Core.Primitives.Pen;
using SolidBrush = GerberLibrary.Core.Primitives.SolidBrush;
namespace GerberLibrary.Core
{
public class Line
{
public float x1;
public float y1;
public float x2;
public float y2;
public void Draw(GraphicsInterface g, Color C, float W = 1.0f)
{
g.DrawLine(new Pen(C, W), x1, y1, x2, y2);
}
public static float SumLengths(List<Line> outline)
{
return outline.Sum(x => x.Length());
}
private float Length()
{
float dx = x2 - x1;
float dy = y2 - y1;
return (float)Math.Sqrt(dx * dx + dy * dy);
}
}
public static class Helpers
{
/*
[Obsolete("This method uses Windows Forms PictureBox which is not cross-platform. Use alternative rendering approach.")]
public static float SetupMatrixForExtends(Graphics G, object P, Bounds Bb, int pixelmargin = 0, float percentmargin = 0)
{
// This method is Windows Forms specific and not used in cross-platform scenarios
throw new NotImplementedException("SetupMatrixForExtends requires Windows Forms PictureBox which is not cross-platform");
}
*/
public static Color RefractionNormalledMaxBrightnessAndSat(float Inp)
{
float adjusted = (Inp * 0.25f) + 0.3f;
var C = Refraction(adjusted);
double h, s, v;
ColorToHSV(C, out h, out s, out v);
s = 1;
v = 1;
return ColorFromHSV(h, s, v);
}
public static void ColorToHSV(Color color, out double hue, out double saturation, out double value)
{
Rgba32 c = color.ToPixel<Rgba32>();
int max = Math.Max(c.R, Math.Max(c.G, c.B));
int min = Math.Min(c.R, Math.Min(c.G, c.B));
// hue = color.GetHue(); // System.Drawing specific
// Manual Hue calculation:
float r = c.R;
float g = c.G;
float b = c.B;
float delta = max - min;
float h = 0;
if (delta == 0) h = 0;
else if (max == r) h = 60 * (((g - b) / delta) % 6);
else if (max == g) h = 60 * (((b - r) / delta) + 2);
else h = 60 * (((r - g) / delta) + 4);
if (h < 0) h += 360;
hue = h;
saturation = (max == 0) ? 0 : 1d - (1d * min / max);
value = max / 255d;
}
public static Color ColorFromHSV(double hue, double saturation, double value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0)
return Color.FromRgba((byte)v, (byte)t, (byte)p, 255);
else if (hi == 1)
return Color.FromRgba((byte)q, (byte)v, (byte)p, 255);
else if (hi == 2)
return Color.FromRgba((byte)p, (byte)v, (byte)t, 255);
else if (hi == 3)
return Color.FromRgba((byte)p, (byte)q, (byte)v, 255);
else if (hi == 4)
return Color.FromRgba((byte)t, (byte)p, (byte)v, 255);
else
return Color.FromRgba((byte)v, (byte)p, (byte)q, 255);
}
public static PathDefWithClosed Sanitize(PathDefWithClosed inp)
{
PathDefWithClosed R = new PathDefWithClosed();
R.Width = inp.Width;
R.Closed = inp.Closed;
PointD last = new PointD(double.NaN, double.NaN);
foreach (var v in inp.Vertices)
{
if (v != last)
{
R.Vertices.Add(v);
last = v.Copy();
}
}
return R;
}
public static List<(double res1, double res2, double scalar)> GetFitForRatio(double scalar, List<double> ValueSet)
{
Dictionary<(double res1, double res2), double> ResistorResults = new Dictionary<(double res1, double res2), double>();
for (int i2 = 0; i2 < ResistorRanges.Count; i2++)
{
for (int j2 = i2 + 1; j2 < ResistorRanges.Count; j2++)
{
for (int i = 0; i < ValueSet.Count; i++)
{
for (int j = 0; j < ValueSet.Count; j++)
{
double R1 = ResistorRanges[i2] * ValueSet[i];
double R2 = ResistorRanges[j2] * ValueSet[j];
ResistorResults.Add((res1: R1, res2: R2), Math.Abs(R1 / R2 - scalar));
}
}
}
}
var items = (from i in ResistorResults orderby i.Value select i.Key).ToList().Take(10);
List<(double res1, double res2, double scalar)> Res = new List<(double res1, double res2, double scalar)>();
foreach (var i in items)
{
Res.Add((res1: i.res1, res2: i.res2, i.res2 / i.res1));
}
return Res;
}
public static List<(double res1, double res2, double scalar)> GetFitForRatioAndSecond(double scalar, List<double> ValueSet, double second)
{
Dictionary<(double res1, double res2), double> ResistorResults = new Dictionary<(double res1, double res2), double>();
for (int i2 = 0; i2 < ResistorRanges.Count; i2++)
{
for (int i = 0; i < ValueSet.Count; i++)
{
double R1 = ResistorRanges[i2] * ValueSet[i];
double R2 = second;
ResistorResults.Add((res1: R1, res2: R2), Math.Abs(R1 / R2 - scalar));
}
}
var items = (from i in ResistorResults orderby i.Value select i.Key).ToList().Take(10);
List<(double res1, double res2, double scalar)> Res = new List<(double res1, double res2, double scalar)>();
foreach (var i in items)
{
Res.Add((res1: i.res1, res2: i.res2, i.res2 / i.res1));
}
return Res;
}
public static List<(double res1, double res2, double scalar)> GetE96FitForRatio(double scalar)
{
return GetFitForRatio(scalar, E96);
}
public static List<(double res1, double res2, double scalar)> GetE24FitForRatio(double scalar)
{
return GetFitForRatio(scalar, E24);
}
public static List<(double res1, double res2, double scalar)> GetE96FitForRatioAndSecond(double scalar, double second)
{
return GetFitForRatioAndSecond(scalar, E96, second);
}
public static List<(double res1, double res2, double scalar)> GetE24FitForRatioAndSecond(double scalar, double second)
{
return GetFitForRatioAndSecond(scalar, E24, second);
}
public static List<double> E12 = new List<double>() { 1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2 };
public static List<double> E24 = new List<double>() { 1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1 };
public static List<double> E48 = new List<double>() { 1.00, 1.05, 1.10, 1.15, 1.21, 1.27, 1.33, 1.40, 1.47, 1.54, 1.62, 1.69, 1.78, 1.87, 1.96, 2.05, 2.15, 2.26, 2.37, 2.49, 2.61, 2.74, 2.87, 3.01, 3.16, 3.32, 3.48, 3.65, 3.83, 4.02, 4.22, 4.42, 4.64, 4.87, 5.11, 5.36, 5.62, 5.90, 6.19, 6.49, 6.81, 7.15, 7.50, 7.87, 8.25, 8.66, 9.09, 9.53 };
public static List<double> E96 = new List<double>() { 1.00, 1.02, 1.05, 1.07, 1.10, 1.13, 1.15, 1.18, 1.21, 1.24, 1.27, 1.30, 1.33, 1.37, 1.40, 1.43, 1.47, 1.50, 1.54, 1.58, 1.62, 1.65, 1.69, 1.74, 1.78, 1.82, 1.87, 1.91, 1.96, 2.00, 2.05, 2.10, 2.15, 2.21, 2.26, 2.32, 2.37, 2.43, 2.49, 2.55, 2.61, 2.67, 2.74, 2.80, 2.87, 2.94, 3.01, 3.09, 3.16, 3.24, 3.32, 3.40, 3.48, 3.57, 3.65, 3.74, 3.83, 3.92, 4.02, 4.12, 4.22, 4.32, 4.42, 4.53, 4.64, 4.75, 4.87, 4.99, 5.11, 5.23, 5.36, 5.49, 5.62, 5.76, 5.90, 6.04, 6.19, 6.34, 6.49, 6.65, 6.81, 6.98, 7.15, 7.32, 7.50, 7.68, 7.87, 8.06, 8.25, 8.45, 8.66, 8.87, 9.09, 9.31, 9.53, 9.76 };
public static List<double> ResistorRanges = new List<double>() { 1, 10, 100, 1000, 10000, 100000, 1000000 };
public static List<double> CapacitorRanges = new List<double>() { 0.000000000001, 0.00000000001, 0.0000000001, 0.000000001, 0.00000001, 0.0000001, 0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1 };
public static string MakeNiceUnitString(double V, Units U)
{
string UnitName = Enum.GetName(typeof(Units), U);
if (U == GerberLibrary.Core.Units.None) UnitName = "";
if (V == 0) return "0" + UnitName;
int M = 0;
int M2 = 0;
double V2 = Math.Abs(V);
double V3 = V2;
while (V2 >= 10)
{
V2 /= 10;
M++;
if (M % 3 == 0) { M2++; V3 /= 1000.0f; };
}
if (V2 < 1)
{
while (V3 < 1)
{
V2 *= 10;
M--;
if (M % 3 == 0) { M2--; V3 *= 1000.0f; };
}
}
M = M2;
List<string> Units = new List<string>() { "p", "n", "u", "m", "", "k", "M", "G", "T" };
M += 4;
if (M >= 0 && M < Units.Count)
{
UnitName = Units[M] + UnitName;
}
return V3.ToString().Replace(',', '.') + " " + UnitName;
}
public static void Transform(double dx, double dy, double cx, double cy, double angle, ref double X, ref double Y)
{
GerberNumberFormat GNF = new GerberNumberFormat();
double na = angle * (Math.PI * 2.0) / 360.0; ;
double SA = Math.Sin(na);
double CA = Math.Cos(na);
GNF.Multiplier = 1;
GerberTransposer.GetTransformedCoord(dx, dy, cx, cy, angle, CA, SA, GNF, true, ref X, ref Y);
double adx = dx;
double ady = dy;
}
public static Line TransFormLine(Line v, double dx, double dy, float cx, float cy, double angle)
{
double x1 = v.x1;
double x2 = v.x2;
double y1 = v.y1;
double y2 = v.y2;
Transform(dx, dy, cx, cy, angle, ref x1, ref y1);
Transform(dx, dy, cx, cy, angle, ref x2, ref y2);
return new Line() { x1 = (float)x1, y1 = (float)y1, x2 = (float)x2, y2 = (float)y2 };
}
public static List<PathDefWithClosed> LineSegmentsToPolygons(ProgressLog log, List<PathDefWithClosed> input, bool joinclosest = true)
{
List<PathDefWithClosed> LeftoverLines = new List<PathDefWithClosed>();
if (input.Count == 0)
{
return LeftoverLines;
}
log.PushActivity("LineSegmentsToPolygons");
// return input;
List<PathDefWithClosed> Paths = new List<PathDefWithClosed>();
List<PathDefWithClosed> FirstSweep = new List<PathDefWithClosed>();
try
{
foreach (var p in input)
{
if (p.Vertices.Count() > 0)
{
FirstSweep.Add(Sanitize(p));
}
}
FirstSweep = StripOverlaps(log, FirstSweep);
LeftoverLines.Add(FirstSweep[0]);
for (int i = 1; i < FirstSweep.Count; i++)
{
var LastLeft = LeftoverLines.Last();
var LastLeftP = LastLeft.Vertices.Last();
if (FirstSweep[i].Vertices.First() == LastLeftP)
{
LastLeft.Vertices.AddRange(FirstSweep[i].Vertices.Skip(1));
}
else
{
if (FirstSweep[i].Vertices.Last() == LastLeftP)
{
FirstSweep[i].Vertices.Reverse();
LastLeft.Vertices.AddRange(FirstSweep[i].Vertices.Skip(1));
}
else
{
LeftoverLines.Add(FirstSweep[i]);
}
}
}
LeftoverLines = StripOverlaps(log, LeftoverLines);
}
catch (Exception E)
{
log.AddString(String.Format(E.Message));
}
while (LeftoverLines.Count > 0)
{
bool added = false;
var a = LeftoverLines.Last();
LeftoverLines.Remove(a);
if (added == false)
{
PathDefWithClosed P = new PathDefWithClosed();
P.Width = a.Width;
foreach (var v in a.Vertices)
{
P.Vertices.Add(v);
}
//P.Points.Add(a.Last());
if (a.Vertices.First() == a.Vertices.Last())
{
int matching = 0;
for (int i = 0; i < a.Vertices.Count / 2; i++)
{
if (a.Vertices[i] == a.Vertices[a.Vertices.Count - 1 - i]) matching++;
}
if (matching > 1)
{
P.Vertices.Clear();
List<PointD> DebugN = new List<PointD>();
List<int> Kinks = new List<int>();
List<int> KinkEnd = new List<int>();
Kinks.Add(0);
for (int i = 1; i < a.Vertices.Count; i++)
{
var N1 = MathHelpers.Normal(a.Vertices[(i - 1)], a.Vertices[i]);
var N2 = MathHelpers.Normal(a.Vertices[i], a.Vertices[(i + 1) % a.Vertices.Count()]);
if (N1.Dot(N2) < 0.8)
{
DebugN.Add(a.Vertices[i]);
DebugN.Add(N1 * 4.0 + a.Vertices[i]);
Kinks.Add(i);
KinkEnd.Add(i);
}
}
KinkEnd.Add(a.Vertices.Count - 1);
double maxD = 0;
int maxSeg = 0;
for (int i = 0; i < Kinks.Count; i++)
{
double D = 0;
for (int j = Kinks[i]; j < KinkEnd[i]; j++)
{
D += (a.Vertices[j] - a.Vertices[j + 1]).Length();
}
if (D > maxD)
{
maxD = D;
maxSeg = i;
}
}
for (int i = Kinks[maxSeg]; i < KinkEnd[maxSeg]; i++)
{
P.Vertices.Add(a.Vertices[i]);
}
bool dumpdebugline = false;
if (dumpdebugline)
{
/*
Bounds Bbox = new Bounds();
foreach (var v in a.Vertices)
{
Bbox.FitPoint(v);
Bbox.FitPoint(new PointD(v.X + 10, v.Y + 10));
Bbox.FitPoint(new PointD(v.X - 10, v.Y - 10));
}
float Scaler = 50.0f;
// Bitmap OutB = new Bitmap((int)(Bbox.Width() * Scaler), (int)(Bbox.Height() * Scaler));
// Graphics G = Graphics.FromImage(OutB);
// G.Clear(Color.White);
// GraphicsGraphicsInterface GGI = new GraphicsGraphicsInterface(G);
// GGI.TranslateTransform((float)-Bbox.TopLeft.X * Scaler, (float)-Bbox.TopLeft.Y * Scaler);
// GGI.DrawLines(new Pen(Color.Red, 0.1f), (from i in a.Vertices select (i * Scaler).ToF()).ToArray());
for (int i = 1; i < a.Vertices.Count; i++)
{
var N = MathHelpers.Normal(a.Vertices[i - 1], a.Vertices[i]);
// GGI.DrawString(i.ToString(), new Font("arial", 14), new SolidBrush(Color.FromArgb((i + 128) % 256, i % 256, i % 256)), (float)a.Vertices[i].X * Scaler + (float)N.X * 20.0f, (float)a.Vertices[i].Y * Scaler + (float)N.Y * 20.0f, new StringFormat());
}
for (int i = 0; i < DebugN.Count; i += 2)
{
// GGI.DrawLine(new Pen(Color.DarkGreen, 1.0f), (DebugN[i] * Scaler).ToF(), (DebugN[i + 1] * Scaler).ToF());
}
// OutB.Save("testout.png");
*/
}
}
else
{
a.Vertices.Remove(a.Vertices.Last());
//Console.WriteLine("closed path with {0} points during stage 2: {1} reversematched", a.Vertices.Count(), matching);
P.Closed = true;
}
}
Paths.Add(P);
}
}
Paths = StripOverlaps(log, Paths);
// return Paths;
int Merges = 1;
int startat = 0;
int lasthigh = 0;
while (Merges > 0)
{
startat = lasthigh;
Merges = FindNextMerge(log, Paths, out lasthigh, startat);
}
//return Paths;
int ClosedCount = (from i in Paths where i.Closed == true select i).Count();
if (ClosedCount < Paths.Count)
{
//Console.WriteLine("{0}/{1} paths open - finding closest connections", Paths.Count - ClosedCount, Paths.Count);
if (joinclosest)
{
Merges = 1;
while (Merges > 0)
{
Merges = FindNextClosest(Paths);
}
int NewClosedCount = (from i in Paths where i.Closed == true select i).Count();
log.AddString(String.Format("remaining open: {0}", NewClosedCount));
/*
var OpenPaths = (from i in Paths where i.Closed == false select i).ToArray();
// foreach (var p in OpenPaths)
// {
// Paths.Remove(p);
// }
while (OpenPaths.Count() > 1)
{
var P = OpenPaths[0];
var V = P.Points.Last();
double ClosestDistance = 1000000;
int closestindex = 0;
bool forward = true;
double OwnDistance = (P.Points.First() - P.Points.Last()).Length();
ClosestDistance = OwnDistance;
for (int i = 1; i < OpenPaths.Count(); i++)
{
double dx1 = OpenPaths[i].Points.First().X - V.X;
double dy1 = OpenPaths[i].Points.First().Y - V.Y;
double dx2 = OpenPaths[i].Points.Last().X - V.X;
double dy2 = OpenPaths[i].Points.Last().Y - V.Y;
float disttofirst = (float)Math.Sqrt(dx1 * dx1 + dy1 * dy1);
float disttolast = (float)Math.Sqrt(dx2 * dx2 + dy2 * dy2);
if (disttofirst < ClosestDistance)
{
forward = true;
closestindex = i;
ClosestDistance = disttofirst;
}
if (disttolast < ClosestDistance)
{
forward = false;
closestindex = i;
ClosestDistance = disttolast;
}
}
if (OwnDistance == ClosestDistance)
{
P.Closed = true;
// Paths.Add(P);
}
else
{
if (forward)
{
var PJ = OpenPaths[closestindex];
P.Points.AddRange(PJ.Points);
Paths.Remove(PJ);
}
else
{
var PJ = OpenPaths[closestindex];
PJ.Points.Reverse();
P.Points.AddRange(PJ.Points);
Paths.Remove(PJ);
}
if (P.Points.First() == P.Points.Last())
{
P.Closed = true;
}
}
OpenPaths = (from i in Paths where i.Closed == false select i).ToArray();
}*/
}
}
List<PathDefWithClosed> Results = new List<PathDefWithClosed>();
foreach (var p in Paths)
{
Results.Add(Sanitize(p));
}
log.PopActivity();
return Results;
}
private static List<PathDefWithClosed> StripOverlaps(ProgressLog log, List<PathDefWithClosed> Paths)
{
List<PathDefWithClosed> Res = new List<PathDefWithClosed>();
QuadTreeNode Root = new QuadTreeNode();
Bounds B = new Bounds();
for (int i = 0; i < Paths.Count; i++)
{
if (Paths[i].Closed == false)
{
foreach (var a in Paths[i].Vertices)
{
B.FitPoint(a);
}
}
else
{
Res.Add(Paths[i]);
}
}
Root.xstart = B.TopLeft.X - 10;
Root.xend = B.BottomRight.X + 10;
Root.ystart = B.TopLeft.Y - 10;
Root.yend = B.BottomRight.Y + 10;
RectangleF QueryRect = new RectangleF();
QueryRect.Width = 3;
QueryRect.Height = 3;
List<PathDefWithClosed> ToDelete = new List<PathDefWithClosed>();
for (int i = 0; i < Paths.Count; i++)
{
if (Paths[i].Closed == false)
{
int nearcount = 0;
foreach (var a in Paths[i].Vertices)
{
QueryRect.X = (float)a.X - 1.5f;
QueryRect.Y = (float)a.Y - 1.5f;
Root.CallBackInside(QueryRect, delegate (QuadTreeItem QI)
{
var S = QI as SegmentEndContainer;
if (S.Point == a)
{
nearcount++;
}
else
{
if (PointD.Distance(a, S.Point) < 0.001)
{
nearcount++;
}
}
return true;
}
);
}
int max = Math.Max(4, (Paths[i].Vertices.Count * 50) / 100);
if (nearcount <= max)
{
foreach (var a in Paths[i].Vertices)
{
Root.Insert(new SegmentEndContainer() { PathID = i, Point = a, Side = SideEnum.Start }, 8);
}
Res.Add(Paths[i]);
}
else
{
Res.Add(Paths[i]);
log.AddString(String.Format("{4}: {0} out of {1}/{2}/{3}", nearcount, max, Paths[i].Vertices.Count, (Paths[i].Vertices.Count * 90) / 100, i));
log.AddString(String.Format("{0}: skipped!", i));
}
}
}
return Res;
}
enum SideEnum
{
Start,
End
}
class SegmentEndContainer : QuadTreeItem
{
public PointD Point;
public SideEnum Side;
public int PathID;
public double x
{
get
{
return Point.X;
}
}
public double y
{
get
{
return Point.Y;
}
}
}
private static int FindNextMerge(ProgressLog log, List<PathDefWithClosed> Paths, out int highestnomatch, int startat = 0)
{
highestnomatch = 0;
QuadTreeNode Root = new QuadTreeNode();
Bounds B = new Bounds();
for (int i = startat; i < Paths.Count; i++)
{
if (Paths[i].Closed == false)
{
B.FitPoint(Paths[i].Vertices.First());
B.FitPoint(Paths[i].Vertices.Last());
}
}
Root.xstart = B.TopLeft.X - 10;
Root.xend = B.BottomRight.X + 10;
Root.ystart = B.TopLeft.Y - 10;
Root.yend = B.BottomRight.Y + 10;
for (int i = startat; i < Paths.Count; i++)
{
if (Paths[i].Closed == false)
{
Root.Insert(new SegmentEndContainer() { PathID = i, Point = Paths[i].Vertices.First(), Side = SideEnum.Start }, 4);
Root.Insert(new SegmentEndContainer() { PathID = i, Point = Paths[i].Vertices.Last(), Side = SideEnum.End }, 4);
}
}
RectangleF R = new RectangleF();
R.Width = 10;
R.Height = 10;
for (int i = startat; i < Paths.Count; i++)
{
// Console.WriteLine("checking path {0}", i);
var P = Paths[i];
if (P.Closed == false)
{
var PF = P.Vertices.First();
// Console.WriteLine("checking firstvert {0}", PF);
R.X = (float)(P.Vertices.First().X - 5);
R.Y = (float)(P.Vertices.First().Y - 5);
int startmatch = -1;
int endmatch = -1;
Root.CallBackInside(R, delegate (QuadTreeItem QI)
{
var S = QI as SegmentEndContainer;
if (S.PathID == i) return true;
// Console.WriteLine(" against {0}", S.Point);
if (S.Point == PF)
{
if (S.Side == SideEnum.Start)
{
startmatch = S.PathID;
// Console.WriteLine(" matched start {0}" , startmatch);
}
else
{
endmatch = S.PathID;
// Console.WriteLine(" matched end {0}", endmatch);
}
}
return true;
});
if (startmatch > -1 || endmatch > -1)
{
if (endmatch > -1)
{
Paths[endmatch].Vertices.Remove(Paths[endmatch].Vertices.Last());
Paths[endmatch].Vertices.AddRange(Paths[i].Vertices);
if (Paths[endmatch].Vertices.First() == Paths[endmatch].Vertices.Last())
{
log.AddString(String.Format("closed path with {0} points during stage 3a", Paths[endmatch].Vertices.Count()));
Paths[endmatch].Closed = true;
}
Paths.Remove(Paths[i]);
//Console.WriteLine(" a");
return 1;
}
if (startmatch > -1)
{
Paths[i].Vertices.Reverse();
Paths[i].Vertices.Remove(Paths[i].Vertices.Last());
Paths[i].Vertices.AddRange(Paths[startmatch].Vertices);
if (Paths[i].Vertices.First() == Paths[i].Vertices.Last())
{
log.AddString(String.Format("closed path with {0} points during stage 3b", Paths[i].Vertices.Count()));
Paths[i].Closed = true;
}
Paths.Remove(Paths[startmatch]);
// Console.WriteLine(" b");
return 1;
}
}
PF = P.Vertices.Last();
// Console.WriteLine("checking lastvert {0}", PF);
R.X = (float)(P.Vertices.First().X - 5);
R.Y = (float)(P.Vertices.First().Y - 5);
startmatch = -1;
endmatch = -1;
Root.CallBackInside(R, delegate (QuadTreeItem QI)
{
var S = QI as SegmentEndContainer;
if (S.PathID == i) return true;
// Console.WriteLine(" against {0}", S.Point);
if (S.Point == PF)
{
if (S.Side == SideEnum.Start)
{
startmatch = S.PathID;
}
else
{
endmatch = S.PathID;
}
}
return true;
});
if (startmatch > -1 || endmatch > -1)
{
if (endmatch > -1)
{
Paths[i].Vertices.Reverse();
Paths[endmatch].Vertices.Remove(Paths[endmatch].Vertices.Last());
Paths[endmatch].Vertices.AddRange(Paths[i].Vertices);
if (Paths[endmatch].Vertices.First() == Paths[endmatch].Vertices.Last())
{
Console.WriteLine("closed path with {0} points during stage 3c", Paths[endmatch].Vertices.Count());
Paths[endmatch].Closed = true;
}
Paths.Remove(Paths[i]);
//Console.WriteLine(" c");
return 1;
}
if (startmatch > -1)
{
Paths[i].Vertices.Remove(Paths[i].Vertices.Last());
Paths[i].Vertices.AddRange(Paths[startmatch].Vertices);
if (Paths[i].Vertices.First() == Paths[i].Vertices.Last())
{
Console.WriteLine("closed path with {0} points during stage 3d", Paths[i].Vertices.Count());
Paths[i].Closed = true;
}
Paths.Remove(Paths[startmatch]);
//Console.WriteLine(" d");
return 1;
}
}
highestnomatch = i;
}
}
return 0;
//for (int i = 0; i < Paths.Count; i++)
//{
// for (int j = i + 1; j < Paths.Count; j++)
// {
// if (Paths[i].Closed == false && Paths[j].Closed == false)
// {
// if (Paths[j].Vertices.First() == Paths[i].Vertices.Last())
// {
// Paths[i].Vertices.Remove(Paths[i].Vertices.Last());
// Paths[i].Vertices.AddRange(Paths[j].Vertices);
// if (Paths[i].Vertices.First() == Paths[i].Vertices.Last()) Paths[i].Closed = true;
// Paths.Remove(Paths[j]);
// return 1;
// }
// else
// {
// if (Paths[i].Vertices.First() == Paths[j].Vertices.Last())
// {
// Paths[j].Vertices.Remove(Paths[j].Vertices.Last());
// Paths[j].Vertices.AddRange(Paths[i].Vertices);
// if (Paths[j].Vertices.First() == Paths[j].Vertices.Last()) Paths[j].Closed = true;
// Paths.Remove(Paths[i]);
// return 1;
// }
// else
// {
// if (Paths[i].Vertices.First() == Paths[j].Vertices.First())
// {
// Paths[i].Vertices.Reverse();
// Paths[i].Vertices.Remove(Paths[i].Vertices.Last());
// Paths[i].Vertices.AddRange(Paths[j].Vertices);
// if (Paths[i].Vertices.First() == Paths[i].Vertices.Last()) Paths[i].Closed = true;
// Paths.Remove(Paths[j]);
// return 1;
// }
// else
// if (Paths[i].Vertices.Last() == Paths[j].Vertices.Last())
// {
// Paths[i].Vertices.Reverse();
// Paths[j].Vertices.Remove(Paths[j].Vertices.Last());
// Paths[j].Vertices.AddRange(Paths[i].Vertices);
// if (Paths[j].Vertices.First() == Paths[j].Vertices.Last()) Paths[j].Closed = true;
// Paths.Remove(Paths[i]);
// return 1;
// }
// }
// }
// }
// }
//}
//return 0;
}
private static int FindNextClosest(List<PathDefWithClosed> Paths)
{
QuadTreeNode Root = new QuadTreeNode();
Bounds B = new Bounds();
for (int i = 0; i < Paths.Count; i++)
{
if (Paths[i].Closed == false)
{
B.FitPoint(Paths[i].Vertices.First());
B.FitPoint(Paths[i].Vertices.Last());
}
}
Root.xstart = B.TopLeft.X - 10;
Root.xend = B.BottomRight.X + 10;
Root.ystart = B.TopLeft.Y - 10;
Root.yend = B.BottomRight.Y + 10;
for (int i = 0; i < Paths.Count; i++)
{
if (Paths[i].Closed == false)
{
Root.Insert(new SegmentEndContainer() { PathID = i, Point = Paths[i].Vertices.First(), Side = SideEnum.Start }, 5);
Root.Insert(new SegmentEndContainer() { PathID = i, Point = Paths[i].Vertices.Last(), Side = SideEnum.End }, 5);
}
}
RectangleF R = new RectangleF();
R.Width = 3;
R.Height = 3;
for (int i = 0; i < Paths.Count; i++)
{
var P = Paths[i];
if (P.Closed == false && P.Vertices.Count > 1)
{
var PF = P.Vertices[0];
var PF2 = P.Vertices[1];
var PathDir = PF - PF2;// MathHelpers.Difference(PF, PF2);
PathDir.Normalize();
R.X = (float)(P.Vertices.First().X - 1.5);
R.Y = (float)(P.Vertices.First().Y - 1.5);
int startmatch = -1;
int endmatch = -1;
double closestdistance = B.Width() + B.Height();
Root.CallBackInside(R, delegate (QuadTreeItem QI)
{
var S = QI as SegmentEndContainer;
if (S.PathID == i) return true;
if (P.Width != Paths[S.PathID].Width) return true;
PointD Dir2;
if (S.Side == SideEnum.Start)
{
if (Paths[S.PathID].Vertices.Count > 1)
{
var S2 = Paths[S.PathID].Vertices[1];
Dir2 = S2 - S.Point;
Dir2.Normalize();
}
else
{
var S2 = Paths[S.PathID].Vertices[0];
Dir2 = S2 - S.Point;
Dir2.Normalize();
}
}
else
{
int idx = Paths[S.PathID].Vertices.Count() - 2;
while (idx < 0) idx++;
var S2 = Paths[S.PathID].Vertices[idx];
Dir2 = S2 - S.Point;
Dir2.Normalize();
}
double dotted = Dir2.Dot(PathDir);
var D = PointD.Distance(S.Point, PF);
if (D < 1.0)
{
// D -= dotted * 3.0;
if (D < closestdistance)
{
closestdistance = D;
if (S.Side == SideEnum.Start)
{
startmatch = S.PathID;
}
else
{
endmatch = S.PathID;
}
}
}
return true;
});
if (startmatch > -1 || endmatch > -1)
{
if (endmatch > -1)
{
if (closestdistance > 0) Paths[endmatch].Vertices.Remove(Paths[endmatch].Vertices.Last());
Paths[endmatch].Vertices.AddRange(Paths[i].Vertices);
if (Paths[endmatch].Vertices.First() == Paths[endmatch].Vertices.Last())
{
Console.WriteLine("closed path with {0} points during stage 4a", Paths[endmatch].Vertices.Count());
Paths[endmatch].Closed = true;
}
Paths.Remove(Paths[i]);
// Console.WriteLine(" 4a");
return 1;
}
if (startmatch > -1)
{
Paths[i].Vertices.Reverse();
if (closestdistance > 0) Paths[i].Vertices.Remove(Paths[i].Vertices.Last());
Paths[i].Vertices.AddRange(Paths[startmatch].Vertices);
if (Paths[i].Vertices.First() == Paths[i].Vertices.Last())
{
Console.WriteLine("closed path with {0} points during stage 4b", Paths[i].Vertices.Count());
Paths[i].Closed = true;
}
Paths.Remove(Paths[startmatch]);
//Console.WriteLine(" 4b");
return 1;
}
}
PF = P.Vertices.Last();
R.X = (float)(P.Vertices.First().X - 1.5);
R.Y = (float)(P.Vertices.First().Y - 1.5);
startmatch = -1;
endmatch = -1;
closestdistance = B.Width() + B.Height();
Root.CallBackInside(R, delegate (QuadTreeItem QI)
{
var S = QI as SegmentEndContainer;
if (S.PathID == i) return true;
if (P.Width != Paths[S.PathID].Width) return true;
var D = PointD.Distance(S.Point, PF);
if (D < 1.0 && D < closestdistance)
{
closestdistance = D;
if (S.Side == SideEnum.Start)
{
startmatch = S.PathID;
}
else
{
endmatch = S.PathID;
}
}
return true;
});
if (startmatch > -1 || endmatch > -1)
{
if (endmatch > -1)
{
Paths[i].Vertices.Reverse();
if (closestdistance > 0) Paths[endmatch].Vertices.Remove(Paths[endmatch].Vertices.Last());
Paths[endmatch].Vertices.AddRange(Paths[i].Vertices);
if (Paths[endmatch].Vertices.First() == Paths[endmatch].Vertices.Last())
{
Console.WriteLine("closed path with {0} points during stage 4c", Paths[endmatch].Vertices.Count());
Paths[endmatch].Closed = true;
}
Paths.Remove(Paths[i]);
// Console.WriteLine(" 4c");
return 1;
}
if (startmatch > -1)
{
if (closestdistance > 0) Paths[i].Vertices.Remove(Paths[i].Vertices.Last());
Paths[i].Vertices.AddRange(Paths[startmatch].Vertices);
if (Paths[i].Vertices.First() == Paths[i].Vertices.Last())
{
Console.WriteLine("closed path with {0} points during stage 4d", Paths[i].Vertices.Count());
Paths[i].Closed = true;
}
Paths.Remove(Paths[startmatch]);
// Console.WriteLine(" 4d");
return 1;
}
}
}
}
return 0;
}
public static double PolygonSurfaceArea(List<PointD> Polygon)
{
List<PointD> P = new List<PointD>();
foreach (var p in Polygon) P.Add(p);
double area = 0;
if (Polygon[0] != Polygon[Polygon.Count - 1]) P.Add(Polygon[0]);
for (int i = 1; i < P.Count - 1; ++i)
area += P[i].X * (P[i + 1].Y - P[i - 1].Y);
area /= 2;
return area;
}
public static double DistanceSq(PointD A, PointD B)
{
double dx = B.X - A.X;
double dy = B.Y - A.Y;
return dx * dx + dy * dy;
}
public static PointD SegmentSegmentIntersect(PointD P0, PointD P1, PointD P2, PointD P3)
{
PointD S1 = P1 - P0;
PointD S2 = P3 - P2;
double s, t;
s = (-S1.Y * (P0.X - P2.X) + S1.X * (P0.Y - P2.Y)) / (-S2.X * S1.Y + S1.X * S2.Y);
t = (S2.X * (P0.Y - P2.Y) - S2.Y * (P0.X - P2.X)) / (-S2.X * S1.Y + S1.X * S2.Y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
double X = P0.X + (t * S1.X); ;
double Y = P0.Y + (t * S1.Y);
return new PointD(X, Y);
}
return null; // No collision
}
public static double HermiteInterpolate(double y0, double y1, double y2, double y3, double mu)
{
var mu2 = mu * mu;
var a0 = -0.5f * y0 + 1.5f * y1 - 1.5f * y2 + 0.5f * y3;
var a1 = y0 - 2.5f * y1 + 2f * y2 - 0.5f * y3;
var a2 = -0.5f * y0 + 0.5f * y2;
var a3 = y1;
return (a0 * mu * mu2) + (a1 * mu2) + (a2 * mu) + a3;
}
public static PointD HermitePoint(PointD A, PointD B, PointD C, PointD D, double mu)
{
PointD R = new PointD();
R.X = HermiteInterpolate(A.X, B.X, C.X, D.X, mu);
R.Y = HermiteInterpolate(A.Y, B.Y, C.Y, D.Y, mu);
return R;
}
public static PointD BezierPoint(PointD p1, PointD p2, PointD p3, PointD p4, double t)
{
PointD R = new PointD();
double t1 = 1 - t;
double t1_3 = t1 * t1 * t1;
double t1_3a = (3 * t) * (t1 * t1);
double t1_3b = (3 * (t * t)) * t1;
double t1_3c = (t * t * t);
double x = t1_3 * p1.X;
x = x + t1_3a * p2.X;
x = x + t1_3b * p3.X;
x = x + t1_3c * p4.X;
double y = t1_3 * p1.Y;
y = y + t1_3a * p2.Y;
y = y + t1_3b * p3.Y;
y = y + t1_3c * p4.Y;
R.X = x;
R.Y = y;
return R;
}
public static PointD Normalize(PointD inp)
{
PointD R = new PointD();
double Dist = (double)Math.Sqrt(Helpers.DistanceSq(new PointD(0, 0), inp));
if (Dist > 0)
{
R.X = inp.X / Dist;
R.Y = inp.Y / Dist;
}
return R;
}
public static bool IsInPolygon(List<PointD> polygon, PointD testPoint, bool reverse = false)
{
if (reverse)
{
List<PointD> P = new List<PointD>();
foreach (var p in polygon) { P.Add(p); };
P.Reverse();
return IsInPolygon(P, testPoint, false);
}
bool result = false;
int j = polygon.Count() - 1;
for (int i = 0; i < polygon.Count(); i++)
{
if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
{
if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
{
result = !result;
}
}
j = i;
}
return result;
}
public static bool IsInPolygona(List<PointD> poly, PointD p)
{
PointD p1, p2;
bool inside = false;
if (poly.Count < 3)
{
return inside;
}
var oldPoint = new PointD(
poly[poly.Count - 1].X, poly[poly.Count - 1].Y);
for (int i = 0; i < poly.Count; i++)
{
var newPoint = new PointD(poly[i].X, poly[i].Y);
if (newPoint.X > oldPoint.X)
{
p1 = oldPoint;
p2 = newPoint;
}
else
{
p1 = newPoint;
p2 = oldPoint;
}
if ((newPoint.X < p.X) == (p.X <= oldPoint.X)
&& (p.Y - p1.Y) * (p2.X - p1.X)
< (p2.Y - p1.Y) * (p.X - p1.X))
{
inside = !inside;
}
oldPoint = newPoint;
}
return inside;
}
public static PointD ClosestIntersection(float cx, float cy, float radius, PointD lineStart, PointD lineEnd)
{
PointD intersection1;
PointD intersection2;
int intersections = FindLineCircleIntersections(cx, cy, radius, lineStart, lineEnd, out intersection1, out intersection2);
if (intersections == 1)
return intersection1;//one intersection
if (intersections == 2)
{
double dist1 = Distance(intersection1, lineStart);
double dist2 = Distance(intersection2, lineStart);
if (dist1 < dist2)
return intersection1;
else
return intersection2;
}
return new PointD(0, 0);// no intersections at all
}
public static double Distance(PointD p1, PointD p2)
{
return Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
}
public static int FindLineCircleIntersections(double cx, double cy, double radius, PointD point1, PointD point2, out PointD intersection1, out PointD intersection2)
{
double dx, dy, A, B, C, det, t;
dx = point2.X - point1.X;
dy = point2.Y - point1.Y;
A = dx * dx + dy * dy;
B = 2 * (dx * (point1.X - cx) + dy * (point1.Y - cy));
C = (point1.X - cx) * (point1.X - cx) + (point1.Y - cy) * (point1.Y - cy) - radius * radius;
det = B * B - 4 * A * C;
if ((A <= 0.0000001) || (det < 0))
{
// No real solutions.
intersection1 = new PointD(double.NaN, double.NaN);
intersection2 = new PointD(double.NaN, double.NaN);
return 0;
}
else if (det == 0)
{
// One solution.
t = -B / (2 * A);
if (t < 0 || t > 1)
{
intersection1 = new PointD(double.NaN, double.NaN);
intersection2 = new PointD(double.NaN, double.NaN);
return 0;
}
intersection1 = new PointD(point1.X + t * dx, point1.Y + t * dy);
intersection2 = new PointD(double.NaN, double.NaN);
return 1;
}
else
{
// Two solutions.
double t1 = (double)((-B + Math.Sqrt(det)) / (2 * A));
if (t1 < 0 || t1 > 1.0)
{
double t2 = (double)((-B - Math.Sqrt(det)) / (2 * A));
if (t2 < 0 || t2 > 1.0)
{
intersection1 = new PointD(double.NaN, double.NaN);
intersection2 = new PointD(double.NaN, double.NaN);
return 0;
}
else
{
intersection1 = new PointD(point1.X + t2 * dx, point1.Y + t2 * dy);
intersection2 = new PointD(double.NaN, double.NaN);
return 1;
}
}
else
{
intersection1 = new PointD(point1.X + t1 * dx, point1.Y + t1 * dy);
double t2 = (double)((-B - Math.Sqrt(det)) / (2 * A));
if (t2 < 0 || t2 > 1.0)
{
intersection2 = new PointD(double.NaN, double.NaN);
return 1;
}
else
{
intersection2 = new PointD(point1.X + t2 * dx, point1.Y + t2 * dy);
return 2;
}
}
}
}
public static double AngleBetween(PointD a, PointD b)
{
return Math.Atan2(a.Y - b.Y, a.X - b.X);
}
public static void DrawMMGrid(GraphicsInterface G, float PW, float width, float height, float minorGrid, float majorGrid)
{
if (minorGrid < 0) return;
// System.Drawing.ColorTranslator.FromHtml("#c4e5ff")
Pen P = new Pen(Color.ParseHex("#c4e5ff"), PW * 0.5f);
for (float X = 0; X <= width; X += minorGrid)
{
G.DrawLine(P, X, 0, X, height);
}
for (float X = 0; X <= height; X += minorGrid)
{
G.DrawLine(P, 0.0f, X, width, X);
}
// System.Drawing.ColorTranslator.FromHtml("#bad7ed")
P = new Pen(Color.ParseHex("#bad7ed"), PW);
for (float X = 0; X <= width; X += majorGrid)
{
G.DrawLine(P, X, 0, X, height);
}
for (float X = 0; X <= height; X += majorGrid)
{
G.DrawLine(P, 0.0f, X, width, X);
}
}
public static Color RefractionBandI(int inp)
{
return RefractionBand(inp / 6.0f);
}
public static Color RefractionBand(float Inp)
{
float adjusted = (((float)Math.Floor(Inp * 6.0f) / 6.0f) * 0.3f) + 0.2f;
return Refraction(adjusted);
}
public static Color RefractionNormalized(float Space)
{
return Refraction(Space * 0.25f + 0.3f);
}
public static Color Refraction(float Space)
{
float space = 0.4f + 0.3f;//muv.x;
float A = 4.0f;
float B = A + space;
float C = B + space;
float c1 = (float)(Math.Max(0.0f, 0.35f - 0.45f * Math.Cos(Space * 6.28f * A) * Math.Sin(Space * 2.14f + 0.8f)));
float c2 = (float)(0.35 - 0.35 * Math.Cos(Space * 6.28 * B) * Math.Sin(Space * 2.14 + 0.8)); ;
float c3 = (float)(0.35 - 0.35 * Math.Cos(Space * 6.28 * C) * Math.Sin(Space * 2.14 + 0.8)); ;
// Time varying pixel color
float r = c1;
float g = c2;
float b = c3;
float expo = 0.5f;//muv.y * 2.0 + 0.1;
byte RR = (byte)(Math.Pow(r, expo) * 255.0);
byte GG = (byte)(Math.Pow(g, expo) * 255.0);
byte BB = (byte)(Math.Pow(b, expo) * 255.0);
return Color.FromRgba(RR, GG, BB, 255);
}
public static int LayerOrdering(BoardSide side, BoardLayer layer)
{
int ID = 40 - (int)layer;
if (side == BoardSide.Both) return ID + 100;
if (side == BoardSide.Bottom) return -ID;
return ID;
}
}
}