BIM Coordinator Program (INT) April 22, 2024

Find the next step in your career as a Graphisoft Certified BIM Coordinator!

Archicad C++ API
About Archicad add-on development using the C++ API.
SOLVED!

Change default column / beam

Lawoks
Contributor

Hi,

Could any of you help with the following matter? The following functions - work for walls (based on Attribute_Test) , but for column or beam it does not. And theoretically it should. (?) What is wrong? - beam is my own profile, exist in document, index is proper.

Regards

 

 

void Set_Current_Wall(GS::UniString WallName)
{
API_Attribute attrib;
int nLin, i, wall_index;
GSErrCode err;
API_ElementMemo memo = {};
API_Element element = {}, mask = {};
BNZeroMemory(&attrib, sizeof(API_Attribute));
attrib.header.typeID = API_CompWallID;
err = ACAPI_Attribute_GetNum(API_CompWallID, &nLin);
for (i = 1; i <= nLin && err == NoError; i++) {
attrib.header.index = i;
err = ACAPI_Attribute_Get(&attrib);
if (err == NoError){
if (attrib.header.name == WallName) {
wall_index = attrib.header.index;
ACAPI_DisposeElemMemoHdls(&memo);
BNZeroMemory(&element, sizeof(API_Element));
element.header.type = API_WallID;
err = ACAPI_Element_GetDefaults(&element, nullptr);
if (err == NoError) {
ACAPI_ELEMENT_MASK_CLEAR(mask);
element.wall.modelElemStructureType = API_CompositeStructure;
ACAPI_ELEMENT_MASK_SET(mask, API_WallType, composite);
element.wall.composite = wall_index;
err = ACAPI_Element_ChangeDefaults(&element, nullptr, &mask);
}}}
if (err == APIERR_DELETED)
err = NoError;}
return;
void Set_Current_Beam(GS::UniString BeamName)
{
API_Attribute attrib;
int nLin, i, beam_index;
GSErrCode err;
beam_index = -1; // initialization
API_ElementMemo memo = {};
API_Element element = {}, mask = {};
BNZeroMemory(&attrib, sizeof(API_Attribute));
attrib.header.typeID = API_ProfileID; // Use API_ProfileID
err = ACAPI_Attribute_GetNum(API_ProfileID, &nLin); // Use API_ProfileID
for (i = 1; i <= nLin && err == NoError; i++) {
attrib.header.index = i;
err = ACAPI_Attribute_Get(&attrib);
if (err == NoError) {
if (attrib.header.name == BeamName) {
beam_index = attrib.header.index;
ACAPI_DisposeElemMemoHdls(&memo);
BNZeroMemory(&element, sizeof(API_Element));
element.header.type = API_BeamID; // Use API_BeamID
err = ACAPI_Element_GetDefaults(&element, nullptr);
if (err == NoError) {
ACAPI_ELEMENT_MASK_CLEAR(mask);
ACAPI_ELEMENT_MASK_SET(mask, API_BeamSegmentType, assemblySegmentData);
element.beamSegment.assemblySegmentData.modelElemStructureType = API_ProfileStructure;
element.beamSegment.assemblySegmentData.profileAttr = beam_index;
err = ACAPI_Element_ChangeDefaults(&element, nullptr, &mask);
} } }
if (err == APIERR_DELETED)
err = NoError;
}
return;
}

 

2 ACCEPTED SOLUTIONS

Accepted Solutions
Solution
Lawoks
Contributor

Hi Bernd,

Thank You for your reaction!

The code runs smoothly, but I didn't try to 'peek' at individual steps. Unfortunately, I can't set up a debugger to properly track the code (I made this in C# and Revit, but here i can't).

I create a beam from the code, read the index (but i wiI'll try via guid). Up to this point it's ok...

You've put me on track...  the available info in the API indicates that I should work via beam segment (in theory... ). To achieve the desired effect using an inserted element in doccument shows: 'Pick up clicked' from the Element_Test example, but I don't have the beam inserted in document yet 🙂

In addition: Typically: the user selects a beam from its own dialog box. If the beam does not exist in the document yet, it is created and can be inserted. In the next steps, user may 'call' the dialog box or not - that's why I needthe beam as the default one.

 regards! And THX!

Beam.PNG

View solution in original post

Solution
Lawoks
Contributor

After many attempts, I finally found a solution based on Bernd's suggestions. Maybe someone would like to use it. I don't know if this is the most efficient code, but it works. Sets the beam as default based on the profile name.

Step 1.  unique index based on profile name.

int Return_Beam_Index(const GS::UniString& profileName) {
API_Attribute attrib;
int profile_index{}, nLin;
GSErrCode err;
BNZeroMemory(&attrib, sizeof(API_Attribute));
attrib.header.typeID = API_ProfileID;
// Get the total number of profiles and iterate through all
err = ACAPI_Attribute_GetNum(API_ProfileID, &nLin);
for (profile_index = 1; profile_index <= nLin && err == NoError; profile_index++) {
attrib.header.index = profile_index;
err = ACAPI_Attribute_Get(&attrib);
// Check if the current profile's name matches the target name
if (err == NoError && GS::UniString(attrib.header.name) == profileName) {
profile_index = attrib.header.index;
break;
}
}
return profile_index;
}

Step 2. Set the current default beam based on a profile index

void Set_Current_Beam(GS::UniString profileName) {
API_Element element;
API_ElementMemo memo;
API_Element mask;
GSErrCode err{};
// Retrieve the index of the specified beam profile (void .Return_Beam_Index)
int profileIndex = Return_Beam_Index(profileName);
if (profileIndex == -1) {
WriteReport_Err("Profile not found", err);
return;
}
// Initialize the beam element structure and retrieve its default settings.
BNZeroMemory(&element, sizeof(API_Element));
element.header.type = API_BeamID;
err = ACAPI_Element_GetDefaults(&element, &memo);
if (err != NoError) {
WriteReport_Err("Cannot get default settings for beam", err);
return;
}
// If segments are present, iterate through them to update profile info.
if (memo.beamSegments != nullptr) {
for (USize idx = 0; idx < element.beam.nSegments; ++idx) {
memo.beamSegments[idx].assemblySegmentData.modelElemStructureType = API_ProfileStructure;
memo.beamSegments[idx].assemblySegmentData.profileAttr = profileIndex;
}
}
ACAPI_ELEMENT_MASK_CLEAR(mask);

// AND DO IT!
err = ACAPI_Element_ChangeDefaults(&element, &memo, &mask);
if (err != NoError) {
WriteReport_Err("Cannot set new profile for beam", err);
}
ACAPI_DisposeElemMemoHdls(&memo);
}

View solution in original post

3 REPLIES 3

Hi Lawoks,

 

Do you get any error code from the ACAPI_Element_ChangeDefaults call?

My first suspicion is, that you should actually setup the element.beam structure instead of the element.beamSegment structure

and instead provide the profile data in the element memo of the beam.

And then things like element.beam.nProfiles should match what's provided in the memo of your element.

Check out the API_BeamID section of the API_ElemMemo documentation.

 

I think the easiest way to go about learning what to actually put in all the fields is to create the desired beam "by hand" in Archicad and then retrieve it with API_Element_Get and API_Element_GetMemo and investigate these data structures.

 

Hope that helps,

Bernd

Bernd Schwarzenbacher - Archicad Add-On Developer - Get Add-Ons & Archicad Tips on my Website: Archi-XT.com
Solution
Lawoks
Contributor

Hi Bernd,

Thank You for your reaction!

The code runs smoothly, but I didn't try to 'peek' at individual steps. Unfortunately, I can't set up a debugger to properly track the code (I made this in C# and Revit, but here i can't).

I create a beam from the code, read the index (but i wiI'll try via guid). Up to this point it's ok...

You've put me on track...  the available info in the API indicates that I should work via beam segment (in theory... ). To achieve the desired effect using an inserted element in doccument shows: 'Pick up clicked' from the Element_Test example, but I don't have the beam inserted in document yet 🙂

In addition: Typically: the user selects a beam from its own dialog box. If the beam does not exist in the document yet, it is created and can be inserted. In the next steps, user may 'call' the dialog box or not - that's why I needthe beam as the default one.

 regards! And THX!

Beam.PNG

Solution
Lawoks
Contributor

After many attempts, I finally found a solution based on Bernd's suggestions. Maybe someone would like to use it. I don't know if this is the most efficient code, but it works. Sets the beam as default based on the profile name.

Step 1.  unique index based on profile name.

int Return_Beam_Index(const GS::UniString& profileName) {
API_Attribute attrib;
int profile_index{}, nLin;
GSErrCode err;
BNZeroMemory(&attrib, sizeof(API_Attribute));
attrib.header.typeID = API_ProfileID;
// Get the total number of profiles and iterate through all
err = ACAPI_Attribute_GetNum(API_ProfileID, &nLin);
for (profile_index = 1; profile_index <= nLin && err == NoError; profile_index++) {
attrib.header.index = profile_index;
err = ACAPI_Attribute_Get(&attrib);
// Check if the current profile's name matches the target name
if (err == NoError && GS::UniString(attrib.header.name) == profileName) {
profile_index = attrib.header.index;
break;
}
}
return profile_index;
}

Step 2. Set the current default beam based on a profile index

void Set_Current_Beam(GS::UniString profileName) {
API_Element element;
API_ElementMemo memo;
API_Element mask;
GSErrCode err{};
// Retrieve the index of the specified beam profile (void .Return_Beam_Index)
int profileIndex = Return_Beam_Index(profileName);
if (profileIndex == -1) {
WriteReport_Err("Profile not found", err);
return;
}
// Initialize the beam element structure and retrieve its default settings.
BNZeroMemory(&element, sizeof(API_Element));
element.header.type = API_BeamID;
err = ACAPI_Element_GetDefaults(&element, &memo);
if (err != NoError) {
WriteReport_Err("Cannot get default settings for beam", err);
return;
}
// If segments are present, iterate through them to update profile info.
if (memo.beamSegments != nullptr) {
for (USize idx = 0; idx < element.beam.nSegments; ++idx) {
memo.beamSegments[idx].assemblySegmentData.modelElemStructureType = API_ProfileStructure;
memo.beamSegments[idx].assemblySegmentData.profileAttr = profileIndex;
}
}
ACAPI_ELEMENT_MASK_CLEAR(mask);

// AND DO IT!
err = ACAPI_Element_ChangeDefaults(&element, &memo, &mask);
if (err != NoError) {
WriteReport_Err("Cannot set new profile for beam", err);
}
ACAPI_DisposeElemMemoHdls(&memo);
}
Learn and get certified!