diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs index f00f23e08b..1ff7aa8618 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Avalonia.Media; using Avalonia.Media.Immutable; using Avalonia.Platform; @@ -6,11 +7,78 @@ using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { + /// /// A node in the scene graph which represents a line draw. /// internal class LineNode : BrushDrawOperation { + + const double degreeToRadians = Math.PI / 180.0; + + private static double CalculateAngle(Point p1, Point p2) + { + var xDiff = p2.X - p1.X; + var yDiff = p2.Y - p1.Y; + + return Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI; + } + + private static double CalculateOppSide(double angle, double hyp) + { + return Math.Sin(angle * degreeToRadians) * hyp; + } + + private static double CalculateAdjSide(double angle, double hyp) + { + return Math.Cos(angle * degreeToRadians) * hyp; + } + + static Rect CalculateBounds(Point p1, Point p2, double thickness, double angleToCorner) + { + var pts = TranslatePointsAlongTangent(p1, p2, angleToCorner + 90, thickness / 2); + + return new Rect(pts.p1, pts.p2); + } + + static (Point p1, Point p2) TranslatePointsAlongTangent(Point p1, Point p2, double angle, double distance) + { + var xDiff = CalculateOppSide(angle, distance); + var yDiff = CalculateAdjSide(angle, distance); + + var c1 = new Point(p1.X + xDiff, p1.Y - yDiff); + var c2 = new Point(p1.X - xDiff, p1.Y + yDiff); + + var c3 = new Point(p2.X + xDiff, p2.Y - yDiff); + var c4 = new Point(p2.X - xDiff, p2.Y + yDiff); + + var minX = Math.Min(c1.X, Math.Min(c2.X, Math.Min(c3.X, c4.X))); + var minY = Math.Min(c1.Y, Math.Min(c2.Y, Math.Min(c3.Y, c4.Y))); + var maxX = Math.Max(c1.X, Math.Max(c2.X, Math.Max(c3.X, c4.X))); + var maxY = Math.Max(c1.Y, Math.Max(c2.Y, Math.Max(c3.Y, c4.Y))); + + return (new Point(minX, minY), new Point(maxX, maxY)); + } + + static Rect CalculateBounds(Point p1, Point p2, IPen p) + { + var angle = CalculateAngle(p1, p2); + + var angleToCorner = 90 - angle; + + if (p.LineCap != PenLineCap.Flat) + { + var pts = TranslatePointsAlongTangent(p1, p2, angleToCorner, p.Thickness / 2); + + return CalculateBounds(pts.p1, pts.p2, p.Thickness, angleToCorner); + } + else + { + return CalculateBounds(p1, p2, p.Thickness, angleToCorner); + } + } + + /// /// Initializes a new instance of the class. /// @@ -25,15 +93,19 @@ namespace Avalonia.Rendering.SceneGraph Point p1, Point p2, IDictionary childScenes = null) - : base(new Rect(p1, p2), transform, pen) + : base(CalculateBounds(p1, p2, pen), transform, pen) { Transform = transform; Pen = pen?.ToImmutable(); P1 = p1; P2 = p2; ChildScenes = childScenes; + + } + + /// /// Gets the transform with which the node will be drawn. ///