License Delivery maintenance is expected to occur on Saturday, November 30, between 8 AM and 11 AM CET. This may cause a short 3-hours outage in which license-related tasks: license key upload, download, update, SSA validation, access to the license pool and Graphisoft ID authentication may not function properly. We apologize for any inconvenience.
Archicad C++ API
About Archicad add-on development using the C++ API.
SOLVED!

How to get the projected area through API function?

leilei
Enthusiast
Hi,

Does anyone know this question?If I have a 3D object and I want to get the projected area of this object, is there any way to get this area through the API function?

Best regards,
Leilei
1 ACCEPTED SOLUTION

Accepted Solutions
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Ralph's right, you have to calculate this on your own.
But of course we are here to help you, so I wrote you a draft for this calculation:
#include	"ACAPinc.h"

#include	"basicgeometry.h"
#include	"Polygon2D.hpp"

static double CalculateProjectedAreaOfElem (const API_Guid& elemGuid)
{
	double			calculatedArea = 0.;
	GSErrCode		err = NoError;
	Geometry::Plane	floorPlanPlane;
	API_Elem_Head	elemHead = {};
	elemHead.guid = elemGuid;

	API_ElemInfo3D 	info3D;
	err = ACAPI_Element_Get3DInfo (elemHead, &info3D);
	if (DBERROR (err != NoError))
		return 0;

	Geometry::MultiPolygon2D projectedPolygon2DArray; 
	for (Int32 ibody = info3D.fbody; ibody <= info3D.lbody; ++ibody) {
		API_Component3D 	component = {};
		component.header.typeID = API_BodyID;
		component.header.index  = ibody;
		err = ACAPI_3D_GetComponent (&component);
		if (DBERROR (err != NoError))
			continue;

		Int32		nPgon = component.body.nPgon;
		API_Tranmat	tm = component.body.tranmat;

		for (Int32 iPgon = 1; iPgon <= nPgon; ++iPgon) {
			component.header.typeID = API_PgonID;
			component.header.index  = iPgon;
			err = ACAPI_3D_GetComponent (&component);
			if (DBERROR (err != NoError))
				continue;

			GS::Array<Coord>	pgonCoordsProjectedToFloorPlan;
			for (Int32 iEdge = component.pgon.fpedg; iEdge <= component.pgon.lpedg; ++iEdge) {
				component.header.typeID = API_EdgeID;
				component.header.index  = iEdge;
				err = ACAPI_3D_GetComponent (&component);
				if (DBERROR (err != NoError))
					continue;

				component.header.typeID = API_VertID;
				component.header.index  = component.edge.vert1;
				err = ACAPI_3D_GetComponent (&component);
				if (DBERROR (err != NoError))
					continue;

				Geometry::Coord3D	worldCoord;
				worldCoord.x = tm.tmx[0]*component.vert.x + tm.tmx[1]*component.vert.y + tm.tmx[2]*component.vert.z + tm.tmx[3];
				worldCoord.y = tm.tmx[4]*component.vert.x + tm.tmx[5]*component.vert.y + tm.tmx[6]*component.vert.z + tm.tmx[7];
				worldCoord.z = tm.tmx[8]*component.vert.x + tm.tmx[9]*component.vert.y + tm.tmx[10]*component.vert.z + tm.tmx[11];
				Geometry::Coord3D	projectedCoord = floorPlanPlane.ProjectToPlane (worldCoord);
				pgonCoordsProjectedToFloorPlan.PushNew (projectedCoord.x, projectedCoord.y);
			}

			Geometry::MultiPolygon2D polygon2DArray; 
			Geometry::Polygon2D::Create (pgonCoordsProjectedToFloorPlan, 0, polygon2DArray);
			projectedPolygon2DArray.Append (polygon2DArray);
		}
	}

	projectedPolygon2DArray.Unify (Geometry::WithoutHoles);
	for (const Geometry::Polygon2D& polygon2D : projectedPolygon2DArray)
		calculatedArea += polygon2D.CalcArea ();

	return calculatedArea;
}
This is based on Ralph's idea.

View solution in original post

5 REPLIES 5
Ralph Wessel
Mentor
Could you clarify whether you mean the total surface area of the 3D body or the area visible in the 3D window?
Ralph Wessel BArch
Software Engineer Speckle Systems
leilei
Enthusiast
Ralph wrote:
Could you clarify whether you mean the total surface area of the 3D body or the area visible in the 3D window?
Hi Ralph,
The area I want is like a projection on a floor plan.
Regards,
Leilei
Ralph Wessel
Mentor
I don't know if there's an easy answer to that. Bottom line, you could extract the faces of the 3D body, project them to 2D polygons in the view plane and merge them to result in the bounding polygon(s). Hopefully there is a simpler method.
Ralph Wessel BArch
Software Engineer Speckle Systems
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Ralph's right, you have to calculate this on your own.
But of course we are here to help you, so I wrote you a draft for this calculation:
#include	"ACAPinc.h"

#include	"basicgeometry.h"
#include	"Polygon2D.hpp"

static double CalculateProjectedAreaOfElem (const API_Guid& elemGuid)
{
	double			calculatedArea = 0.;
	GSErrCode		err = NoError;
	Geometry::Plane	floorPlanPlane;
	API_Elem_Head	elemHead = {};
	elemHead.guid = elemGuid;

	API_ElemInfo3D 	info3D;
	err = ACAPI_Element_Get3DInfo (elemHead, &info3D);
	if (DBERROR (err != NoError))
		return 0;

	Geometry::MultiPolygon2D projectedPolygon2DArray; 
	for (Int32 ibody = info3D.fbody; ibody <= info3D.lbody; ++ibody) {
		API_Component3D 	component = {};
		component.header.typeID = API_BodyID;
		component.header.index  = ibody;
		err = ACAPI_3D_GetComponent (&component);
		if (DBERROR (err != NoError))
			continue;

		Int32		nPgon = component.body.nPgon;
		API_Tranmat	tm = component.body.tranmat;

		for (Int32 iPgon = 1; iPgon <= nPgon; ++iPgon) {
			component.header.typeID = API_PgonID;
			component.header.index  = iPgon;
			err = ACAPI_3D_GetComponent (&component);
			if (DBERROR (err != NoError))
				continue;

			GS::Array<Coord>	pgonCoordsProjectedToFloorPlan;
			for (Int32 iEdge = component.pgon.fpedg; iEdge <= component.pgon.lpedg; ++iEdge) {
				component.header.typeID = API_EdgeID;
				component.header.index  = iEdge;
				err = ACAPI_3D_GetComponent (&component);
				if (DBERROR (err != NoError))
					continue;

				component.header.typeID = API_VertID;
				component.header.index  = component.edge.vert1;
				err = ACAPI_3D_GetComponent (&component);
				if (DBERROR (err != NoError))
					continue;

				Geometry::Coord3D	worldCoord;
				worldCoord.x = tm.tmx[0]*component.vert.x + tm.tmx[1]*component.vert.y + tm.tmx[2]*component.vert.z + tm.tmx[3];
				worldCoord.y = tm.tmx[4]*component.vert.x + tm.tmx[5]*component.vert.y + tm.tmx[6]*component.vert.z + tm.tmx[7];
				worldCoord.z = tm.tmx[8]*component.vert.x + tm.tmx[9]*component.vert.y + tm.tmx[10]*component.vert.z + tm.tmx[11];
				Geometry::Coord3D	projectedCoord = floorPlanPlane.ProjectToPlane (worldCoord);
				pgonCoordsProjectedToFloorPlan.PushNew (projectedCoord.x, projectedCoord.y);
			}

			Geometry::MultiPolygon2D polygon2DArray; 
			Geometry::Polygon2D::Create (pgonCoordsProjectedToFloorPlan, 0, polygon2DArray);
			projectedPolygon2DArray.Append (polygon2DArray);
		}
	}

	projectedPolygon2DArray.Unify (Geometry::WithoutHoles);
	for (const Geometry::Polygon2D& polygon2D : projectedPolygon2DArray)
		calculatedArea += polygon2D.CalcArea ();

	return calculatedArea;
}
This is based on Ralph's idea.
leilei
Enthusiast
Thanks Ralph and Tibor,