/// <summary> /// For each mesh object in the viewport, this method calculates the vertex that when mapped to 2D is closest to the given 2D point on the screen /// </summary> /// <param name="pointToHitTest"> The point in screen coordinates to calculate the closest vertices for. </param> /// <returns> A ClosestVertexResult containing the distance to the 2D point and the closest 3D vertex point for each geometry/model </returns> public IEnumerable <ClosestVertexResult> FindClosestHits(Point pointToHitTest) { ProjectionCamera camera = mViewPort3D.Camera as ProjectionCamera; if (camera == null) { throw new InvalidOperationException("No projection camera defined. Cannot find rectangle hits."); } List <ClosestVertexResult> results = new List <ClosestVertexResult>(); mViewPort3D.Children.Traverse <GeometryModel3D>( (model, transform) => { MeshGeometry3D geometry = model.Geometry as MeshGeometry3D; if (geometry == null || geometry.Positions == null || geometry.TriangleIndices == null) { return; } Point3D[] point3Ds; if (geometry.Positions.Count <= mMaximumVerticesPerMesh) { point3Ds = geometry.Positions.Select(transform.Transform).ToArray(); } else { Rect3D bounds = geometry.Bounds; point3Ds = GetBoundaryPointsBoundingBox(bounds).Select(transform.Transform).ToArray(); } // transform the positions of the mesh to screen coordinates Point[] point2Ds = mViewPort3D.Point3DtoPoint2D(point3Ds).ToArray(); double minSquaredDistanceToPoint = double.PositiveInfinity; Point3D closestPoint = point3Ds[0]; Point closestPointIn2D = point2Ds[0]; for (int i = 0; i < point2Ds.Length; i++) { Point point = point2Ds[i]; double xDiff = point.X - pointToHitTest.X; double yDiff = point.Y - pointToHitTest.Y; double squaredDistance = xDiff * xDiff + yDiff * yDiff; if (squaredDistance < minSquaredDistanceToPoint) { minSquaredDistanceToPoint = squaredDistance; closestPoint = point3Ds[i]; closestPointIn2D = point; } } ClosestVertexResult hitTestResult = new ClosestVertexResult(model, geometry, closestPoint, closestPointIn2D, Math.Sqrt(minSquaredDistanceToPoint)); results.Add(hitTestResult); }); return(results); }
public ClosestVertexResult ResultOfClosestPointHit2D(Point position) { double minX = 0; double minY = 0; double maxX = mViewPort3D.ActualWidth; double maxY = mViewPort3D.ActualHeight; IEnumerable <ClosestVertexResult> hitsInView = FindClosestHits(position).Where(result => result.ClosestPointIn2D.X >= minX && result.ClosestPointIn2D.X <= maxX && result.ClosestPointIn2D.Y >= minY && result.ClosestPointIn2D.Y <= maxY); IOrderedEnumerable <ClosestVertexResult> closestHits = hitsInView.OrderBy(x => x.DistanceToPoint2D); //Return the closest valid result, or null in case no valid result was found. ClosestVertexResult closestHitInView = closestHits.FirstOrDefault(); return(closestHitInView); }
/// <summary> /// Returns the 3D point that the user most likely clicked on. /// Returns the closest intersection with a mesh, if possible. /// If useClosestMeshIfPossible is true it will otherwise returns the mesh vertex that is closest to the clicked 2D screen coordinate, if possible. /// Returns the unprojected position otherwise. /// Also returns the 2D point corresponding to the returned 3D point. /// </summary> /// <param name="position"> The point in screen coordinates to calculate the closest vertices for. </param> /// <param name="useClosestMeshIfPossible"> When true the closest vertex point to the clicked position will be used when no mesh is hit at the exact mouse cursor area.</param> public NearestPointInCamera CalculateMouseDownNearestPoint(Point position, bool useClosestMeshIfPossible) { if (mViewPort3D.FindNearest(position, out Point3D nearestPoint, out _, out _)) { return(new NearestPointInCamera(position, nearestPoint)); } if (useClosestMeshIfPossible) { ClosestVertexResult closestResult = ResultOfClosestPointHit2D(position); if (closestResult != null) { return(new NearestPointInCamera(closestResult.ClosestPointIn2D, closestResult.ClosestPoint)); } } Point3D?unprojectedPosition = mViewPort3D.UnProject(position); return(new NearestPointInCamera(position, unprojectedPosition)); }