Daniel
Daniel

Reputation: 117

How to select topmost element in a helix-toolkit sharpDX viewport?

I'm working with the sharpDX branch of the helix-toolkit library for a project at my university (HelixToolKit Library)

Currently I'm searching for a way to correctly select elements in a viewport.

I found a helpful example in the source code of the helixtoolkit library:example code on GitHub

public class MyLineGeometryModel3D : LineGeometryModel3D
{
    private Color? initialColor = null;

    public override bool HitTest(Ray rayWS, ref List<HitTestResult> hits)
    {
        if (initialColor == null)
        {
            initialColor = this.Color;
        }

        var result = base.HitTest(rayWS, ref hits);
        var pressedMouseButtons = Viewport3DX.GetPressedMouseButtons();

        if (pressedMouseButtons == 0 || pressedMouseButtons.HasFlag(MouseButtons.Left))
        {
            this.Color = result ? Color.Red : this.initialColor.Value;
        }
        return result;
    }

}

I managed to get this running in my application. However instead of selecting only the topmost element, all elements intersected by the ray are selected. Some kind of handler function is probably needed to highlight the element with the shortest distance?

I was checking some of the standard WPF solutions for that and they often use an eventhandler. ( e.g. 3D Hit Testing in WPF )

private void m_viewport3d_MouseDown(object sender, MouseButtonEventArgs e)
{
    Point mousePos = e.GetPosition(m_viewport3d);
    PointHitTestParameters hitParams = new PointHitTestParameters(mousePos);
    HitTestResult result = VisualTreeHelper.HitTest(m_viewport3d, mousePos);
    RayMeshGeometry3DHitTestResult rayMeshResult = result as RayMeshGeometry3DHitTestResult;
    if (rayMeshResult != null)
    {
        MeshGeometry3D mesh = new MeshGeometry3D();
        mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex1]);
        mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex2]);
        mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex3]);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);
        GeometryModel3D marker = new GeometryModel3D(mesh, new DiffuseMaterial(Brushes.Blue));
        //...add marker to the scene...
    }
}

Is using an eventhandler a sensible solution ? And if yes how to get a ray element for calling the HitTest function in the eventhandler?

Upvotes: 0

Views: 2364

Answers (2)

Daniel
Daniel

Reputation: 117

So, I actually found a solution myself, which is probably not perfect. But maybe this will be of use for somebody.

        private void ViewPort3D_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {

            Ray ray = this.ViewPort3D.UnProject(new Vector2((float)e.GetPosition(ViewPort3D).X, (float)e.GetPosition(ViewPort3D).Y));
            var hits = new List<HitTestResult>();

            // dictionary for connecting the id of the specific element and its distance
            var hitElements = new Dictionary<int, double>();

            // loop over all MeshGeometryModel3D elements
            foreach (var geometry in Geometrys)
            {
                var isHit = geometry.Model3D.HitTest(ray, ref hits);
                if (isHit)
                {
                    hitElements.Add(geometry.Id, hits[hits.Count - 1].Distance);
                }
            }

            if (hits.Count > 0)
            {

                double minDistance = hitElements.First().Value;
                int id_of_hit_element = hitElements.First().Key;

                foreach (var hit in hitElements)
                {
                    if (hit.Value < minDistance)
                    {
                        minDistance = hit.Value;
                        id_of_hit_element = hit.Key;
                    }
                }

                var topElement = Geometrys.Find(geometry => geometry.Id == id_of_hit_element);

                // do something with top element
            }
        }

P.S. Not a computer scientist btw, just a civil engineering student trying his best xD

Upvotes: 1

egse
egse

Reputation: 179

Use the Viewport method FindNearest:

private void OnMouseDown(object sender, MouseButtonEventArgs e)
{     
    Viewport3DX vp = e.Source as Viewport3DX;

    Point3D p;
    Vector3D v;
    Model3D m;
    if (vp.FindNearest(e.GetPosition(vp), out p, out v, out m))
    {
         //Do something with the found object
    }
 }

Upvotes: 2

Related Questions