private void UpdateClosestLine() { if (_lineSelectorData == null || _lineSelectorData.Count == 0) { return; } if (_isCameraChanged) { // Each time camera is changed, we need to call CalculateScreenSpacePositions method. // This will update the 2D screen positions of the 3D lines. // IMPORTANT: // Before calling CalculateScreenSpacePositions it is highly recommended to call Refresh method on the camera. Camera1.Refresh(); if (MultiThreadedCheckBox.IsChecked ?? false) { // This code demonstrates how to use call CalculateScreenSpacePositions from multiple threads. // This significantly improves performance when many 3D lines are used (thousands) // but is not needed when using only a few lines (as in this demo). // // When calling CalculateScreenSpacePositions we need to prepare all the data // from WPF properties before calling the method because those properties // are not accessible from the other thread. // We need worldToViewportMatrix and // the _lineSelectorData[i].Camera and _lineSelectorData[i].UsedLineThickness need to be set // (in this sample they are set in the LineSelectorData constructor). var worldToViewportMatrix = new Matrix3D(); bool isWorldToViewportMatrixValid = Camera1.GetWorldToViewportMatrix(ref worldToViewportMatrix, forceMatrixRefresh: false); if (isWorldToViewportMatrixValid) { Parallel.For(0, _lineSelectorData.Count, i => _lineSelectorData[i].CalculateScreenSpacePositions(ref worldToViewportMatrix, transform: null)); } } else { for (var i = 0; i < _lineSelectorData.Count; i++) { _lineSelectorData[i].CalculateScreenSpacePositions(Camera1); } } _isCameraChanged = false; } // Now we can call the GetClosestDistance method. // This method calculates the closest distance from the _lastMousePosition to the line that was used to create the LineSelectorData. // GetClosestDistance also sets the LastDistance, LastLinePositionIndex properties on the LineSelectorData. if (MultiThreadedCheckBox.IsChecked ?? false) { Parallel.For(0, _lineSelectorData.Count, i => _lineSelectorData[i].GetClosestDistance(_lastMousePosition)); } else { for (var i = 0; i < _lineSelectorData.Count; i++) { _lineSelectorData[i].GetClosestDistance(_lastMousePosition); } } // Get the closest line IEnumerable <LineSelectorData> usedLineSelectors; // If we limit the distance of the specified position to the line, then we can filter all the line with Where if (_maxSelectionDistance >= 0) { usedLineSelectors = _lineSelectorData.Where(l => l.LastDistance <= _maxSelectionDistance).ToList(); } else { usedLineSelectors = _lineSelectorData; } List <LineSelectorData> orderedLineSelectors; if (OrderByDistanceCheckBox.IsChecked ?? false) { // Order by camera distance orderedLineSelectors = usedLineSelectors.OrderBy(l => l.LastDistanceFromCamera).ToList(); } else { // Order by distance to the specified position orderedLineSelectors = usedLineSelectors.OrderBy(l => l.LastDistance).ToList(); } // Get the closest LineSelectorData LineSelectorData closestLineSelector; if (orderedLineSelectors.Count > 0) { closestLineSelector = orderedLineSelectors[0]; } else { closestLineSelector = null; } // It is possible to get the positions of the line segment that is closest to the mouse position //var closestPolyLine = (PolyLineVisual3D)closestLineSelector.LineVisual; //Point3D firstSegmentPosition = closestPolyLine.Positions[closestLineSelector.LastLinePositionIndex]; //Point3D secondSegmentPosition = closestPolyLine.Positions[closestLineSelector.LastLinePositionIndex + 1]; // To get the actual position on the line that is closest to the mouse position, use the LastClosestPositionOnLine //closestLineSelector.LastClosestPositionOnLine; // The closest position on the line is shown with a SphereVisual3D if (_closestPositionSphereVisual3D == null) { _closestPositionSphereVisual3D = new SphereVisual3D() { Radius = 2, Material = new DiffuseMaterial(Brushes.Red) }; MainViewport.Children.Add(_closestPositionSphereVisual3D); } if (closestLineSelector == null) { ClosestDistanceValue.Text = ""; LineSegmentIndexValue.Text = ""; _closestPositionSphereVisual3D.IsVisible = false; } else { ClosestDistanceValue.Text = string.Format("{0:0.0}", closestLineSelector.LastDistance); LineSegmentIndexValue.Text = closestLineSelector.LastLinePositionIndex.ToString(); _closestPositionSphereVisual3D.CenterPosition = closestLineSelector.LastClosestPositionOnLine; _closestPositionSphereVisual3D.IsVisible = true; } // Show the closest line as red if (!ReferenceEquals(_lastSelectedLineSelector, closestLineSelector)) { if (_lastSelectedLineSelector != null) { _lastSelectedLineSelector.LineVisual3D.LineColor = _savedLineColor; } if (closestLineSelector != null) { _savedLineColor = closestLineSelector.LineVisual3D.LineColor; closestLineSelector.LineVisual3D.LineColor = Colors.Red; } _lastSelectedLineSelector = closestLineSelector; } }