ex-inh-03-shapes/Shapes/Shapes/Triangle.cs
2025-03-16 23:18:52 +01:00

73 lines
3 KiB
C#

using Avalonia;
using Avalonia.Media;
using SimpleDrawing;
namespace Shapes.Shapes;
public sealed class Triangle : Shape
{
private const int SideLengthMin = 10;
private const int SideLengthMax = 150;
public Triangle(Point position, Point scale,
double? lineThickness = null, IBrush? lineColor = null, IBrush? fillColor = null)
: base(position, new Point(Math.Clamp(scale.X, SideLengthMin, SideLengthMax),
Math.Clamp(scale.Y, SideLengthMin, SideLengthMax)),
lineThickness, lineColor, fillColor)
{
// Clamp the scale values directly in the base ctor
}
public override bool DrawSelf(Point position = default, Point scale = default,
double? lineThickness = null, IBrush? lineColor = null, IBrush? fillColor = null)
{
if (position == default)
{
position = Position;
}
if (scale == default)
{
scale = Scale;
}
double lineThicknessUsed = lineThickness ?? LineThickness ?? DefaultLineThickness;
IBrush? lineColorUsed = lineColor ?? LineColor;
IBrush? fillColorUsed = fillColor ?? FillColor;
var vertices = CalculateTriangleVertices(position, scale.X);
return LeoCanvas.DrawPolygonByPath(vertices, lineThicknessUsed, lineColorUsed, fillColorUsed);
}
public override bool PointInShape(Point mousePoint)
{
var vertices = CalculateTriangleVertices(Position, Scale.X);
return IsPointInTriangle(mousePoint, vertices[0], vertices[1], vertices[2]);
}
private static Point[] CalculateTriangleVertices(Point center, double sideLength)
{
double height = sideLength * Math.Sqrt(3) / 2;
double yOffset = height / 3;
var vertices = new Point[3];
vertices[0] = new Point(center.X, center.Y - height + yOffset);
vertices[1] = new Point(center.X - sideLength / 2, center.Y + yOffset);
vertices[2] = new Point(center.X + sideLength / 2, center.Y + yOffset);
return vertices;
}
// Ripped from https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle
// very nice & self written code :)
// Double is used instead of float to avoid casting. Unfortunately, float isn't supported in Avalonia.Base.Point
// Double is worse in performance than float, if you don't need high precision and run it in a loop like GameDev
private static bool IsPointInTriangle(Point p, Point a, Point b, Point c)
{
double d = (b.Y - c.Y) * (a.X - c.X) + (c.X - b.X) * (a.Y - c.Y);
double alpha = ((b.Y - c.Y) * (p.X - c.X) + (c.X - b.X) * (p.Y - c.Y)) / d;
double beta = ((c.Y - a.Y) * (p.X - c.X) + (a.X - c.X) * (p.Y - c.Y)) / d;
double gamma = 1 - alpha - beta;
return alpha >= 0 && beta >= 0 && gamma >= 0;
}
}