Well I'm lacking GraphicsPath in Unity (to fills polygon, draw them with and outline and utilities with shapes in general), so I'm doing my own implementation of it. Well, we could debate also which is the best option, but actually, I prefer this because I'm learning a lot.
The idea is the following, given a polygon, we do an offset polygon (inwards and outwards) with ClipperLib, and later with LibTessDotNet we triangulate it, outputing this:
Green, blue and yellow pixels are the sides of every triangle. LibTessDotNet output like 501 triangles for this shape.
So, thanks to @SimpleVar I done this:
public static IEnumerable<T> PointsInTriangle<T>(T pt1, T pt2, T pt3)
where T : IPoint
{
/*
// https://www.geeksforgeeks.org/check-whether-triangle-valid-not-sides-given/
a + b > c
a + c > b
b + c > a
*/
float a = Vector2.Distance(new Vector2(pt1.x, pt1.y), new Vector2(pt2.x, pt2.y)),
b = Vector2.Distance(new Vector2(pt2.x, pt2.y), new Vector2(pt3.x, pt3.y)),
c = Vector2.Distance(new Vector2(pt3.x, pt3.y), new Vector2(pt1.x, pt1.y));
// (new[] { pt1, pt2, pt3 }).Distinct(new PointComparer()).Count() == 0
if (a + b <= c || a + c <= b || b + c <= a)
{
Debug.LogWarning($"The given points must form a triangle. {{{pt1}, {pt2}, {pt3}}}");
yield break;
}
T tmp;
if (pt2.x < pt1.x)
{
tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
if (pt3.x < pt2.x)
{
tmp = pt2;
pt2 = pt3;
pt3 = tmp;
if (pt2.x < pt1.x)
{
tmp = pt1;
pt1 = pt2;
pt2 = tmp;
}
}
var baseFunc = CreateFunc(pt1, pt3);
var line1Func = pt1.x == pt2.x ? (x => pt2.y) : CreateFunc(pt1, pt2);
for (var x = pt1.x; x < pt2.x; ++x)
{
int maxY;
int minY = GetRange(line1Func(x), baseFunc(x), out maxY);
for (int y = minY; y <= maxY; ++y)
yield return (T)Activator.CreateInstance(typeof(T), x, y);
}
var line2Func = pt2.x == pt3.x ? (x => pt2.y) : CreateFunc(pt2, pt3);
for (var x = pt2.x; x <= pt3.x; ++x)
{
int maxY;
int minY = GetRange(line2Func(x), baseFunc(x), out maxY);
for (int y = minY; y <= maxY; ++y)
yield return (T)Activator.CreateInstance(typeof(T), x, y);
}
}
private static int GetRange(float y1, float y2, out int maxY)
{
if (y1 < y2)
{
maxY = Mathf.FloorToInt(y2);
return Mathf.CeilToInt(y1);
}
maxY = Mathf.FloorToInt(y1);
return Mathf.CeilToInt(y2);
}
private static Func<int, float> CreateFunc<T>(T pt1, T pt2)
where T : IPoint
{
var y0 = pt1.y;
if (y0 == pt2.y)
return x => y0;
float m = (float)(pt2.y - y0) / (pt2.x - pt1.x);
return x => m * (x - pt1.x) + y0;
}
Actually it works, but not too fine. Because it causes overflows (I have to kill Unity process with Process Explorer due to the big amount of RAM used by this code).
I have debugged this thing with breakpoints, but I can't figure where is the problem actually.
I think the problem are in for (var x = pt1.x; x < pt2.x; ++x)
or for (int y = minY; y <= maxY; ++y)
or in the next block... But as I said I can't debug like I'm get used to in WinForms. When the overflow is reached Visual Studio stops debugging and Unity crashes, so I'm a little bit stucked.
I tried to do a DotNetFiddle doing an overflow but I can't figure out nothing here... So... I don't know what can I do to improve the code.
Explain me everything you find is unoptimized, as well as, approaches I could do to improve my main goal.
Update
or something? Make sure you are only calling it once. – Beatitude