WPF: OnRender и тестирование хитов - PullRequest
0 голосов
/ 21 мая 2010

при использовании OnRender для рисования чего-либо на экране, есть ли способ выполнить тестирование удара на нарисованной графике?

Пример кода

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        drawingContext.DrawRectangle(Brushes.Black, null, new Rect(50, 50, 100, 100));
    }

Очевидно, что никто не ссылается на нарисованный прямоугольник, который был бы необходим для проверки удара, или я ошибаюсь? Я знаю, что могу использовать DrawingVisual, мне просто любопытно, правильно ли я понимаю, что, используя OnRender для рисования чего-либо, вы не можете выполнить какое-либо тестирование удара по нарисованным вещам?

Ответы [ 3 ]

0 голосов
/ 07 ноября 2015

Мое быстрое решение. Это не идеально во многих отношениях, но это работает для меня как начало. Может быть легко реорганизован для повышения производительности, если это станет необходимым.

    /// <summary>
    /// Provides basic hit testing
    /// </summary>
    public class RadarHitTestUtility
    {
        public Dictionary<Tuple<int,int>, HitContainer> HitStorage = new Dictionary<Tuple<int, int>, HitContainer>();

        public void AddEllipse(RadarObject radarObject, Point point, double x, double y)
        {
            // Geometry used for hit testing
            var elipse = new EllipseGeometry
            {
                Center = point,
                RadiusX = x,
                RadiusY = y,
            };

            var key = new Tuple<int, int>((int)point.X, (int)point.Y);

            var hit = new HitContainer
            {
                Geometry = elipse,
                Bounds = elipse.Bounds,
                RadarObject = radarObject
            };

            if(!HitStorage.ContainsKey(key))
                HitStorage.Add(key, hit);
        }

        /// <summary>
        /// Gets first radar object whose center point is within distance of point.
        /// </summary>
        public HitContainer GetSimpleHit(Point point, int distance = 5)
        {
            foreach (var hit in HitStorage)
            {
                if (Math.Abs(hit.Key.Item1 - point.X) <= distance && Math.Abs(hit.Key.Item2 - point.Y) <= distance)
                {
                    hit.Value.Intersection = IntersectionDetail.NotCalculated;
                    return hit.Value;
                }
            }
            return default(HitContainer);
        }

        /// <summary>
        /// Gets first radar object that contains point
        /// </summary>
        public HitContainer GetHit(Point point)
        {
            var PreCheckDistance = 50;

            foreach (var hit in HitStorage)
            {
                if (Math.Abs(hit.Key.Item1 - point.X) <= PreCheckDistance && Math.Abs(hit.Key.Item2 - point.Y) <= PreCheckDistance)
                {
                    if (hit.Value.Geometry.FillContains(point))
                    {
                        hit.Value.Intersection = IntersectionDetail.FullyInside;
                        return hit.Value;
                    }                    
                }
            }
            return default(HitContainer);
        }

        /// <summary>
        /// Gets first radar object that intersects with geometry
        /// </summary>
        public HitContainer GetHit(Geometry geometry)
        {
            var PreCheckDistance = 50;
            var GeometryCheckTolerance = 1;
            var center = geometry.Bounds.Center();

            foreach (var hit in HitStorage)
            {
                if (Math.Abs(hit.Key.Item1 - center.X) <= PreCheckDistance && Math.Abs(hit.Key.Item2 - center.Y) <= PreCheckDistance)
                {
                    var result = hit.Value.Geometry.FillContainsWithDetail(geometry, GeometryCheckTolerance, ToleranceType.Absolute);
                    if (result != IntersectionDetail.Empty)
                    {
                        hit.Value.Intersection = result;
                        return hit.Value;
                    }
                }
            }
            return default(HitContainer);
        }

        public class HitContainer
        {
            public RadarObject RadarObject { get; set; }
            public Geometry Geometry { get; set; }
            public Rect Bounds { get; set; }
            public IntersectionDetail Intersection { get; set; }
        }

        public void Clear()
        {
            HitStorage.Clear();
        }
    }

Объявите экземпляр в вашем контроле

public RadarHitTestUtility HitTester = new RadarHitTestUtility();

Вам нужно будет вызвать Clear () в какой-то момент, чтобы в коллекции тестов на попадание существовали только текущие объекты.

HitTester.Clear();

В OnRender (), когда вы хотите, чтобы вызов отрисовки записывался в целях тестирования удара, вы можете записать его с помощью AddEllipse ()

HitTester.AddEllipse(radarObject, radarObject.Point, actorRadius, actorRadius);

Вот как я призываю к проверке попадания

    private void MouseDownHandler(object sender, MouseButtonEventArgs e)
    {
        IsMouseDown = true;
        Cursor = Cursors.Hand;
        DragInitialPosition = Mouse.GetPosition(this);
        DragInitialPanOffset = CanvasData.PanOffset;

        var hit = FindElementUnderClick(sender, e);
        if (hit != null)
        {
            SelectedItem = hit.RadarObject.Actor;
        }            
    }

    private RadarHitTestUtility.HitContainer FindElementUnderClick(object sender, MouseEventArgs e)
    {
        var position = e.GetPosition((UIElement)sender);            
        return HitTester.GetHit(position);
    }

Если вы хотите иметь возможность тестировать формы, отличные от эллипса, вы просто добавляете соответствующий метод записи в RadarHitTestUtility и вызываете его, создавая другой тип геометрии для тестирования.

0 голосов
/ 03 марта 2018

Если я полностью не понимаю вопрос ОП, ответ - ДА, тест на удар основан на том, что было нарисовано во время OnRender. Вы можете сказать, переопределив OnMouseEnter в том же классе, в котором вы переопределяете OnRender. В переопределении OnMouseEnter попробуйте это:

var htr = VisualTreeHelper.HitTest(this, e.GetPosition(this));
if (htr != null)
     System.Diagnostics.Debug.WriteLine("It's a hit!");

Теперь я замечаю, что вы все еще звоните base.OnRender, что говорит о том, что вы пытаетесь нарисовать на вершине то, что в противном случае Visual отрисовывает. То, что вы не сможете сделать, это отличить попадание родительского класса от визуализированного в переопределении. Однако, если вам это нужно, просто держите свой рисунок в своем собственном Visual.

0 голосов
/ 22 мая 2010

Вы правильно поняли, что нет никакого способа сделать проверку на графике, которую вы нарисовали, используя методы DrawingContext, потому что они не представлены как объекты в визуальном дереве.

...