BREP ClosestPoint

Hello,

I noticed that the C++ OpenNurbs API does not provide a method to get the closest point to a Brep.

BUT the RhinoCommon C# API does provide this method.
I wonder why this is the case.

What is the most effective way to get the closest point to a Brep?

Thank you!

I noticed that yet another function does not exist in the OpenNurbs, but it does exist in RhinoCommon:
The Transform.PlaneToPlane Method

It seems that the equivalent to this function is the ON_XForm::Rotate method (it has an overload taking 2 ON_Planes)

And I did find a workaround to have access to functions as they are specified in the rhcommon_c.dll file.

You can use the C# code fo find out what is the native methods it is calling. For example, if you navigate to the definition of the Brep.ClosestPoint method, you see this:

        [ConstOperation]
        public bool ClosestPoint(Point3d testPoint, out Point3d closestPoint, out ComponentIndex ci, out double s, out double t, double maximumDistance, out Vector3d normal)
        {
            ci = Rhino.Geometry.ComponentIndex.Unset;
            s = -1.23432101234321E+308;
            t = -1.23432101234321E+308;
            normal = Vector3d.Unset;
            closestPoint = Point3d.Unset;
            return UnsafeNativeMethods.ON_Brep_GetClosestPoint(ConstPointer(), testPoint, ref closestPoint, ref ci, ref s, ref t, maximumDistance, ref normal);
        }

Then, inspecting further (going to the definition of the class UnsafeNativeMethods) we can see:

    [DllImport("rhcommon_c", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.U1)]
    internal static extern bool ON_Brep_GetClosestPoint(IntPtr pBrep, Point3d testPoint, ref Point3d closestPoint, ref ComponentIndex ci, ref double u, ref double v, double maximumDistance, ref Vector3d normal);

This basically gives the signature of a ON_Brep_GetClosestPoint function that is in the rhcommon_c.dll file. With this, I wrote something like this, which is a typedef of a function that matches the signature as we can read in the UnsafeNativeMethods C# side.

typedef BOOL(__stdcall* pON_Brep_GetClosestPoint)(
	ON_Brep* pBrep,
	const ON_3dPoint& testPoint,
	ON_3dPoint* closestPoint,
	ON_COMPONENT_INDEX* ci,
	double* u,
	double* v,
	double maximumDistance,
	ON_3dVector* normal);

Finaly, to call this function in C++, you first need to make sure that the dll is loaded (in my tests sometimes it wasn’t loaded previously by Rhino).

// This is a global variable that contains the pointer to the function in the rhcommon_c.dll
	pON_Brep_GetClosestPoint ON_Brep_GetClosestPoint = nullptr;

// I call this whenever I need to be sure that I can use a rhcommon_c.dll function
// It checks if the module is loaded in the current rhino instance, otherwise it loads.
// This is still BETA - for sure there are improvements.
	void LoadRhCommonC()
	{
		pON_Brep_GetClosestPoint oldPtr = ON_Brep_GetClosestPoint;

		//if (ON_Brep_GetClosestPoint == nullptr)
		//{
			// Gets all loaded modules
			vector<HMODULE> lphModule(1000);
			DWORD lpcbNeeded = 0;

			// Is the library loaded?
			HANDLE hProcess = GetCurrentProcess();
			// Reads the modules of the current process
			BOOL gotAll = EnumProcessModules(hProcess, lphModule.data(), lphModule.size() * sizeof(HMODULE), &lpcbNeeded);
			
			array<WCHAR, 5000> buffer;
			HMODULE rhcommonModule = from(lphModule).firstOrDefault([&](HMODULE mod)
				{
					GetModuleFileName(mod, buffer.data(), 5000);
					wstring s(buffer.data());
					auto lastSlash = s.rfind('\\');
					auto libNamePos = s.find(L"rhcommon_c.dll", lastSlash);
					if (libNamePos == std::wstring::npos) return false;
					else return true;
				});
			
			// Wasn't loaded yet - loads
			if (rhcommonModule == nullptr)
			{
				// Gets the name of the current process 
				GetModuleFileName(NULL, buffer.data(), 5000);
				wstring s(buffer.data());
				boost::filesystem::path rhinoPath(s);
				boost::filesystem::path rhcommon_cPath = rhinoPath.parent_path();
				rhcommon_cPath += boost::filesystem::path("\\rhcommon_c.dll");

				// Loads the module
				rhcommonModule = LoadLibrary(rhcommon_cPath.c_str());
			}

			if (rhcommonModule == nullptr) pagmo_throw(runtime_error, "Module rhcommon_c.dll was not loaded and could not be loaded.");

			auto retFPtr = GetProcAddress(rhcommonModule, "ON_Brep_GetClosestPoint");
			if (rhcommonModule == nullptr) pagmo_throw(runtime_error, "Could not find function called ON_Brep_GetClosestPoint in module rhcommon_c.dll.");
			ON_Brep_GetClosestPoint = (pON_Brep_GetClosestPoint)retFPtr;

			int a = 0;
			a++;
		//}
	}

Then, to call the function you can do something like this:

		ON_3dPoint closestPoint;
		ON_COMPONENT_INDEX ci;
		double u, v;
		ON_3dVector normal;

		// Tests the C function
		bool ret = ON_Brep_GetClosestPoint(inBrep, inPoint, &closestPoint, &ci, &u, &v, 1000, &normal);

		return closestPoint;

If you require access to Rhino functionality from outside of Rhino, and your are writing in .NET, then consider using Rhino.Inside.

— Dale

1 Like

Hi @dale ,

Thank you for your reply. It is clear to me that the opennurbs public that links to the opennurbs_public.dll does not expose some methods (such as distance calculations). But the topic is different. The issue is that the opennurbs that is inside Rhino (the one that is called plus in the header files and that links to the opennurbs.dll) does expose these members - I can use the distance calculations in my C++ code.

The issue I have is that the ON_Brep does not expose the GetClosestPoint method (see documentation). We have this method in the other classes such as ON_Surface and the ON_Mesh.

But the method ON_Brep_GetClosestPoint is only exposed in the rhcommon_c.dll file, which I suppose contains the wrappers that allow you to Marshal the calls from the .NET managed side into the native C++.

I did find a way to infer its signature and to dynamically link to the rhcommon_c.dll. It works.

But, from the user side of the API, it would be an improvement to have all the functionality that you have in the rhcommon_c.dll to be also available to people using the opennurbs api from C++. Either by putting them all in the opennurbs (the plus version) or by sharing the headers and the lib of the rhcommon_c.dll so that people can link to it.

The way we have it now (correct me if I am wrong - that was mainly my question :slight_smile: ) is that C++ plugin developers do not have the ON_Brep_GetClosestPoint function without all the workaround I made.

An interesting note is that even the description of the Brep.ClosestPoint does kind of signal that it is actually making some other ClosestPoint checks under the hood:
Finds a point on the Brep that is closest to testPoint. The method searches all Brep faces looking for the one closest to testPoint. When found, if the closest point falls on the inactive region of the face, then the method finds the face’s edge that is closest to testPoint.

Thank you so much and I wish you a great sunday - and I am sorry for the nuisance.

Hi @scudelari,

The functionality of Brep.ClosestPoint is provided by the RhinoBrepClosestPoint C++ SDK function. See rhinoSdkUtilitites.h for details.

— Dale

A post was split to a new topic: Rhino SDK and P/Invoke