using System; using System.Collections.Generic; using Avalonia.Media; using Avalonia.Media.Immutable; using Avalonia.Platform; using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { /// /// A node in the scene graph which represents a line draw. /// internal class LineNode : BrushDrawOperation { /// /// Initializes a new instance of the class. /// /// The transform. /// The stroke pen. /// The start point of the line. /// The end point of the line. /// Child scenes for drawing visual brushes. public LineNode( Matrix transform, IPen pen, Point p1, Point p2, IDisposable? aux = null) : base(LineBoundsHelper.CalculateBounds(p1, p2, pen), transform, aux) { Transform = transform; Pen = pen.ToImmutable(); P1 = p1; P2 = p2; } /// /// Gets the transform with which the node will be drawn. /// public Matrix Transform { get; } /// /// Gets the stroke pen. /// public ImmutablePen Pen { get; } /// /// Gets the start point of the line. /// public Point P1 { get; } /// /// Gets the end point of the line. /// public Point P2 { get; } /// /// Determines if this draw operation equals another. /// /// The transform of the other draw operation. /// The stroke of the other draw operation. /// The start point of the other draw operation. /// The end point of the other draw operation. /// True if the draw operations are the same, otherwise false. /// /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// public bool Equals(Matrix transform, IPen pen, Point p1, Point p2) { return transform == Transform && Equals(Pen, pen) && p1 == P1 && p2 == P2; } public override void Render(IDrawingContextImpl context) { context.Transform = Transform; context.DrawLine(Pen, P1, P2); } public override bool HitTest(Point p) { if (!Transform.HasInverse) return false; p *= Transform.Invert(); var halfThickness = Pen.Thickness / 2; var minX = Math.Min(P1.X, P2.X) - halfThickness; var maxX = Math.Max(P1.X, P2.X) + halfThickness; var minY = Math.Min(P1.Y, P2.Y) - halfThickness; var maxY = Math.Max(P1.Y, P2.Y) + halfThickness; if (p.X < minX || p.X > maxX || p.Y < minY || p.Y > maxY) return false; var a = P1; var b = P2; //If dot1 or dot2 is negative, then the angle between the perpendicular and the segment is obtuse. //The distance from a point to a straight line is defined as the //length of the vector formed by the point and the closest point of the segment Vector ap = p - a; var dot1 = Vector.Dot(b - a, ap); if (dot1 < 0) return ap.Length <= Pen.Thickness / 2; Vector bp = p - b; var dot2 = Vector.Dot(a - b, bp); if (dot2 < 0) return bp.Length <= halfThickness; var bXaX = b.X - a.X; var bYaY = b.Y - a.Y; var distance = (bXaX * (p.Y - a.Y) - bYaY * (p.X - a.X)) / (Math.Sqrt(bXaX * bXaX + bYaY * bYaY)); return Math.Abs(distance) <= halfThickness; } } }