How do I calculate the surface area on a solid using C++?

Hi All,
In one example calculate the solid volumes.

How do I calculate the surface area on a solid ?

CRhinoCommand::result CCommandTest::RunCommand(const CRhinoCommandContext& context)
{
        CRhinoGetObject go;
        go.SetCommandPrompt(L"Select surface or polysurface");
        go.EnableSubObjectSelect(FALSE);
        go.EnableGroupSelect(TRUE);
        go.GetObjects(1, 0);
        if (go.CommandResult() != CRhinoCommand::success)	return go.CommandResult();
	for (int i = 0; i < go.ObjectCount(); i++)
	{
		const CRhinoObjRef& ref = go.Object(i);
		const CRhinoObject* pObject = ref.Object();
		if (0 == pObject)	continue;
		ON_SimpleArray<ON_COMPONENT_INDEX> subidxs;
		pObject->GetSelectedSubObjects(subidxs);
		for (int su = 0; su < subidxs.Count(); su++) {
			if (subidxs[su].m_type == ON_COMPONENT_INDEX::brep_face) {
				if (const ON_Surface* srf = ON_Surface::Cast(pObject->Geometry())) {
					ON_SimpleArray<ON_MassProperties> MassProp(1);
					ON_MassProperties* mp = &MassProp.AppendNew();
					srf->AreaMassProperties(*mp, true, false, false, false);
					::RhinoApp().Print(L"Object[%d] Area[%d] = %f\n", i, su, mp->Area());
				}
			}
		}
	}
	return CRhinoCommand::success;
}

How ?

– Karlos

Get the solid as ON_Brep and use its member function AreaMassProperties.

https://developer.rhino3d.com/api/cpp/class_o_n___brep.html#ab1a4d17b895d22868cd2ee1c9dddab77

Hi Menno.

CRhinoCommand::result CCommandTest::RunCommand(const CRhinoCommandContext& context)
{
	CRhinoGetObject go;
	go.EnablePreSelect();
	go.SetCommandPrompt(L"Select surface or polysurface");
	go.SetGeometryFilter(CRhinoGetObject::surface_object | CRhinoGetObject::polysrf_object);
	go.EnableSubObjectSelect(FALSE);
	go.EnableGroupSelect(TRUE);
	go.GetObjects(1, 0);
	if (go.CommandResult() != CRhinoCommand::success)	return go.CommandResult();
	ON_SimpleArray<const ON_Brep*> Breps;
	for (int i = 0; i < go.ObjectCount(); i++)
	{
		::RhinoApp().ActiveDoc()->UnselectAll();
		const CRhinoObjRef& ref = go.Object(i);
		const CRhinoObject* obj = ref.Object();
		const ON_Brep* brep = ref.Brep();
		if (!obj | !brep)	continue;
		Breps.Append(brep);
	}
	int Bn = 0;
	Bn = Breps.Count();
	if (Bn > 0)
	{
		::RhinoApp().Print(L"for (int j = 0; j < Breps.Count(%d); j++)\t\n\{\n", Bn);
		for (int j = 0; j < Bn; j++)
		{
			const ON_Brep* brep = Breps[j];
			if (brep)
			{
				if (brep->m_S.Count() > 0)
				{
					int n = 0;
					n = brep->m_S.Count();
					::RhinoApp().Print(L"\tfor (int k = 0; k < brep->m_S.Count(%d); k++)\t( Brep[%d] )\n\t\{\n", n, j);
					for (int k = 0; k < n; k++) {
						::RhinoApp().Print(L"\t\tSurface[%d]", k);
						const ON_Surface* brep_srf = brep->m_S[k];
						if (!brep_srf) continue;
						ON_Surface* dup_brep_srf = brep->m_S[k]->DuplicateSurface();
						if (!dup_brep_srf) continue;
						if (dup_brep_srf->IsCone())		::RhinoApp().Print(L"\tIsCone()\t");
						if (dup_brep_srf->IsCylinder())	::RhinoApp().Print(L"\tIsCylinder()\t");
						if (dup_brep_srf->IsPlanar())	::RhinoApp().Print(L"\tIsPlanar()\t");
						if (dup_brep_srf->IsSolid())	::RhinoApp().Print(L"\tIsSolid()\t");
						if (dup_brep_srf->IsSphere())	::RhinoApp().Print(L"\tIsSphere()\t");
						if (dup_brep_srf->IsTorus())	::RhinoApp().Print(L"\tIsTorus()\t");
						if (brep_srf) {
							::RhinoApp().Print(L"\tbrep[%d] m_S[%d]\t", j, k);
							ON_SimpleArray<ON_MassProperties> MassProp(1);
							ON_MassProperties* mp = &MassProp.AppendNew();
							if (brep_srf->AreaMassProperties(*mp, true, false, false, false))
							{
								::RhinoApp().Print(L"\tArea[%d] = %f\n", k, mp->Area());
								CRhinoSurfaceObject* srf_obj = new CRhinoSurfaceObject();
								srf_obj->SetSurface(dup_brep_srf);
								if (context.m_doc.AddObject(srf_obj))
									context.m_doc.Redraw();
								else
								{
									delete srf_obj;
									srf_obj = NULL;
								}
							}
						}
					}
				}
			}
			::RhinoApp().Print(L"\t\}\n");
		}
		::RhinoApp().Print(L"\}\n");
	}
	return CRhinoCommand::success;
}

why does the area of a planar surface read the surface of a rectangle ?

?
– Karlos

What you’re doing is getting the underlying surfaces from the m_S array. These surfaces are not trimmed, and form part of the ON_Brep data structure. More info on that data structure can be found here https://developer.rhino3d.com/guides/cpp/brep-data-structure/

What you should do is when you loop over the selected BReps, is calculate the mass properties on the whole thing, not each surface:

// these need to come from the document or you need to choose an absolute and a relative tolerance
double abs_tolerance = TODO; 
double rel_tolerance = TODO;

for (int j = 0; j < Bn; j++)
{
  const ON_Brep* brep = Breps[j];
  if (brep)
  {
    ON_MassProperties mp;
    // get the area-mass-properties for the whole b-rep:
    if (brep->AreaMassProperties(mp, true, false, false, false, abs_tolerance, rel_tolerance))
    {
       // do stuff
    }
  }
}
CRhinoCommand::result CCommandTest::RunCommand(const CRhinoCommandContext& context)
{
	CRhinoGetObject go;
	go.EnablePreSelect();
	go.SetCommandPrompt(L"Select surface or polysurface");
	go.SetGeometryFilter(CRhinoGetObject::surface_object | CRhinoGetObject::polysrf_object);
	go.EnableSubObjectSelect(FALSE);
	go.EnableGroupSelect(TRUE);
	go.GetObjects(1, 0);
	if (go.CommandResult() != CRhinoCommand::success)	return go.CommandResult();
	ON_SimpleArray<const ON_Brep*> Breps;
	for (int i = 0; i < go.ObjectCount(); i++)
	{
		::RhinoApp().ActiveDoc()->UnselectAll();
		const CRhinoObjRef& ref = go.Object(i);
		const CRhinoObject* obj = ref.Object();
		const ON_Brep* brep = ref.Brep();
		if (!obj | !brep)	continue;
		Breps.Append(brep);
	}
	int Bn = 0;
	Bn = Breps.Count();
	if (Bn > 0)
	{
		::RhinoApp().Print(L"for (int j = 0; j < Breps.Count(%d); j++)\t\n\{\n", Bn);
		// these need to come from the document or you need to choose an absolute and a relative tolerance
		double abs_tolerance = 1.0e-6;
		double rel_tolerance = 1.0e-6;
		for (int j = 0; j < Bn; j++)
		{
			const ON_Brep* brep = Breps[j];
			if (brep)
			{
				//Given a brep and a face index
				const int face_index = 0;
				
				const ON_BrepFace* face = brep->Face(face_index);
				if (0 == face)
					return CRhinoCommand::failure;

				int BFn = brep->m_F.Count();
				::RhinoApp().Print(L"\tBFn[%d]\n", BFn);

				if (brep->m_F.Count() > 0)
				{
					::RhinoApp().Print(L"\tfor (int fi = 0; fi < brep->m_F.Count(%d); fi++)\t( Brep[%d] )\n\t\{\n", brep->m_F.Count(), j);
					for (int fi = 0; fi < brep->m_F.Count(); fi++)
					{
						::RhinoApp().Print(L"\t\tSurface[%d]", fi);
						ON_MassProperties mp_F;
						// get the area-mass-properties for the whole b-rep:
						if (brep->m_F[fi].AreaMassProperties(mp_F, true, false, false, false, abs_tolerance, rel_tolerance))
						{
							ON_Surface* dup_brep_srf = brep->m_F[fi].DuplicateSurface();
							if (!dup_brep_srf) continue;
							if (dup_brep_srf->IsCone())		::RhinoApp().Print(L"\tIsCone()\t");
							if (dup_brep_srf->IsCylinder())	::RhinoApp().Print(L"\tIsCylinder()\t");
							if (dup_brep_srf->IsPlanar())	::RhinoApp().Print(L"\tIsPlanar()\t");
							if (dup_brep_srf->IsSolid())	::RhinoApp().Print(L"\tIsSolid()\t");
							if (dup_brep_srf->IsSphere())	::RhinoApp().Print(L"\tIsSphere()\t");
							if (dup_brep_srf->IsTorus())	::RhinoApp().Print(L"\tIsTorus()\t");
							::RhinoApp().Print(L"\tbrep[%d] m_F[%d]\t\tArea[%d] = %f\n", j, fi, fi, mp_F.Area());
							CRhinoSurfaceObject* srf_obj = new CRhinoSurfaceObject();
							srf_obj->SetSurface(dup_brep_srf);
							if (context.m_doc.AddObject(srf_obj))
								context.m_doc.Redraw();
							else
							{
								delete srf_obj;
								srf_obj = NULL;
							}
						}
					}
				}
			}
			::RhinoApp().Print(L"\t\}\n");
		}
		::RhinoApp().Print(L"\}\n");
	}
	return CRhinoCommand::success;
}
1 Like

Thank you very much, Menno.
How can I correctly get all the surfaces separately for analysis?
– Karlos

Here is one way:

– Dale

2 Likes

Thank you very much Dale.
Thank you very much Menno.
– Karlos

1 Like