Skip to content

Commit

Permalink
Merge pull request #6747 from RomanSoloweow/master
Browse files Browse the repository at this point in the history
LineNode hit test optimization
  • Loading branch information
Dan Walmsley committed Oct 21, 2021
1 parent 4bb99fd commit f1b0d18
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 25 deletions.
18 changes: 16 additions & 2 deletions src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
Expand Up @@ -83,6 +83,20 @@ public override void Render(IDrawingContextImpl context)

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;

Expand All @@ -100,15 +114,15 @@ public override bool HitTest(Point p)
var dot2 = Vector.Dot(a - b, bp);

if (dot2 < 0)
return bp.Length <= Pen.Thickness / 2;
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) <= Pen.Thickness / 2;
return Math.Abs(distance) <= halfThickness;
}
}
}
Expand Up @@ -11,50 +11,49 @@ public class LineNodeTests
public void HitTest_Should_Be_True()
{
var lineNode = new LineNode(
Matrix.Identity,
Matrix.Identity,
new Pen(Brushes.Black, 3),
new Point(15, 15),
new Point(150, 150));

new Point(15, 10),
new Point(150, 73));

var pointsInside = new List<Point>()
{
new Point(14, 14),
new Point(15, 15),
new Point(32.1, 30),
new Point(30, 32.1),
new Point(150, 150),
new Point(151, 151),
new Point(14, 8.9),
new Point(15, 10),
new Point(30, 15.5),
new Point(30, 18.5),
new Point(150, 73),
new Point(151, 71.9),
};

foreach (var point in pointsInside)
{
Assert.True(lineNode.HitTest(point));
Assert.True(lineNode.HitTest(point));
}
}

[Fact]
public void HitTest_Should_Be_False()
{
var lineNode = new LineNode(
Matrix.Identity,
Matrix.Identity,
new Pen(Brushes.Black, 3),
new Point(15, 15),
new Point(150, 150));

new Point(15, 10),
new Point(150, 73));

var pointsOutside= new List<Point>()
var pointsOutside = new List<Point>()
{
new Point(13.9, 13.9),
new Point(30, 32.2),
new Point(32.2, 30),
new Point(151.1, 151.1),
new Point(200, 200),
new Point(14, 8),
new Point(14, 8.8),
new Point(30, 15.3),
new Point(30, 18.7),
new Point(151, 71.8),
new Point(155, 75),
};

foreach (var point in pointsOutside)
{
Assert.False(lineNode.HitTest(point));
Assert.False(lineNode.HitTest(point));
}
}
}
Expand Down

0 comments on commit f1b0d18

Please sign in to comment.