diff --git a/Shapes/Shapes/Circle.cs b/Shapes/Shapes/Circle.cs index c0989eb..3e02f3a 100644 --- a/Shapes/Shapes/Circle.cs +++ b/Shapes/Shapes/Circle.cs @@ -1,4 +1,7 @@ -using System.Numerics; +using System; +using Avalonia; +using Avalonia.Media; +using SimpleDrawing; namespace Shapes.Shapes; @@ -7,33 +10,40 @@ public sealed class Circle : Shape private const int RadiusMin = 5; private const int RadiusMax = 100; - public Circle(Vector2 position, Vector2 scale) - : base(position, new Vector2( - Math.Clamp(scale.X, RadiusMin, RadiusMax), - Math.Clamp(scale.Y, RadiusMin, RadiusMax))) + public Circle(Point position, Point scale, + double? lineThickness = null, IBrush? lineColor = null, IBrush? fillColor = null) + : base(position, new Point(Math.Clamp(scale.X, RadiusMin, RadiusMax), + Math.Clamp(scale.Y, RadiusMin, RadiusMax)), + lineThickness, lineColor, fillColor) { // Clamp the scale values directly in the base ctor + // Just clamping the size.Y as default. Not actually needed } - public override bool DrawSelf(Vector2 position = default, Vector2 scale = default) + 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; - // TODO - return false; + return LeoCanvas.DrawCircle(new Point(position.X, position.Y), scale.X, + lineThicknessUsed, lineColorUsed, fillColorUsed); } - public override bool PointInShape(Vector2 mousePoint) + public override bool PointInShape(Point mousePoint) { - // TODO - return false; + double distanceSquared = Math.Pow(mousePoint.X - Position.X, 2) + Math.Pow(mousePoint.Y - Position.Y, 2); + + double radiusSquared = Math.Pow(Scale.X, 2); + return distanceSquared <= radiusSquared; } } diff --git a/Shapes/Shapes/Rectangle.cs b/Shapes/Shapes/Rectangle.cs index 8bf619e..69ae952 100644 --- a/Shapes/Shapes/Rectangle.cs +++ b/Shapes/Shapes/Rectangle.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using Avalonia; +using Avalonia.Media; using SimpleDrawing; namespace Shapes.Shapes; @@ -8,40 +9,46 @@ public sealed class Rectangle : Shape private const int SideLengthMin = 10; private const int SideLengthMax = 150; - public Rectangle(Vector2 position, Vector2 scale) - : base(position, new Vector2( - Math.Clamp(scale.X, SideLengthMin, SideLengthMax), - Math.Clamp(scale.Y, SideLengthMin, SideLengthMax))) + public Rectangle(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(Vector2 position = default, Vector2 scale = default) + 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; } - - return LeoCanvas.DrawRectangle(new(position.X - scale.X/2, position.Y - scale.Y/2), new(scale.X, scale.Y)); + double lineThicknessUsed = lineThickness ?? LineThickness ?? DefaultLineThickness; + IBrush? lineColorUsed = lineColor ?? LineColor; + IBrush? fillColorUsed = fillColor ?? FillColor; + + var topLeft = new Point(position.X - scale.X/2, position.Y - scale.Y/2); + var bottomRight = new Point(position.X + scale.X/2, position.Y + scale.Y/2); + return LeoCanvas.DrawRectangle(topLeft, bottomRight, + lineThicknessUsed, lineColorUsed, fillColorUsed); } - public override bool PointInShape(Vector2 mousePoint) + public override bool PointInShape(Point mousePoint) { - float halfWidth = Scale.X / 2; - float halfHeight = Scale.Y / 2; + double halfWidth = Scale.X / 2; + double halfHeight = Scale.Y / 2; - float left = Position.X - halfWidth; - float right = Position.X + halfWidth; - float top = Position.Y - halfHeight; - float bottom = Position.Y + halfHeight; + double left = Position.X - halfWidth; + double right = Position.X + halfWidth; + double top = Position.Y - halfHeight; + double bottom = Position.Y + halfHeight; - // If point is inside shape return mousePoint.X >= left && mousePoint.X <= right && mousePoint.Y >= top && mousePoint.Y <= bottom; } diff --git a/Shapes/Shapes/Square.cs b/Shapes/Shapes/Square.cs index c775fb4..f7bda9b 100644 --- a/Shapes/Shapes/Square.cs +++ b/Shapes/Shapes/Square.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using Avalonia; +using Avalonia.Media; using SimpleDrawing; namespace Shapes.Shapes; @@ -7,39 +8,48 @@ public sealed class Square : Shape { private const int SideLengthMin = 10; private const int SideLengthMax = 120; - - public Square(Vector2 position, Vector2 scale) - : base(position, new Vector2( - Math.Clamp(scale.X, SideLengthMin, SideLengthMax), - Math.Clamp(scale.Y, SideLengthMin, SideLengthMax))) + + public Square(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 + // Just clamping the size.Y as default. Not actually needed } - - public override bool DrawSelf(Vector2 position = default, Vector2 scale = default) + + 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; - return LeoCanvas.DrawRectangle(new(position.X - scale.X/2, position.Y - scale.Y/2), new(scale.X, scale.Y)); + double size = scale.X; + var topLeft = new Point(position.X - size/2, position.Y - size/2); + var bottomRight = new Point(position.X + size/2, position.Y + size/2); + return LeoCanvas.DrawRectangle(topLeft, bottomRight, + lineThicknessUsed, lineColorUsed, fillColorUsed); } - - public override bool PointInShape(Vector2 mousePoint) - { - float halfWidth = Scale.X / 2; - float halfHeight = Scale.Y / 2; - float left = Position.X - halfWidth; - float right = Position.X + halfWidth; - float top = Position.Y - halfHeight; - float bottom = Position.Y + halfHeight; + public override bool PointInShape(Point mousePoint) + { + double halfWidth = Scale.X / 2; + double halfHeight = Scale.Y / 2; + + double left = Position.X - halfWidth; + double right = Position.X + halfWidth; + double top = Position.Y - halfHeight; + double bottom = Position.Y + halfHeight; // If point is inside shape return mousePoint.X >= left && mousePoint.X <= right && diff --git a/Shapes/Shapes/Triangle.cs b/Shapes/Shapes/Triangle.cs index 54439f0..e771053 100644 --- a/Shapes/Shapes/Triangle.cs +++ b/Shapes/Shapes/Triangle.cs @@ -1,5 +1,5 @@ -using System.Numerics; -using Avalonia; +using Avalonia; +using Avalonia.Media; using SimpleDrawing; namespace Shapes.Shapes; @@ -9,33 +9,65 @@ public sealed class Triangle : Shape private const int SideLengthMin = 10; private const int SideLengthMax = 150; - public Triangle(Vector2 position, Vector2 scale) - : base(position, new Vector2( - Math.Clamp(scale.X, SideLengthMin, SideLengthMax), - Math.Clamp(scale.Y, SideLengthMin, SideLengthMax))) + 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(Vector2 position = default, Vector2 scale = default) + 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; - // TODO - return false; + var vertices = CalculateTriangleVertices(position, scale.X); + return LeoCanvas.DrawPolygonByPath(vertices, lineThicknessUsed, lineColorUsed, fillColorUsed); } - public override bool PointInShape(Vector2 mousePoint) + public override bool PointInShape(Point mousePoint) { - // TODO - return false; + 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; } }