[FWD] How to retrieve the index of the closest polygon in a mesh to a point?

Dear community,

we received a support request via mail which we thought might also be interesting to the community. This thread captures the question asked in the mail.

The question is "How to retrieve the index of the closest polygon in a mesh to a point?". The question has been asked for the C++ environment, looking for the right optimization data structure provided by the C++ SDK.

Cheers,
Ferdinand

Dear user,

the problem with your example code is that you do use a VoxelizationInterface which is not well suited for this task. The type DistanceQueryInterface(Documentation) is much better suited for the task. You should also be more cautious with statements like the following, taken from your code,

voxelRef = maxon::PolyVoxelization().Create() iferr_ignore("Could not create VoxelizationRef");

as stepping over a failure of initialization will lead to crashes. At the end of this posting you will find an example for how to use DistanceQueryInterface as well as some light comments on error handling.

I hope this helps and cheers,
Ferdinand

/* This function expects a point object (A) to be the first object in the scene, followed by a 
   polygon object (B) as the second object. It will then return the index for closest polygon
   in B for each vertex in A.
*/
static maxon::Result<void> Pc13296(BaseDocument* doc)
{
	iferr_scope;

	// Get the first and second node in the scene. The first one is expected
	// to be a point node, serving as a point source, and the second one is
	// expected to be a polygon node, serving as a polygon source.
	BaseObject* node = doc->GetFirstObject();
	if (node == nullptr || !node->IsInstanceOf(Opoint))
	{
		ApplicationOutput("Please provide a point object as the first node in the scene."_s);
		return maxon::OK;
	}
	PointObject* pointNode = static_cast<PointObject*>(node);

	node = node->GetNext();
	if (node == nullptr || !node->IsInstanceOf(Opolygon))
	{
		ApplicationOutput("Please provide a polygon object as the second node in the scene."_s);
		return maxon::OK;
	}
	PolygonObject* polygonNode = static_cast<PolygonObject*> (node);

	// Get access to the point and polygon data and the global transforms of both nodes.
	const Vector* points = pointNode->GetPointR();
	const CPolygon* polygons = polygonNode->GetPolygonR();
	const int pointCount = pointNode->GetPointCount();
	const int polygonCount = polygonNode->GetPolygonCount();
	Matrix mgPointNode = pointNode->GetMg();
	// We invert it right away, as we only will need it in this form.
	Matrix iMgPolygonNode = ~polygonNode->GetMg();

	// When an error is being raised by Cinema we should be cautious with using iferr_ingore, as
	// then the code will keep running in the current scope. In some cases like manipulating an 
	// array like structure this can be useful. But when an initialization of an entity fails, we
	// almost never want to keep going, as trying to use that not initialized object then will be
	// a sure fire way to crash Cinema 4D. Instead we can either use iferr_return to simply leave 
	// the current scope, use iferr_throw to throw a specific error or handle it manually with
	// iferr(). Below you will find a two examples.

	// Create a reference to the distance query interface.
	maxon::DistanceQueryRef distanceQuery = maxon::DistanceCalculator().Create() iferr_return;

	// Pass true for the second argument to voxelize the input to speed up larger queries. Just as
	// with any optimization, for smaller data sets this voxelization setup might eat up all the
	// performance benefits gained latter on.
	iferr(distanceQuery.Init(polygonNode, false)) {
		ApplicationOutput("Failed to initialize DistanceQueryInterfacae for @", polygonNode->GetName());
		return maxon::OK;
	}

	// Go over all vertices and query them for the closest polygon (id) in the other mesh.
	float distance = 0;
	Vector p;
	for (int poindId = 0; poindId < pointCount; poindId++)
	{
		// The point in the point mesh converted first to global coordinates and then to local
		// coordinates in the polygon node. We have to do that because the points in the point
		// source live in their own coordinate system as well the ones in the polygon source.
		p = mgPointNode * iMgPolygonNode * points[poindId];
		maxon::PrimitiveInformation info;
		distance = distanceQuery.GetClosestMeshPrimitive(p, info);
		ApplicationOutput("Closest polygon id for point id @: @", poindId, info.GetRealPolyIndex());
	}

	return maxon::OK;
}

The output/setup:
642545d3-3ffa-467e-9123-cf0a328b18f1-image.png