How-to: Finding the nearest feature to a point (HPS)

The following sample code demonstrates two methods to find the closest feature to a 3D point in the scene using HOOPS Visualize (HPS). This code can be copied and pasted into the wpf_sandbox project (DemoUserCommands.cs) included in your HPS package.

Note: this sample code requires HOOPS Exchange as well and references a model provided in the Exchange download package. This was built with HNP 2024.


In the first method, a small simple sphere is defined and inserted into the scene. SelectByShell() is used to return all selections within that sphere. If this returns nothing, the sphere is deleted and a new, slightly larger sphere is created – this process repeats until a selection result is returned.


HPS.View myView = GetCanvas().GetAttachedLayout().GetFrontView();

myView.AttachModel(myModel);


// Insert a sample model to test:

try
{
	string file_in = "<ExchangeInstallDirectory>/samples/data/pmi/PMI_Sample/CV5_Sample.CATPart";

	HPS.Exchange.ImportOptionsKit iok = new HPS.Exchange.ImportOptionsKit();
	iok.SetBRepMode(HPS.Exchange.BRepMode.BRepAndTessellation);

	HPS.Exchange.ImportNotifier exchangeNotifier = HPS.Exchange.File.Import(file_in, iok);
	exchangeNotifier.Wait();

	HPS.Exchange.CADModel modelFile = exchangeNotifier.GetCADModel();
	
	myModel = modelFile.GetModel();
	myView.AttachModel(myModel);

	HPS.SegmentKey myModelKey = myModel.GetSegmentKey();

	myModelKey.InsertDistantLight(new HPS.Vector(1, 1, -1));


	// Given a point in space, find the closest feature by iteratively increasing the size of a sphere
	// with its center at that point until it hits a feature, then highlight that feature:
	HPS.Point pointToTest = new HPS.Point(10, 0, 50);

	bool isHit = false;
	int upperBound = 1000; // Max distance the point can be from the model we're checking against
	int i = 0;

	while (!isHit && i<upperBound)
	{
		// Define the sphere we will use to perform a shell selection (collision check)
		HPS.SphereKit mySphereKit= new HPS.SphereKit();
		float radius = (float) (i + 1) / 2;
		mySphereKit.SetCenter(pointToTest);
		mySphereKit.SetRadius(radius);
		mySphereKit.SetBasis(new HPS.Vector(0, 1, 0), new HPS.Vector(1, 0, 0)); // "axis" and "up" vectors

		System.Diagnostics.Debug.Write("Radius:");
		System.Diagnostics.Debug.WriteLine(radius);
		
		// Create a reference sphere 
		HPS.SegmentKey referenceSphereSegKey = HPS.Database.CreateRootSegment();
		HPS.SphereKey mySphereKey = referenceSphereSegKey.InsertSphere(mySphereKit);

		// Insert the reference sphere into the scene
		HPS.SegmentKey sphereSubSeg = myModelKey.Subsegment();
		HPS.ShellKey mySphereShell = sphereSubSeg.InsertShellFromGeometry(mySphereKey);

		
		// Perform a select by shell selection:
		HPS.SelectionOptionsKit selectionOptions = new HPS.SelectionOptionsKit();
		selectionOptions.SetAlgorithm(HPS.Selection.Algorithm.Analytic);
		selectionOptions.SetLevel(HPS.Selection.Level.Subentity);
		selectionOptions.SetProximity(0);
		selectionOptions.SetScope(myModelKey);
		
		HPS.SelectionResults selectionResults;
		ulong numSelectedItems = GetCanvas().GetWindowKey().GetSelectionControl().SelectByShell(mySphereShell, selectionOptions, out selectionResults);

		HPS.SelectionResultsIterator srIterator = selectionResults.GetIterator();

		if (!srIterator.IsValid()) // If nothing is returned
		{
			System.Diagnostics.Debug.WriteLine("miss");
			referenceSphereSegKey.Delete(); // Delete the offscreen root segment and its subsegments
			sphereSubSeg.Delete(); // Delete the branch containing the sphere
			i++;

		}
		else
		{
			isHit = true;

			var highlightOptions = new HighlightOptionsKit("highlight_style");
			highlightOptions.SetOverlay(Drawing.Overlay.InPlace);

			while (srIterator.IsValid())
			{
				HPS.SelectionItem selectionItem = srIterator.GetItem();

				var componentPath = modelFile.GetComponentPath(selectionItem);
				if (!componentPath.Empty())
				{
					highlightOptions.SetNotification(true);
					componentPath.Highlight(GetCanvas(), highlightOptions);
				}
				
				srIterator.Next();
			}
			
		}
		myView.FitWorld();
		GetCanvas().Update();
	}


}
catch (HPS.IOException ioe)
{
	System.Diagnostics.Debug.WriteLine(ioe.Message);
}


Sphere shown for reference - query point is at center of sphere. The highlighted face is the closest feature to the point.


In the second method, SelectByVolume() is used with a similar process. However, this method only allows for cuboid selection geometry, so it will not return a result as precise as a method using a sphere.


HPS.View myView = GetCanvas().GetAttachedLayout().GetFrontView();

myView.AttachModel(myModel);


// Insert a sample model to test:

try
{
	string file_in = "<ExchangeInstallDirectory>/samples/data/pmi/PMI_Sample/CV5_Sample.CATPart";

	HPS.Exchange.ImportOptionsKit iok = new HPS.Exchange.ImportOptionsKit();
	iok.SetBRepMode(HPS.Exchange.BRepMode.BRepAndTessellation);

	HPS.Exchange.ImportNotifier exchangeNotifier = HPS.Exchange.File.Import(file_in, iok);
	exchangeNotifier.Wait();

	HPS.Exchange.CADModel modelFile = exchangeNotifier.GetCADModel();

	myModel = modelFile.GetModel();
	myView.AttachModel(myModel);

	HPS.SegmentKey myModelKey = myModel.GetSegmentKey();

	myModelKey.InsertDistantLight(new HPS.Vector(1, 1, -1));


	// Given a point in space, find the closest feature by iteratively increasing the size of a cuboid volume
	// with its center at that point until it hits a feature, then highlight that feature:
	HPS.Point pointToTest = new HPS.Point(35, 0, 30);

	bool isHit = false;
	int upperBound = 1000; // Max distance the point can be from the model we're checking against
	int i = 1;

	while (!isHit && i < upperBound)
	{
		// Define the cuboid for volumetric selection
		HPS.SimpleCuboid boundingCuboid = new HPS.SimpleCuboid(new HPS.Point(-i + pointToTest.x, -i + pointToTest.y, -i + pointToTest.z), new HPS.Point(i + pointToTest.x, i + pointToTest.y, i + pointToTest.z));

		System.Diagnostics.Debug.Write("Length: ");
		System.Diagnostics.Debug.WriteLine(i*2);

		// Perform a select by volume selection:
		HPS.SelectionOptionsKit selectionOptions = new HPS.SelectionOptionsKit();
		selectionOptions.SetAlgorithm(HPS.Selection.Algorithm.Analytic);
		selectionOptions.SetLevel(HPS.Selection.Level.Subentity);
		selectionOptions.SetScope(myModelKey);


		HPS.SelectionResults selectionResults;
		ulong numSelectedItems = GetCanvas().GetWindowKey().GetSelectionControl().SelectByVolume(boundingCuboid, selectionOptions, out selectionResults);

		HPS.SelectionResultsIterator srIterator = selectionResults.GetIterator();

		if (!srIterator.IsValid()) // If nothing is returned
		{
			System.Diagnostics.Debug.WriteLine("miss");
			i++;

		}
		else
		{
			isHit = true;

			var highlightOptions = new HighlightOptionsKit("highlight_style");
			highlightOptions.SetOverlay(Drawing.Overlay.InPlace);
		

			while (srIterator.IsValid())
			{
				HPS.SelectionItem selectionItem = srIterator.GetItem();

				var componentPath = modelFile.GetComponentPath(selectionItem);
				if (!componentPath.Empty())
				{
					highlightOptions.SetNotification(true);
					componentPath.Highlight(GetCanvas(), highlightOptions);
				}

				srIterator.Next();
			}

			// Insert the cube for reference:
			HPS.ShellKit myShellKit = new HPS.ShellKit();
			HPS.Point[] shellPoints = {
				new HPS.Point(-i + pointToTest.x, -i + pointToTest.y, -i + pointToTest.z),
				new HPS.Point(i + pointToTest.x, -i + pointToTest.y, -i + pointToTest.z),
				new HPS.Point(i + pointToTest.x, i + pointToTest.y, -i + pointToTest.z),
				new HPS.Point(-i + pointToTest.x, i + pointToTest.y, -i + pointToTest.z),
				new HPS.Point(-i + pointToTest.x, -i + pointToTest.y, i + pointToTest.z),
				new HPS.Point(i + pointToTest.x, -i + pointToTest.y, i + pointToTest.z),
				new HPS.Point(i + pointToTest.x, i + pointToTest.y, i + pointToTest.z),
				new HPS.Point(-i + pointToTest.x, i + pointToTest.y, i + pointToTest.z)
			};
			int[] shellFaceList = {
				4, 0, 1, 2, 3,
				4, 1, 5, 6, 2,
				4, 5, 4, 7, 6,
				4, 4, 0, 3, 7,
				4, 3, 2, 6, 7,
				4, 0, 4, 5, 1
			};

			myShellKit.SetPoints(shellPoints);
			myShellKit.SetFacelist(shellFaceList);
			HPS.SegmentKey myShellSegKey = myModelKey.Subsegment();
			HPS.ShellKey cubeShellKey = myShellSegKey.InsertShell(myShellKit);

		}

		myView.FitWorld();
		GetCanvas().Update();
	}


}
catch (HPS.IOException ioe)
{
	System.Diagnostics.Debug.WriteLine(ioe.Message);
}


Cube shown for reference - query point is at center of cube. The highlighted face is the closest feature to the point.

2 Likes

Thanks for sharing this Beau!

1 Like