Es increíble que después de 5 años y medio aún sigas actualizando el tema. Wow.
Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.
Mostrar Mensajes Menúusing System;
using UnityEngine;
namespace UnityEngine.Utilities.Drawing
{
// Uncomment this if you don't a an own implementation
/*public struct Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = 0;
this.y = 0;
}
}*/
public static class TriangleUtils
{
public static void Rasterize(Point pt1, Point pt2, Point pt3, Action<int, int> action, bool debug = false)
{
/*
// https://www.geeksforgeeks.org/check-whether-triangle-valid-not-sides-given/
a + b > c
a + c > b
b + c > a
*/
if (!CheckIfValidTriangle(pt1, pt2, pt3, debug))
return;
if (TriangleArea(pt1, pt2, pt3) <= 1)
{
Point center = GetTriangleCenter(pt1, pt2, pt3);
action?.Invoke(center.x, center.y);
return;
}
Point 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)
action?.Invoke(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)
action?.Invoke(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(Point pt1, Point pt2)
{
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;
}
public static void RasterizeStandard(Point p1, Point p2, Point p3, Action<int, int> action, bool debug = false)
{
// Thanks to: https://www.davrous.com/2013/06/21/tutorial-part-4-learning-how-to-write-a-3d-software-engine-in-c-ts-or-js-rasterization-z-buffering/
if (!CheckIfValidTriangle(p1, p2, p3, debug))
return;
// Sorting the points in order to always have this order on screen p1, p2 & p3
// with p1 always up (thus having the y the lowest possible to be near the top screen)
// then p2 between p1 & p3
if (p1.y > p2.y)
{
var temp = p2;
p2 = p1;
p1 = temp;
}
if (p2.y > p3.y)
{
var temp = p2;
p2 = p3;
p3 = temp;
}
if (p1.y > p2.y)
{
var temp = p2;
p2 = p1;
p1 = temp;
}
// inverse slopes
float dP1P2, dP1P3;
// http://en.wikipedia.org/wiki/Slope
// Computing inverse slopes
if (p2.y - p1.y > 0)
dP1P2 = (p2.x - p1.x) / (p2.y - p1.y);
else
dP1P2 = 0;
if (p3.y - p1.y > 0)
dP1P3 = (p3.x - p1.x) / (p3.y - p1.y);
else
dP1P3 = 0;
// First case where triangles are like that:
// P1
// -
// --
// - -
// - -
// - - P2
// - -
// - -
// -
// P3
if (dP1P2 > dP1P3)
{
for (var y = p1.y; y <= p3.y; y++)
{
if (y < p2.y)
ProcessScanLine(y, p1, p3, p1, p2, action);
else
ProcessScanLine(y, p1, p3, p2, p3, action);
}
}
// First case where triangles are like that:
// P1
// -
// --
// - -
// - -
// P2 - -
// - -
// - -
// -
// P3
else
{
for (var y = p1.y; y <= p3.y; y++)
{
if (y < p2.y)
ProcessScanLine(y, p1, p2, p1, p3, action);
else
ProcessScanLine(y, p2, p3, p1, p3, action);
}
}
}
// drawing line between 2 points from left to right
// papb -> pcpd
// pa, pb, pc, pd must then be sorted before
private static void ProcessScanLine(int y, Point pa, Point pb, Point pc, Point pd, Action<int, int> action)
{
// Thanks to current y, we can compute the gradient to compute others values like
// the starting x (sx) and ending x (ex) to draw between
// if pa.y == pb.y or pc.y == pd.y, gradient is forced to 1
var gradient1 = pa.y != pb.y ? (y - pa.y) / (pb.y - pa.y) : 1;
var gradient2 = pc.y != pd.y ? (y - pc.y) / (pd.y - pc.y) : 1;
int sx = (int)Mathf.Lerp(pa.x, pb.x, gradient1);
int ex = (int)Mathf.Lerp(pc.x, pd.x, gradient2);
// drawing a line from left (sx) to right (ex)
for (var x = sx; x < ex; x++)
action?.Invoke(x, y);
}
public static void RasterizeBarycentric(Point v1, Point v2, Point v3, Action<int, int> action, bool debug = false)
{
// Thanks to: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html#algo3 && https://www.cs.unm.edu/~joel/cs491_VR/src/DrawUtilities.cs
/* checks for a valid triangle */
if (!CheckIfValidTriangle(v1, v2, v3, debug))
return;
/* get the bounding box of the triangle */
int maxX = Mathf.Max(v1.x, Mathf.Max(v2.x, v3.x));
int minX = Mathf.Min(v1.x, Mathf.Min(v2.x, v3.x));
int maxY = Mathf.Max(v1.y, Mathf.Max(v2.y, v3.y));
int minY = Mathf.Min(v1.y, Mathf.Min(v2.y, v3.y));
//if (debug)
// Debug.Log($"({minX}, {minY}, {maxX}, {maxY})");
/* spanning vectors of edge (v1,v2) and (v1,v3) */
Point vs1 = new Point(v2.x - v1.x, v2.y - v1.y);
Point vs2 = new Point(v3.x - v1.x, v3.y - v1.y);
for (int x = minX; x <= maxX; x++)
{
for (int y = minY; y <= maxY; y++)
{
Point q = new Point(x - v1.x, y - v1.y);
float s = Vector2.Dot(q, vs2) / Vector2.Dot(vs1, vs2);
float t = Vector2.Dot(vs1, q) / Vector2.Dot(vs1, vs2);
if ((s >= 0) && (t >= 0) && (s + t <= 1))
{ /* inside triangle */
action?.Invoke(x, y);
}
}
}
}
public static void RasterizeBresenham(Point vt1, Point vt2, Point vt3, Action<int, int> action, bool debugException = false)
{
// Thanks to: http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html#algo3 && https://www.cs.unm.edu/~joel/cs491_VR/src/DrawUtilities.cs
/* checks for a valid triangle */
if (!CheckIfValidTriangle(vt1, vt2, vt3, debugException))
return;
string invalidTriangle = $"The given points must form a triangle. {{{vt1}, {vt2}, {vt3}}}";
/* at first sort the three vertices by y-coordinate ascending so v1 is the topmost vertice */
sortVerticesAscendingByY(ref vt1, ref vt2, ref vt3);
/* here we know that v1.y <= v2.y <= v3.y */
/* check for trivial case of bottom-flat triangle */
if (vt2.y == vt3.y)
{
if (!fillBottomFlatTriangle(vt1, vt2, vt3, action, debugException))
{
if (debugException)
Debug.LogWarning(invalidTriangle);
return;
}
}
/* check for trivial case of top-flat triangle */
else if (vt1.y == vt2.y)
{
if (!fillTopFlatTriangle(vt1, vt2, vt3, action, debugException))
{
if (debugException)
Debug.LogWarning(invalidTriangle);
return;
}
}
else
{
/* general case - split the triangle in a topflat and bottom-flat one */
Point v4 = new Point((int)(vt1.x + (vt2.y - vt1.y) / (float)(vt3.y - vt1.y) * (vt3.x - vt1.x)), vt2.y);
if (!fillBottomFlatTriangle(vt1, vt2, v4, action, debugException) || !fillTopFlatTriangle(vt2, v4, vt3, action, debugException))
{
if (debugException)
Debug.LogWarning(invalidTriangle);
return;
}
}
}
public static bool CheckIfValidTriangle(Point v1, Point v2, Point v3, bool debug = false)
{
/*
// 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(v1.x, v1.y), new Vector2(v2.x, v2.y)),
b = Vector2.Distance(new Vector2(v2.x, v2.y), new Vector2(v3.x, v3.y)),
c = Vector2.Distance(new Vector2(v3.x, v3.y), new Vector2(v1.x, v1.y));
if (a + b <= c || a + c <= b || b + c <= a)
{
if (debug)
Debug.LogWarning($"The given points must form a triangle. {{{v1}, {v2}, {v3}}}");
return false;
}
return true;
}
public static bool CheckIfValidTriangle(Point v1, Point v2, Point v3, out float a, out float b, out float c)
{
a = Vector2.Distance(new Vector2(v1.x, v1.y), new Vector2(v2.x, v2.y));
b = Vector2.Distance(new Vector2(v2.x, v2.y), new Vector2(v3.x, v3.y));
c = Vector2.Distance(new Vector2(v3.x, v3.y), new Vector2(v1.x, v1.y));
if (a + b <= c || a + c <= b || b + c <= a)
{
// Debug.LogWarning($"The given points must form a triangle. {{{v1}, {v2}, {v3}}}");
return false;
}
return true;
}
private static bool fillBottomFlatTriangle(Point v1, Point v2, Point v3, Action<int, int> action, bool debugException = false)
{
try
{
float invslope1 = (v2.x - v1.x) / (v2.y - v1.y);
float invslope2 = (v3.x - v1.x) / (v3.y - v1.y);
float curx1 = v1.x;
float curx2 = v1.x;
for (int scanlineY = v1.y; scanlineY <= v2.y; scanlineY++)
{
DrawLine((int)curx1, scanlineY, (int)curx2, scanlineY, action);
curx1 += invslope1;
curx2 += invslope2;
}
}
catch (Exception ex)
{
if (debugException)
Debug.LogException(ex);
return false;
}
return true;
}
private static bool fillTopFlatTriangle(Point v1, Point v2, Point v3, Action<int, int> action, bool debugException = false)
{
try
{
float invslope1 = (v3.x - v1.x) / (v3.y - v1.y);
float invslope2 = (v3.x - v2.x) / (v3.y - v2.y);
float curx1 = v3.x;
float curx2 = v3.x;
for (int scanlineY = v3.y; scanlineY > v1.y; scanlineY--)
{
DrawLine((int)curx1, scanlineY, (int)curx2, scanlineY, action);
curx1 -= invslope1;
curx2 -= invslope2;
}
}
catch (Exception ex)
{
if (debugException)
Debug.LogException(ex);
return false;
}
return true;
}
private static void sortVerticesAscendingByY(ref Point v0, ref Point v1, ref Point v2)
{
if (v2.y < v1.y)
Swap(ref v1, ref v2);
if (v1.y < v0.y)
Swap(ref v0, ref v1);
if (v2.y < v1.y)
Swap(ref v1, ref v2);
}
private static void Swap<T>(ref T lhs, ref T rhs)
{
(lhs, rhs) = (rhs, lhs);
// Impl for versions lower than C# 7
//T temp;
//temp = lhs;
//lhs = rhs;
//rhs = temp;
}
public static float TriangleArea(Point p1, Point p2, Point p3)
{
float a, b, c;
if (!CheckIfValidTriangle(p1, p2, p3, out a, out b, out c))
return 0;
return TriangleArea(a, b, c);
}
public static float TriangleArea(float a, float b, float c)
{
// Thanks to: http://james-ramsden.com/area-of-a-triangle-in-3d-c-code/
float s = (a + b + c) / 2.0f;
return Mathf.Sqrt(s * (s - a) * (s - b) * (s - c));
}
// Bresenham impl: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
public static void DrawLine(Vector2 p1, Vector2 p2, Action<int, int> action)
{
DrawLine((int)p1.x, (int)p1.y, (int)p2.x, (int)p2.y, action);
}
public static void DrawLine(int x0, int y0, int x1, int y1, Action<int, int> action)
{
int sx = 0,
sy = 0;
int dx = Mathf.Abs(x1 - x0),
dy = Mathf.Abs(y1 - y0);
if (x0 < x1) { sx = 1; } else { sx = -1; }
if (y0 < y1) { sy = 1; } else { sy = -1; }
int err = dx - dy,
e2 = 0;
while (true)
{
//colors[P(x0 % width, y0 % height, width, height)] = x0 / width >= 1 || y0 / height >= 1 ? UnityEngine.Color.yellow : c;
action?.Invoke(x0, y0);
if ((x0 == x1) && (y0 == y1))
break;
e2 = 2 * err;
if (e2 > -dy)
{
err = err - dy;
x0 = x0 + sx;
}
if (e2 < dx)
{
err = err + dx;
y0 = y0 + sy;
}
}
}
public static Point GetTriangleCenter(Point p0, Point p1, Point p2)
{
// Thanks to: https://stackoverflow.com/questions/524755/finding-center-of-2d-triangle
return new Point(p0.x + p1.x + p2.x / 3, p0.y + p1.y + p2.y / 3);
}
}
}
Cita de: MCKSys Argentina en 8 Diciembre 2018, 17:26 PM
PD: He colocado la noticia porque sé que a algunos les gustan éstas cosas...