mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 04:18:43 +00:00
1101 lines
39 KiB
C#
1101 lines
39 KiB
C#
#define DebugDraw // If this is uncommented, all triangles will have a set-color border drawn. This is useful to debug polygon drawing code.
|
|
//#define SqrtWorks // Only uncomment this when the square-root works
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace Orvid.Graphics
|
|
{
|
|
/// <summary>
|
|
/// This class is used to describe an image.
|
|
/// </summary>
|
|
public class Image : Shapes.Shape, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// The raw data in the image.
|
|
/// </summary>
|
|
public Pixel[] Data;
|
|
/// <summary>
|
|
/// The height of the image.
|
|
/// </summary>
|
|
public int Height;
|
|
/// <summary>
|
|
/// The width of the image.
|
|
/// </summary>
|
|
public int Width;
|
|
|
|
///// <summary>
|
|
///// Creates an image from a bitmap.
|
|
///// </summary>
|
|
///// <param name="b">The input bitmap.</param>
|
|
//public Image(System.Drawing.Bitmap b)
|
|
//{
|
|
// this.Width = b.Width;
|
|
// this.Height = b.Height;
|
|
// this.Data = new Pixel[(Height + 1) * (Width + 1)];
|
|
// for (int height = 0; height < this.Height; height++)
|
|
// {
|
|
// for (int width = 0; width < this.Width; width++)
|
|
// {
|
|
// System.Drawing.Color pc = b.GetPixel(width, height);
|
|
// Pixel p = new Pixel(pc.R, pc.G, pc.B, pc.A);
|
|
// Data[((height * Width) + width)] = p;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
/// <summary>
|
|
/// Creates a new image with the specified height and width.
|
|
/// </summary>
|
|
/// <param name="height">The height of the new image.</param>
|
|
/// <param name="width">The width of the new image.</param>
|
|
public Image(int width, int height)
|
|
{
|
|
this.Width = width;
|
|
this.Height = height;
|
|
this.Data = new Pixel[(Height + 1) * (Width + 1)];
|
|
}
|
|
|
|
public Image(Vec2 v)
|
|
: this(v.X, v.Y)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default Destructor.
|
|
/// </summary>
|
|
~Image()
|
|
{
|
|
this.Data = null;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.Data = null;
|
|
System.GC.Collect();
|
|
}
|
|
|
|
#region Clear
|
|
public virtual void Clear(Pixel color)
|
|
{
|
|
for (uint i = 0; i < Data.Length; i++)
|
|
{
|
|
Data[i] = color;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
#region DON'T WORK!!!
|
|
|
|
#region DrawElipse
|
|
/// <summary>
|
|
/// Draws and fills an elipse.
|
|
/// </summary>
|
|
/// <param name="CenterPoint">The center of the elipse</param>
|
|
/// <param name="height">The height of the elipse.</param>
|
|
/// <param name="width">The width of the elipse.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawElipse(Vec2 CenterPoint, int height, int width, Pixel color)
|
|
{
|
|
double angle = 0;
|
|
double range = (360 * (Math.PI / 180));
|
|
double x = (width * Math.Cos(angle));
|
|
double y = (height * Math.Sin(angle));
|
|
do
|
|
{
|
|
int X = (int)(CenterPoint.X + x + 0.5);
|
|
int Y = (int)(CenterPoint.Y - y + 0.5);
|
|
SetPixel((uint)(X), (uint)(Y), color);
|
|
DrawLine(CenterPoint, new Vec2(X, Y), color);
|
|
angle += 0.001;
|
|
x = (width * Math.Cos(angle));
|
|
y = (height * Math.Sin(angle));
|
|
}
|
|
while (angle <= range);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws and fills an elipse.
|
|
/// </summary>
|
|
/// <param name="CenterPoint">The center of the elipse</param>
|
|
/// <param name="height">The height of the elipse.</param>
|
|
/// <param name="width">The width of the elipse.</param>
|
|
/// <param name="fillColor">The color to fill in.</param>
|
|
/// <param name="borderColor">The color to draw the border in.</param>
|
|
public void DrawElipse(Vec2 CenterPoint, int height, int width, Pixel fillColor, Pixel borderColor)
|
|
{
|
|
DrawElipse(CenterPoint, height, width, fillColor);
|
|
DrawElipseOutline(CenterPoint, height, width, borderColor);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawElipseOutline
|
|
/// <summary>
|
|
/// Draws an elipse outline.
|
|
/// </summary>
|
|
/// <param name="CenterPoint">The center of the elipse</param>
|
|
/// <param name="height">The height of the elipse.</param>
|
|
/// <param name="width">The width of the elipse.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawElipseOutline(Vec2 CenterPoint, int height, int width, Pixel color)
|
|
{
|
|
DrawElipticalArc(CenterPoint, height, width, 0, 360, color);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawElipticalArc
|
|
/// <summary>
|
|
/// Draws an eliptical arc.
|
|
/// </summary>
|
|
/// <param name="CenterPoint">The center-point of the elipse to use.</param>
|
|
/// <param name="height">The height of the elipse to use.</param>
|
|
/// <param name="width">The width of the elipse to use.</param>
|
|
/// <param name="startAngle">The angle to start drawing at.</param>
|
|
/// <param name="endAngle">The angle to stop drawing at.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawElipticalArc(Vec2 CenterPoint, int height, int width, int startAngle, int endAngle, Pixel color)
|
|
{
|
|
double angle = (((startAngle <= endAngle) ? startAngle : endAngle) * (Math.PI / 180));
|
|
double range = (((endAngle > startAngle) ? endAngle : startAngle) * (Math.PI / 180));
|
|
double x = (width * Math.Cos(angle));
|
|
double y = (height * Math.Sin(angle));
|
|
do
|
|
{
|
|
SetPixel((uint)((int)(CenterPoint.X + x + 0.5)), (uint)((int)(CenterPoint.Y - y + 0.5)), color);
|
|
angle += 0.001;
|
|
x = (width * Math.Cos(angle));
|
|
y = (height * Math.Sin(angle));
|
|
}
|
|
while (angle <= range);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawQuad
|
|
/// <summary>
|
|
/// Draws a quadrilateral with the specified points.
|
|
/// </summary>
|
|
/// <param name="p1">The first point.</param>
|
|
/// <param name="p2">The second point.</param>
|
|
/// <param name="p3">The third point.</param>
|
|
/// <param name="p4">The fourth point.</param>
|
|
/// <param name="FillColor">The color to fill in the rectangle with.</param>
|
|
/// <param name="BorderColor">The color to draw the border in.</param>
|
|
public void DrawQuad(Vec2 p1, Vec2 p2, Vec2 p3, Vec2 p4, Pixel FillColor, Pixel BorderColor)
|
|
{
|
|
DrawPolygon(new Vec2[] { p1, p2, p3, p4 }, FillColor);
|
|
DrawPolygonOutline(new Vec2[] { p1, p2, p3, p4 }, BorderColor);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws a quadrilateral with the specified points.
|
|
/// </summary>
|
|
/// <param name="p1">The first point.</param>
|
|
/// <param name="p2">The second point.</param>
|
|
/// <param name="p3">The third point.</param>
|
|
/// <param name="p4">The fourth point.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawQuad(Vec2 p1, Vec2 p2, Vec2 p3, Vec2 p4, Pixel color)
|
|
{
|
|
DrawPolygon(new Vec2[] { p1, p2, p3, p4 }, color);
|
|
}
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
|
|
// This SHOULD work. (I can't guarantee it though.)
|
|
#region DrawCircle
|
|
/// <summary>
|
|
/// Draws a filled circle.
|
|
/// </summary>
|
|
/// <param name="Center">The center of the circle.</param>
|
|
/// <param name="radius">The radius of the circle.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawCircle(Vec2 Center, int radius, Pixel color)
|
|
{
|
|
#if SqrtWorks
|
|
int x1;
|
|
int x2;
|
|
int rSquared = radius * radius;
|
|
int counter = (Center.Y + radius);
|
|
for (int count = (Center.Y - radius); count <= counter; count++)
|
|
{
|
|
int ySquared = (count - Center.Y) * (count - Center.Y);
|
|
double sqrt = Math.Sqrt(rSquared - ySquared) + 0.5;
|
|
x1 = (int)(Center.X + sqrt);
|
|
x2 = (int)(Center.X - sqrt);
|
|
Vec2 p1 = new Vec2(x1, count);
|
|
Vec2 p2 = new Vec2(x2, count);
|
|
DrawLine(p1, p2, color);
|
|
}
|
|
DrawCircleOutline(Center, radius, color);
|
|
#else
|
|
Vec2 tlcorn = new Vec2(Center.X - radius, Center.Y - radius);
|
|
Image i = new Image(radius * 2 + 2, radius * 2 + 2);
|
|
i.DrawCircleOutline(Center - tlcorn, radius, color);
|
|
i.FloodFill(Center - tlcorn, new Pixel(true), color);
|
|
DrawImage(tlcorn, i);
|
|
#endif
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region DrawCircleOutline
|
|
/// <summary>
|
|
/// Draws the outline of a circle.
|
|
/// </summary>
|
|
/// <param name="Center">The center of the circle.</param>
|
|
/// <param name="radius">The radius of the circle.</param>
|
|
/// <param name="color">The color to draw with.</param>
|
|
public void DrawCircleOutline(Vec2 Center, int radius, Pixel color)
|
|
{
|
|
int x = 0;
|
|
int y = radius;
|
|
int p = (5 - radius * 4) / 4;
|
|
|
|
SetPixel((uint)(Center.X), (uint)(Center.Y + y), color);
|
|
SetPixel((uint)(Center.X), (uint)(Center.Y - y), color);
|
|
SetPixel((uint)(Center.X + y), (uint)(Center.Y), color);
|
|
SetPixel((uint)(Center.X - y), (uint)(Center.Y), color);
|
|
while (x < y)
|
|
{
|
|
x++;
|
|
if (p < 0)
|
|
{
|
|
p += 2 * x + 1;
|
|
}
|
|
else
|
|
{
|
|
y--;
|
|
p += 2 * (x - y) + 1;
|
|
}
|
|
if (x == y)
|
|
{
|
|
SetPixel((uint)(Center.X + x), (uint)(Center.Y + y), color);
|
|
SetPixel((uint)(Center.X - x), (uint)(Center.Y + y), color);
|
|
SetPixel((uint)(Center.X + x), (uint)(Center.Y - y), color);
|
|
SetPixel((uint)(Center.X - x), (uint)(Center.Y - y), color);
|
|
}
|
|
else
|
|
{
|
|
if (x < y)
|
|
{
|
|
SetPixel((uint)(Center.X + x), (uint)(Center.Y + y), color);
|
|
SetPixel((uint)(Center.X - x), (uint)(Center.Y + y), color);
|
|
SetPixel((uint)(Center.X + x), (uint)(Center.Y - y), color);
|
|
SetPixel((uint)(Center.X - x), (uint)(Center.Y - y), color);
|
|
SetPixel((uint)(Center.X + y), (uint)(Center.Y + x), color);
|
|
SetPixel((uint)(Center.X - y), (uint)(Center.Y + x), color);
|
|
SetPixel((uint)(Center.X + y), (uint)(Center.Y - x), color);
|
|
SetPixel((uint)(Center.X - y), (uint)(Center.Y - x), color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region DrawImage
|
|
/// <summary>
|
|
/// Draws the specified image, at the specified point.
|
|
/// </summary>
|
|
/// <param name="loc"></param>
|
|
/// <param name="i"></param>
|
|
public void DrawImage(Vec2 loc, Image i)
|
|
{
|
|
Pixel value;
|
|
if (loc.X < 0)
|
|
{
|
|
if (loc.Y < 0) // Both X and Y are negative
|
|
{
|
|
for (uint y = (uint)i.Height; y >= 0 && y + loc.Y >= 0; y--)
|
|
{
|
|
for (uint x = (uint)i.Width; x >= 0 && x + loc.X >= 0; x--)
|
|
{
|
|
value = i.GetPixel(x, y);
|
|
if (!value.Empty)
|
|
SetPixel((uint)(x + loc.X), (uint)(y + loc.Y), value);
|
|
}
|
|
}
|
|
}
|
|
else // Only X is negative.
|
|
{
|
|
for (uint y = 0; y < i.Height && y + loc.Y < this.Height; y++)
|
|
{
|
|
for (uint x = (uint)i.Width; x >= 0 && x + loc.X >= 0; x--)
|
|
{
|
|
value = i.GetPixel(x, y);
|
|
if (!value.Empty)
|
|
SetPixel((uint)(x + loc.X), (uint)(y + loc.Y), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (loc.Y < 0) // Only X Positive
|
|
{
|
|
for (uint y = (uint)i.Height; y >= 0 && y + loc.Y >= 0; y--)
|
|
{
|
|
for (uint x = 0; x < i.Width && x + loc.X < this.Width; x++)
|
|
{
|
|
value = i.GetPixel(x, y);
|
|
if (!value.Empty)
|
|
SetPixel((uint)(x + loc.X), (uint)(y + loc.Y), value);
|
|
}
|
|
}
|
|
}
|
|
else // Both positive
|
|
{
|
|
for (uint y = 0; y < i.Height && y + loc.Y < this.Height; y++)
|
|
{
|
|
for (uint x = 0; x < i.Width && x + loc.X < this.Width; x++)
|
|
{
|
|
value = i.GetPixel(x, y);
|
|
if (!value.Empty)
|
|
SetPixel((uint)(x + loc.X), (uint)(y + loc.Y), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region DrawLine
|
|
/// <summary>
|
|
/// Draws a line between 2 points.
|
|
/// </summary>
|
|
/// <param name="Point1">The first point.</param>
|
|
/// <param name="Point2">The first point.</param>
|
|
/// <param name="color">The color to draw.</param>
|
|
public void DrawLine(Vec2 Point1, Vec2 Point2, Pixel color)
|
|
{
|
|
int x0 = Point1.X;
|
|
int x1 = Point2.X;
|
|
int y0 = Point1.Y;
|
|
int y1 = Point2.Y;
|
|
bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
|
|
if (steep)
|
|
{
|
|
int x3 = y0;
|
|
y0 = x0;
|
|
x0 = x3;
|
|
int x4 = y1;
|
|
y1 = x1;
|
|
x1 = x4;
|
|
}
|
|
if (x0 > x1)
|
|
{
|
|
int x5 = x0;
|
|
x0 = x1;
|
|
x1 = x5;
|
|
int x6 = y0;
|
|
y0 = y1;
|
|
y1 = x6;
|
|
}
|
|
int deltax = x1 - x0;
|
|
int deltay = Math.Abs(y1 - y0);
|
|
int error = deltax / 2;
|
|
int ystep;
|
|
int y = y0;
|
|
if (y0 < y1)
|
|
{
|
|
ystep = 1;
|
|
}
|
|
else
|
|
{
|
|
ystep = -1;
|
|
}
|
|
for (int x = x0; x <= x1; x++)
|
|
{
|
|
if (steep)
|
|
{
|
|
SetPixel((uint)y, (uint)x, color);
|
|
}
|
|
else
|
|
{
|
|
SetPixel((uint)x, (uint)y, color);
|
|
}
|
|
error = error - deltay;
|
|
if (error < 0)
|
|
{
|
|
y = y + ystep;
|
|
error = error + deltax;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region DrawLines
|
|
/// <summary>
|
|
/// Draws a set of connected lines.
|
|
/// </summary>
|
|
/// <param name="Points">
|
|
/// An array of points to draw, in the order they need drawing.
|
|
/// </param>
|
|
/// <param name="color">The color to draw.</param>
|
|
public void DrawLines(Vec2[] Points, Pixel color)
|
|
{
|
|
int n = Points.Length;
|
|
if (n >= 2)
|
|
{
|
|
DrawLine(Points[0], Points[1], color);
|
|
|
|
for (int count = 1; count < (n - 1); count++)
|
|
{
|
|
DrawLine(Points[count], Points[count + 1], color);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Not enough points provided!");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region DrawPolygon
|
|
/// <summary>
|
|
/// Draws a polygon with the specified points.
|
|
/// </summary>
|
|
/// <param name="points">The points of the polygon.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawPolygon(Vec2[] points, Pixel color)
|
|
{
|
|
Vec2[] contour = points;
|
|
List<Vec2> result = new List<Vec2>();
|
|
int cnt = 0;
|
|
#region Process
|
|
{
|
|
#warning TODO: Change these back to floats when their implemented fully.
|
|
double EPSILON = 0.0000000001f;
|
|
int n = contour.Length;
|
|
if (n < 3)
|
|
{
|
|
throw new Exception("Not enough Verticies!");
|
|
}
|
|
int[] V = new int[n];
|
|
int Area;
|
|
{
|
|
int n2 = contour.Length;
|
|
double A = 0.0f;
|
|
for (int p = n2 - 1, q = 0; q < n2; p = q++)
|
|
{
|
|
A += contour[p].X * contour[q].Y - contour[q].X * contour[p].Y;
|
|
}
|
|
Area = (Int32)(A * 0.5f);
|
|
}
|
|
if (0.0f < Area)
|
|
{
|
|
for (int v = 0; v < n; v++)
|
|
{
|
|
V[v] = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int v = 0; v < n; v++)
|
|
{
|
|
V[v] = (n - 1) - v;
|
|
}
|
|
}
|
|
int nv = n;
|
|
int count = 2 * nv;
|
|
for (int m = 0, v = nv - 1; nv > 2; )
|
|
{
|
|
if (0 >= (count--))
|
|
{
|
|
throw new Exception("Invalid polygon!");
|
|
}
|
|
int u = v;
|
|
if (nv <= u)
|
|
{
|
|
u = 0;
|
|
}
|
|
v = u + 1;
|
|
if (nv <= v)
|
|
{
|
|
v = 0;
|
|
}
|
|
int w = v + 1;
|
|
if (nv <= w)
|
|
{
|
|
w = 0;
|
|
}
|
|
bool Snip;
|
|
{
|
|
int p;
|
|
double Ax, Ay, Bx, By, Cx, Cy, Px, Py;
|
|
Ax = contour[V[u]].X;
|
|
Ay = contour[V[u]].Y;
|
|
Bx = contour[V[v]].X;
|
|
By = contour[V[v]].Y;
|
|
Cx = contour[V[w]].X;
|
|
Cy = contour[V[w]].Y;
|
|
if (EPSILON > (((Bx - Ax) * (Cy - Ay)) - ((By - Ay) * (Cx - Ax))))
|
|
{
|
|
Snip = false;
|
|
}
|
|
for (p = 0; p < nv; p++)
|
|
{
|
|
if ((p == u) || (p == v) || (p == w))
|
|
{
|
|
continue;
|
|
}
|
|
Px = contour[V[p]].X;
|
|
Py = contour[V[p]].Y;
|
|
bool InsideTriangle;
|
|
{
|
|
double ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
|
|
double cCROSSap, bCROSScp, aCROSSbp;
|
|
ax = Cx - Bx;
|
|
ay = Cy - By;
|
|
bx = Ax - Cx;
|
|
by = Ay - Cy;
|
|
cx = Bx - Ax;
|
|
cy = By - Ay;
|
|
apx = Px - Ax;
|
|
apy = Py - Ay;
|
|
bpx = Px - Bx;
|
|
bpy = Py - By;
|
|
cpx = Px - Cx;
|
|
cpy = Py - Cy;
|
|
aCROSSbp = ax * bpy - ay * bpx;
|
|
cCROSSap = cx * apy - cy * apx;
|
|
bCROSScp = bx * cpy - by * cpx;
|
|
InsideTriangle = ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
|
|
}
|
|
if (InsideTriangle)
|
|
{
|
|
Snip = false;
|
|
}
|
|
}
|
|
Snip = true;
|
|
}
|
|
if (Snip)
|
|
{
|
|
int a, b, c, s, t;
|
|
a = V[u];
|
|
b = V[v];
|
|
c = V[w];
|
|
result.Add(contour[a]);
|
|
result.Add(contour[b]);
|
|
result.Add(contour[c]);
|
|
m++;
|
|
for (s = v, t = v + 1; t < nv; s++, t++)
|
|
{
|
|
V[s] = V[t];
|
|
}
|
|
nv--;
|
|
count = 2 * nv;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
while (cnt < (result.Count / 3))
|
|
{
|
|
DrawTriangle(result[(cnt * 3)], result[(cnt * 3) + 1], result[(cnt * 3) + 2], color);
|
|
cnt++;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region DrawPolygonOutline
|
|
/// <summary>
|
|
/// Draws the outline of a polygon.
|
|
/// The last point connects to the first point.
|
|
/// </summary>
|
|
/// <param name="points">An array containing the points to draw.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawPolygonOutline(Vec2[] points, Pixel color)
|
|
{
|
|
Vec2[] pts = new Vec2[points.Length + 1];
|
|
points.CopyTo(pts, 0);
|
|
pts[pts.Length - 1] = points[0];
|
|
DrawLines(pts, color);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawRectangle
|
|
/// <summary>
|
|
/// This method fills in the space between
|
|
/// the 2 points specified in a rectangle.
|
|
/// </summary>
|
|
/// <param name="TopLeftCorner">
|
|
/// The point that specifies the top left
|
|
/// corner of the rectangle being drawn.
|
|
/// </param>
|
|
/// <param name="BottomRightCorner">
|
|
/// The point that specifies the bottom right
|
|
/// corner of the rectangle being drawn.
|
|
/// </param>
|
|
/// <param name="color">The color to draw.</param>
|
|
public void DrawRectangle(Vec2 TopLeftCorner, Vec2 BottomRightCorner, Pixel color)
|
|
{
|
|
int height = BottomRightCorner.Y - TopLeftCorner.Y;
|
|
int width = BottomRightCorner.X - TopLeftCorner.X;
|
|
Image i = new Image(width, height);
|
|
i.Clear(color);
|
|
DrawImage(TopLeftCorner, i);
|
|
//DrawQuad(TopLeftCorner, new Vec2(BottomRightCorner.X, TopLeftCorner.Y), BottomRightCorner, new Vec2(TopLeftCorner.X, BottomRightCorner.Y), color);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawReverseRectangle
|
|
/// <summary>
|
|
/// This method fills in the space between
|
|
/// the 2 points specified in a rectangle.
|
|
/// </summary>
|
|
/// <param name="TopRightCorner">
|
|
/// The point that specifies the top right
|
|
/// corner of the rectangle being drawn.
|
|
/// </param>
|
|
/// <param name="BottomLeftCorner">
|
|
/// The point that specifies the bottom left
|
|
/// corner of the rectangle being drawn.
|
|
/// </param>
|
|
/// <param name="color">The color to draw.</param>
|
|
public void ReverseDrawRectangle(Vec2 TopRightCorner, Vec2 BottomLeftCorner, Pixel color)
|
|
{
|
|
int height = BottomLeftCorner.Y - TopRightCorner.Y;
|
|
int width = TopRightCorner.X - BottomLeftCorner.X;
|
|
Image i = new Image(width, height);
|
|
i.Clear(color);
|
|
Vec2 TopLeftCorner = new Vec2(BottomLeftCorner.X, TopRightCorner.Y);
|
|
DrawImage(TopLeftCorner, i);
|
|
//DrawQuad(new Vec2(BottomLeftCorner.X, TopRightCorner.Y), TopRightCorner, new Vec2(TopRightCorner.X, BottomLeftCorner.Y), BottomLeftCorner, color);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawTriangle
|
|
/// <summary>
|
|
/// Draws a triangle and fills it in.
|
|
/// </summary>
|
|
/// <param name="p1">The first point of the triangle.</param>
|
|
/// <param name="p2">The second point of the triangle.</param>
|
|
/// <param name="p3">The third point of the triangle.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawTriangle(Vec2 p1, Vec2 p2, Vec2 p3, Pixel color)
|
|
{
|
|
|
|
int l; // The farthest left
|
|
int r;
|
|
int t;
|
|
int b;
|
|
#region Organize points
|
|
if (p1.Y > p2.Y && p1.Y > p3.Y)
|
|
{
|
|
t = p1.Y;
|
|
if (p2.Y < p3.Y)
|
|
{
|
|
b = p2.Y;
|
|
}
|
|
else
|
|
{
|
|
b = p3.Y;
|
|
}
|
|
}
|
|
else if (p2.Y > p1.Y && p2.Y > p3.Y)
|
|
{
|
|
t = p2.Y;
|
|
if (p1.Y < p3.Y)
|
|
{
|
|
b = p1.Y;
|
|
}
|
|
else
|
|
{
|
|
b = p3.Y;
|
|
}
|
|
}
|
|
else // This means p3 is the highest point
|
|
{
|
|
t = p3.Y;
|
|
if (p1.Y < p2.Y)
|
|
{
|
|
b = p1.Y;
|
|
}
|
|
else
|
|
{
|
|
b = p2.Y;
|
|
}
|
|
}
|
|
|
|
// Now organize in the width direction.
|
|
if (p1.X > p3.X && p1.X > p2.X)
|
|
{
|
|
l = p1.X;
|
|
if (p2.X < p3.X)
|
|
{
|
|
r = p2.X;
|
|
}
|
|
else
|
|
{
|
|
r = p3.X;
|
|
}
|
|
}
|
|
else if (p2.X > p3.X && p2.X > p1.X)
|
|
{
|
|
l = p2.X;
|
|
if (p1.X < p3.X)
|
|
{
|
|
r = p1.X;
|
|
}
|
|
else
|
|
{
|
|
r = p3.X;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l = p3.X;
|
|
if (p2.X < p1.X)
|
|
{
|
|
r = p2.X;
|
|
}
|
|
else
|
|
{
|
|
r = p1.X;
|
|
}
|
|
}
|
|
#endregion
|
|
int h;
|
|
int w;
|
|
if (t - b + 1 > 0)
|
|
{
|
|
h = t - b + 1;
|
|
}
|
|
else
|
|
{
|
|
h = b - t + 1;
|
|
}
|
|
if (l - r + 1 > 0)
|
|
{
|
|
w = l - r + 1;
|
|
}
|
|
else
|
|
{
|
|
w = r - l + 1;
|
|
}
|
|
Image i = new Image(w, h);
|
|
// Get the location of the points within the image.
|
|
Vec2 p4 = new Vec2(p1.X - r, p1.Y - b);
|
|
Vec2 p5 = new Vec2(p2.X - r, p2.Y - b);
|
|
Vec2 p6 = new Vec2(p3.X - r, p3.Y - b);
|
|
#if DebugDraw
|
|
Pixel p9 = new Pixel();
|
|
p9.B = 200;
|
|
i.DrawLine(p4, p5, p9);
|
|
i.DrawLine(p5, p6, p9);
|
|
i.DrawLine(p6, p4, p9);
|
|
#else
|
|
i.DrawLine(p4, p5, color);
|
|
i.DrawLine(p5, p6, color);
|
|
i.DrawLine(p6, p4, color);
|
|
#endif
|
|
Vec2 center = (p4 + p5 + p6) / 3;
|
|
i.FloodFill(center, new Pixel(true), color);
|
|
|
|
/*
|
|
|
|
NOTE TO SELF: Draw a line from each corner to the middle of the opposite line,
|
|
so as to fill in the holes that this flood fill method might leave,
|
|
when dealing with small angles. Some acute triangles have this problem.
|
|
|
|
*/
|
|
Vec2 m1 = (p4 + p5) / 2;
|
|
Vec2 m2 = (p5 + p6) / 2;
|
|
Vec2 m3 = (p4 + p6) / 2;
|
|
#if DebugDraw
|
|
Pixel p50 = new Pixel();
|
|
p50.B = 200;
|
|
i.DrawLine(p4, p5, p50);
|
|
i.DrawLine(p5, p6, p50);
|
|
i.DrawLine(p6, p4, p50);
|
|
#else
|
|
i.DrawLine(p6, m1, color);
|
|
i.DrawLine(p4, m2, color);
|
|
i.DrawLine(p5, m3, color);
|
|
#endif
|
|
|
|
Vec2 v = new Vec2(r, b);
|
|
DrawImage(v, i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws a triangle with the specified fill color, and the specified border color.
|
|
/// </summary>
|
|
/// <param name="p1">The first point.</param>
|
|
/// <param name="p2">The second point.</param>
|
|
/// <param name="p3">The third point.</param>
|
|
/// <param name="FillColor">The color to fill the triangle with.</param>
|
|
/// <param name="BorderColor">The color to draw the border of the triangle.</param>
|
|
public void DrawTriangle(Vec2 p1, Vec2 p2, Vec2 p3, Pixel FillColor, Pixel BorderColor)
|
|
{
|
|
DrawTriangle(p1, p2, p3, FillColor);
|
|
DrawTriangleOutline(p1, p2, p3, BorderColor);
|
|
}
|
|
#endregion
|
|
|
|
#region DrawTriangleOutline
|
|
/// <summary>
|
|
/// Draw a triangle's outline.
|
|
/// </summary>
|
|
/// <param name="p1">The first point of the triangle.</param>
|
|
/// <param name="p2">The second point of the triangle.</param>
|
|
/// <param name="p3">The third point of the triangle.</param>
|
|
/// <param name="color">The color to draw in.</param>
|
|
public void DrawTriangleOutline(Vec2 p1, Vec2 p2, Vec2 p3, Pixel color)
|
|
{
|
|
DrawLines(new Vec2[] { p1, p2, p3, p1 }, color);
|
|
}
|
|
#endregion
|
|
|
|
#region FloodFill
|
|
private bool[] Visiteds;
|
|
/// <summary>
|
|
/// Returns true if the given pixel has
|
|
/// been visited with the flood-fill algorithm.
|
|
/// </summary>
|
|
/// <param name="height"></param>
|
|
/// <param name="width"></param>
|
|
/// <returns></returns>
|
|
public bool Visited(int y, int x)
|
|
{
|
|
if (x >= 0 && y >= 0 && y <= Height && x <= Width)
|
|
return Visiteds[((y * Width) + x)];
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implements a flood fill algorithm.
|
|
/// </summary>
|
|
/// <param name="startPoint">The point to start filling from.</param>
|
|
/// <param name="sColor">The color to search for.</param>
|
|
/// <param name="dColor">The color to change it to.</param>
|
|
public void FloodFill(Vec2 startPoint, Pixel sColor, Pixel dColor)
|
|
{
|
|
int x = startPoint.X;
|
|
int y = startPoint.Y;
|
|
if ((GetPixel((uint)x, (uint)y).Empty) || GetPixel((uint)x, (uint)y) == sColor)
|
|
{
|
|
SetPixel((uint)x, (uint)y, dColor);
|
|
}
|
|
Stack<Vec2> q = new Stack<Vec2>();
|
|
Visiteds = new bool[(Height + 1) * (Width + 1)];
|
|
|
|
#region Setup Start of queue
|
|
Pixel p;
|
|
Vec2 v;
|
|
p = GetPixel((uint)x + 1, (uint)y);
|
|
if ((p.Empty || p == sColor) && p != dColor)
|
|
{
|
|
v = new Vec2(x + 1, y);
|
|
q.Push(v);
|
|
Visiteds[((y * Width) + x + 1)] = true;
|
|
SetPixel((uint)x + 1, (uint)y, dColor);
|
|
}
|
|
p = GetPixel((uint)x, (uint)y + 1);
|
|
if ((p.Empty || p == sColor) && p != dColor)
|
|
{
|
|
v = new Vec2(x, y + 1);
|
|
q.Push(v);
|
|
Visiteds[(((y + 1) * Width) + x)] = true;
|
|
SetPixel((uint)x, (uint)y + 1, dColor);
|
|
}
|
|
p = GetPixel((uint)x - 1, (uint)y);
|
|
if ((p.Empty || p == sColor) && p != dColor)
|
|
{
|
|
v = new Vec2(x - 1, y);
|
|
q.Push(v);
|
|
Visiteds[((y * Width) + x - 1)] = true;
|
|
SetPixel((uint)(x - 1), (uint)y, dColor);
|
|
}
|
|
p = GetPixel((uint)x, (uint)y - 1);
|
|
if ((p.Empty || p == sColor) && p != dColor)
|
|
{
|
|
v = new Vec2(x, y - 1);
|
|
q.Push(v);
|
|
Visiteds[(((y - 1) * Width) + x)] = true;
|
|
SetPixel((uint)x, (uint)(y - 1), dColor);
|
|
}
|
|
#endregion
|
|
|
|
while (q.Count > 0)
|
|
{
|
|
v = q.Pop();
|
|
x = v.X;
|
|
y = v.Y;
|
|
#region Check
|
|
|
|
p = GetPixel((uint)x + 1, (uint)y);
|
|
if (p != dColor && (p.Empty || p == sColor))
|
|
{
|
|
v = new Vec2(x + 1, y);
|
|
if (x >= 0 && !(Visited(y, x + 1)))
|
|
{
|
|
q.Push(v);
|
|
Visiteds[((y * Width) + x + 1)] = true;
|
|
SetPixel((uint)x + 1, (uint)y, dColor);
|
|
}
|
|
}
|
|
p = GetPixel((uint)x, (uint)y + 1);
|
|
if (p != dColor && (p.Empty || p == sColor))
|
|
{
|
|
v = new Vec2(x, y + 1);
|
|
if (x >= 0 && !(Visited(y + 1, x)))
|
|
{
|
|
q.Push(v);
|
|
Visiteds[(((y + 1) * Width) + x)] = true;
|
|
SetPixel((uint)x, (uint)y + 1, dColor);
|
|
}
|
|
}
|
|
p = GetPixel((uint)x - 1, (uint)y);
|
|
if (p != dColor && (p.Empty || p == sColor))
|
|
{
|
|
v = new Vec2(x - 1, y);
|
|
if (x >= 0 && !(Visited(y, x - 1)))
|
|
{
|
|
q.Push(v);
|
|
Visiteds[((y * Width) + x - 1)] = true;
|
|
SetPixel((uint)x - 1, (uint)y, dColor);
|
|
}
|
|
}
|
|
p = GetPixel((uint)x, (uint)y - 1);
|
|
if (p != dColor && (p.Empty || p == sColor))
|
|
{
|
|
v = new Vec2(x, y - 1);
|
|
if (!(Visited(y - 1, x)))
|
|
{
|
|
q.Push(v);
|
|
Visiteds[(((y - 1) * Width) + x)] = true;
|
|
SetPixel((uint)x, (uint)y - 1, dColor);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
// And finally empty the visiteds array.
|
|
Visiteds = new bool[0];
|
|
}
|
|
#endregion
|
|
|
|
#region SubImage
|
|
/// <summary>
|
|
/// Gets a sub-image of this image,
|
|
/// from the specified location,
|
|
/// of the specified size.
|
|
/// </summary>
|
|
/// <param name="loc">The location of the image to pull.</param>
|
|
/// <param name="size">The size of the image to pull.</param>
|
|
/// <returns>The sub-image obtained.</returns>
|
|
public Image SubImage(Vec2 loc, Vec2 size)
|
|
{
|
|
Image i = new Image(size);
|
|
for (int y = loc.Y; y < (loc.Y + size.Y); y++)
|
|
{
|
|
for (int x = loc.X; x < (loc.X + size.X); x++)
|
|
{
|
|
i.SetPixel((uint)(x - loc.X), (uint)(y - loc.Y), this.GetPixel((uint)x, (uint)y));
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
/// Get's the pixel a the specified location.
|
|
/// </summary>
|
|
/// <param name="width">The width position of the pixel.</param>
|
|
/// <param name="height">The height position of the pixel.</param>
|
|
/// <returns>The Pixel at the specified position.</returns>
|
|
public virtual Pixel GetPixel(uint x, uint y)
|
|
{
|
|
if (/*x > 0 &&*/ x < Width /*&& y > 0 */&& y < Height)
|
|
return Data[(y * Width) + x];
|
|
else
|
|
return new Pixel(true);
|
|
}
|
|
|
|
//public List<Vec2> Modified = new List<Vec2>();
|
|
|
|
/// <summary>
|
|
/// Set's the pixel at the specified location,
|
|
/// to the specified pixel.
|
|
/// </summary>
|
|
/// <param name="width">The width position of the pixel.</param>
|
|
/// <param name="height">The height position of the pixel.</param>
|
|
/// <param name="p">The pixel to set to.</param>
|
|
public virtual void SetPixel(uint x, uint y, Pixel p)
|
|
{
|
|
//Modified.Add(new Vec2(width, height));
|
|
if (p.A != 255)
|
|
{
|
|
if (p.A != 0)
|
|
{
|
|
double r1 = ((double)p.A / 255);
|
|
double r2 = 1.0d - r1;
|
|
Pixel cur = Data[((y * Width) + x)];
|
|
//throw new Exception();
|
|
|
|
Data[((y * Width) + x)] = new Pixel((byte)((p.R * r1) + (cur.R * r2)), (byte)((p.G * r1) + (cur.G * r2)), (byte)((p.B * r1) + (cur.B * r2)), 255);
|
|
}
|
|
// else nothing gets drawn.
|
|
}
|
|
else
|
|
{
|
|
Data[((y * Width) + x)] = p;
|
|
}
|
|
}
|
|
|
|
public void DrawString(Vec2 loc, String s, FontSupport.Font f, int stringHeight, FontSupport.FontStyle flags, Pixel color)
|
|
{
|
|
FontSupport.FontManager.Instance.DrawText(this, null, null, s, f, loc, color);
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
Parent.DrawImage(new Vec2(base.X, base.Y), this);
|
|
}
|
|
|
|
#region Operators
|
|
public static explicit operator System.Drawing.Bitmap(Image i)
|
|
{
|
|
System.Drawing.Bitmap b = new System.Drawing.Bitmap(i.Width, i.Height);
|
|
System.Drawing.Imaging.BitmapData bd = b.LockBits(new System.Drawing.Rectangle(0, 0, i.Width, i.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
|
int bytes = Math.Abs(bd.Stride) * bd.Height;
|
|
byte[] rgbValues = new byte[bytes];
|
|
System.Runtime.InteropServices.Marshal.Copy(bd.Scan0, rgbValues, 0, bytes);
|
|
uint len = (uint)(i.Height * i.Width);
|
|
for (uint d = 0, ind = 0; d < len; d++)
|
|
{
|
|
rgbValues[ind] = i.Data[d].B;
|
|
ind++;
|
|
rgbValues[ind] = i.Data[d].G;
|
|
ind++;
|
|
rgbValues[ind] = i.Data[d].R;
|
|
ind++;
|
|
rgbValues[ind] = i.Data[d].A;
|
|
ind++;
|
|
}
|
|
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, bd.Scan0, bytes);
|
|
rgbValues = null;
|
|
b.UnlockBits(bd);
|
|
return b;
|
|
}
|
|
|
|
public static explicit operator Image(System.Drawing.Bitmap b)
|
|
{
|
|
Image i = new Image(b.Width, b.Height);
|
|
for (uint x = 0; x < b.Width; x++)
|
|
{
|
|
for (uint y = 0; y < b.Height; y++)
|
|
{
|
|
i.SetPixel(x, y, b.GetPixel((int)x, (int)y));
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|