Learn to manage BIM workflows and create professional Archicad templates with the BIM Manager Program.
2025-01-07 02:03 AM
Hi everyone,
I’m trying to automate the creation of cut planes in front of ~200 doors, so that each door has its own section.
I compute each door’s position and then create an API_CutPlaneType element for the cut line and marker. This part works. The marker shows in plan. But no section is added to the Navigator, and the marker is locked so I can’t manually convert it into a real section viewpoint.
Here’s my (messy) code : (Devikit 25)
#include "APIEnvir.h"
#include "ACAPinc.h"
#include <cmath>
#pragma execution_character_set("utf-8")
static void WriteReport(const GS::UniString& message)
{
ACAPI_WriteReport(message.ToCStr().Get(), false);
}
static GSErrCode ValidateDoors(void)
{
GSErrCode err = NoError;
GS::Array<API_Guid> doorGuids;
err = ACAPI_Element_GetElemList(API_DoorID, &doorGuids);
if (err != NoError) {
WriteReport("Erreur lors de la récupération des GUIDs des portes.");
return err;
}
WriteReport("Nombre de portes trouvées : " + GS::UniString::Printf("%d", doorGuids.GetSize()));
Int32 doorIndex = 0;
for (const auto& doorGuid : doorGuids) {
API_Element doorElem{}, wallElem{};
BNZeroMemory(&doorElem, sizeof(API_Element));
BNZeroMemory(&wallElem, sizeof(API_Element));
doorElem.header.guid = doorGuid;
if (ACAPI_Element_Get(&doorElem) != NoError) {
WriteReport("Erreur lors de la récupération de la porte.");
continue;
}
wallElem.header.guid = doorElem.door.owner;
if (ACAPI_Element_Get(&wallElem) != NoError) {
WriteReport("Erreur lors de la récupération du mur parent.");
continue;
}
// Points début/fin du mur
API_Coord beg = wallElem.wall.begC;
API_Coord end = wallElem.wall.endC;
// Vecteur unitaire du mur
API_Vector wallVec = {end.x - beg.x, end.y - beg.y};
double vecLength = sqrt(wallVec.x * wallVec.x + wallVec.y * wallVec.y);
if (vecLength < 1e-9) {
WriteReport("Longueur mur invalide.");
continue;
}
API_Vector wallUnitVec = {wallVec.x / vecLength, wallVec.y / vecLength};
// Position porte
double objLoc = doorElem.door.objLoc;
API_Coord doorPos = {
beg.x + wallUnitVec.x * objLoc,
beg.y + wallUnitVec.y * objLoc
};
// Normale (perp. mur)
API_Vector normVec = {-wallUnitVec.y, wallUnitVec.x};
// Préparation de la coupe
API_Element cutPlaneElem{};
API_ElementMemo cutPlaneMemo{};
API_SubElement cutPlaneMarker{};
BNZeroMemory(&cutPlaneElem, sizeof(API_Element));
BNZeroMemory(&cutPlaneMemo, sizeof(API_ElementMemo));
BNZeroMemory(&cutPlaneMarker, sizeof(API_SubElement));
// Type coupe
cutPlaneElem.header.typeID = API_CutPlaneID;
// Important : copier l’étage
cutPlaneElem.header.floorInd = doorElem.header.floorInd;
cutPlaneMarker.subType = APISubElement_MainMarker;
// Récup. des defaults
err = ACAPI_Element_GetDefaultsExt(&cutPlaneElem, &cutPlaneMemo, 1UL, &cutPlaneMarker);
if (err != NoError) {
WriteReport("Impossible de récupérer les defaults pour la coupe.");
continue;
}
// Remplir manuellement quelques champs
// (parfois nécessaires selon la configuration du template)
cutPlaneElem.cutPlane.segment.segType = APISect_Normal;
cutPlaneElem.cutPlane.segment.flags = 0;
cutPlaneElem.cutPlane.linkData.showOnHome = true;
cutPlaneElem.cutPlane.linkData.refFloor = doorElem.header.floorInd;
cutPlaneElem.cutPlane.linkData.sourceMarker = true;
// Nom unique
GS::UniString coupeName = GS::UniString::Printf("Section_Porte_%d", ++doorIndex);
GS::UniString coupeId = GS::UniString::Printf("ID_Porte_%d", doorIndex);
GS::ucscpy(cutPlaneElem.cutPlane.segment.cutPlName, coupeName.ToUStr());
GS::ucscpy(cutPlaneElem.cutPlane.segment.cutPlIdStr, coupeId.ToUStr());
// Points "main coords" (ligne de coupe)
double halfLength = 0.5;
API_Coord startCoord, endCoord;
startCoord.x = doorPos.x - halfLength * normVec.x;
startCoord.y = doorPos.y - halfLength * normVec.y;
endCoord.x = doorPos.x + halfLength * normVec.x;
endCoord.y = doorPos.y + halfLength * normVec.y;
cutPlaneMemo.sectionSegmentMainCoords = (API_Coord*) BMAllocatePtr(2 * sizeof(API_Coord), ALLOCATE_CLEAR, 0);
if (cutPlaneMemo.sectionSegmentMainCoords == nullptr) {
WriteReport("Erreur d'allocation memo mainCoords.");
ACAPI_DisposeElemMemoHdls(&cutPlaneMemo);
ACAPI_DisposeElemMemoHdls(&cutPlaneMarker.memo);
continue;
}
cutPlaneMemo.sectionSegmentMainCoords[0] = startCoord;
cutPlaneMemo.sectionSegmentMainCoords[1] = endCoord;
cutPlaneElem.cutPlane.segment.nMainCoord = 2;
cutPlaneElem.cutPlane.segment.nDepthCoord = 2;
// Rotation manuelle à 90° pour la profondeur
double dx = endCoord.x - startCoord.x;
double dy = endCoord.y - startCoord.y;
// Tourner de 90° autour de startCoord
API_Coord rotCoord;
rotCoord.x = startCoord.x - dy;
rotCoord.y = startCoord.y + dx;
// Vecteur unitaire
API_Vector v;
v.x = rotCoord.x - startCoord.x;
v.y = rotCoord.y - startCoord.y;
double len = sqrt(v.x*v.x + v.y*v.y);
if (len < 1e-9)
len = 1.0; // éviter division par 0
v.x /= len;
v.y /= len;
// Points de profondeur
cutPlaneMemo.sectionSegmentDepthCoords = (API_Coord*) BMAllocatePtr(2 * sizeof(API_Coord), ALLOCATE_CLEAR, 0);
if (cutPlaneMemo.sectionSegmentDepthCoords == nullptr) {
WriteReport("Erreur alloc memo depthCoords.");
ACAPI_DisposeElemMemoHdls(&cutPlaneMemo);
ACAPI_DisposeElemMemoHdls(&cutPlaneMarker.memo);
continue;
}
API_Coord refCoord, deeperCoord;
refCoord.x = startCoord.x + v.x;
refCoord.y = startCoord.y + v.y;
deeperCoord.x = refCoord.x + v.x;
deeperCoord.y = refCoord.y + v.y;
cutPlaneMemo.sectionSegmentDepthCoords[0] = refCoord;
cutPlaneMemo.sectionSegmentDepthCoords[1] = deeperCoord;
// Création enveloppée dans une Undoable Command :
err = ACAPI_CallUndoableCommand("Create Section for Door", [&]() -> GSErrCode {
return ACAPI_Element_CreateExt(&cutPlaneElem, &cutPlaneMemo, 1UL, &cutPlaneMarker);
});
if (err == NoError) {
WriteReport("Coupe créée avec succès : " + coupeName);
} else {
WriteReport("Erreur lors de la création de la coupe pour la porte "
+ GS::UniString::Printf("%d", doorIndex));
}
// Libération
ACAPI_DisposeElemMemoHdls(&cutPlaneMemo);
ACAPI_DisposeElemMemoHdls(&cutPlaneMarker.memo);
}
WriteReport("Validation des portes terminée.");
return NoError;
}
static GSErrCode __ACENV_CALL MenuCommandHandler(const API_MenuParams* menuParams)
{
WriteReport("Menu sélectionné : Validation des portes + création de coupes.");
return ValidateDoors();
}
GSErrCode __ACDLL_CALL RegisterInterface(void)
{
return ACAPI_Register_Menu(32500, 32501, MenuCode_UserDef, MenuFlag_Default);
}
API_AddonType __ACDLL_CALL CheckEnvironment(API_EnvirParams* envir)
{
envir->addOnInfo.description = "Valider les données des portes et créer des coupes";
return APIAddon_Normal;
}
GSErrCode __ACDLL_CALL Initialize(void)
{
GSErrCode err = NoError;
err = ACAPI_Install_MenuHandler(32500, MenuCommandHandler);
if (err != NoError) {
WriteReport("Erreur lors de l'installation du gestionnaire de menu.");
}
return err;
}
GSErrCode __ACDLL_CALL FreeData(void)
{
return NoError;
}
I must be missing something. The line is drawn, but I don’t see any new section in the Navigator. Has anyone successfully done this? Any help or guidance would be greatly appreciated!
Thanks!