Browse Source

Rework scene hit testing.

pull/3315/head
Dariusz Komosinski 7 years ago
parent
commit
29b7d2e5ae
  1. 3
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  2. 65
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

3
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -3,6 +3,9 @@
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />

65
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.VisualTree;
using Collections.Pooled;
namespace Avalonia.Rendering.SceneGraph
{
@ -158,37 +159,63 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
private IEnumerable<IVisual> HitTest(IVisualNode node, Point p, Rect? clip, Func<IVisual, bool> filter)
private IEnumerable<IVisual> HitTest(IVisualNode root, Point p, Rect? rootClip, Func<IVisual, bool> filter)
{
if (filter?.Invoke(node.Visual) != false && node.Visual.IsAttachedToVisualTree)
bool FilterAndClip(IVisualNode node, ref Rect? clip)
{
var clipped = false;
if (node.ClipToBounds)
if (filter?.Invoke(node.Visual) != false && node.Visual.IsAttachedToVisualTree)
{
clip = clip == null ? node.ClipBounds : clip.Value.Intersect(node.ClipBounds);
clipped = !clip.Value.Contains(p);
}
var clipped = false;
if (node.GeometryClip != null)
{
var controlPoint = Root.Visual.TranslatePoint(p, node.Visual);
clipped = !node.GeometryClip.FillContains(controlPoint.Value);
if (node.ClipToBounds)
{
clip = clip == null ? node.ClipBounds : clip.Value.Intersect(node.ClipBounds);
clipped = !clip.Value.Contains(p);
}
if (node.GeometryClip != null)
{
var controlPoint = Root.Visual.TranslatePoint(p, node.Visual);
clipped = !node.GeometryClip.FillContains(controlPoint.Value);
}
return !clipped;
}
if (!clipped)
return false;
}
using (var nodeStack = new PooledStack<(IVisualNode, bool, Rect?)>())
{
nodeStack.Push((root, false, rootClip));
while (nodeStack.Count > 0)
{
for (var i = node.Children.Count - 1; i >= 0; --i)
(IVisualNode current, var wasVisited, Rect? currentClip) = nodeStack.Pop();
if (wasVisited && current == root)
{
foreach (var h in HitTest(node.Children[i], p, clip, filter))
break;
}
var children = current.Children;
int childCount = children.Count;
if (childCount == 0 || wasVisited)
{
if ((wasVisited || FilterAndClip(current, ref currentClip)) && current.HitTest(p))
{
yield return h;
yield return current.Visual;
}
}
if (node.HitTest(p))
else if (FilterAndClip(current, ref currentClip))
{
yield return node.Visual;
nodeStack.Push((current, true, default));
for (var i = 0; i < childCount; i++)
{
nodeStack.Push((current.Children[i], false, currentClip));
}
}
}
}

Loading…
Cancel
Save