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> <TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace> <RootNamespace>Avalonia</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" /> <ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.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.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Collections.Pooled;
namespace Avalonia.Rendering.SceneGraph namespace Avalonia.Rendering.SceneGraph
{ {
@ -158,37 +159,63 @@ namespace Avalonia.Rendering.SceneGraph
return result; 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 (filter?.Invoke(node.Visual) != false && node.Visual.IsAttachedToVisualTree)
if (node.ClipToBounds)
{ {
clip = clip == null ? node.ClipBounds : clip.Value.Intersect(node.ClipBounds); var clipped = false;
clipped = !clip.Value.Contains(p);
}
if (node.GeometryClip != null) if (node.ClipToBounds)
{ {
var controlPoint = Root.Visual.TranslatePoint(p, node.Visual); clip = clip == null ? node.ClipBounds : clip.Value.Intersect(node.ClipBounds);
clipped = !node.GeometryClip.FillContains(controlPoint.Value); 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;
} }
} }
else if (FilterAndClip(current, ref currentClip))
if (node.HitTest(p))
{ {
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