Wednesday
I'm working in a problem where I want to replace one surface material in the project (For example A) with Another Surface Material (B), with all the instances in the project which has A as Surface Material, changing from A to B.
I have tried to replace the material in A's index with B. It doesn't seem to work.
I'm wondering if there is a way to export the Surface Material's XML, from Attribute Manager, using any Archicad API and edit the <Index></Index> and import the same XML, to achieve the Material replacement.
Is there any way to export the Surface material by Index, from the Attribute Manager using Archicad API, as shown in the screenshot below?
Thanks in Advance!
Wednesday
Re-index only works to change the index number to another number that is not being used.
What you want to do is 'Delete' the attribute and then use the 'Delete & Replace' option to change it to the attribute that you want it to be.
All placed instances of elements using that attribute will be changed to the new one.
The original attribute will be deleted completely.
Barry.
Wednesday - last edited Wednesday
How can I perform 'Delete & Replace' in the plugin code? Is there any Archicad API to do this?
Wednesday
Sorry, I completely missed this was in the API forum.
Just reading unread posts and not looking in actual forums.
I have no idea what you need to do in API.
Barry.
Wednesday
You should be able to change the attributes of elements like walls through the API, but how you do that for surfaces may not be straightforward. The surface may actually come from the assigned building material, or it could be an override of the building material. Can you post some code showing how you are currently trying to change the surface?
Thursday
Yes, I'm unable to replace Materials in Surfaces of Windows, Doors etc. I'm facing APIERR_BADINDEX when the element is window/door. This is the code that I'm trying to use.
Please let me know how can I replace the Surface Material from one to another, for all the elements.
GSErrCode BrowserPalette::ReplaceSurfaceMaterial(const char* oldMaterialName, const char* newMaterialName) {
API_AttributeIndex oldSurfaceIndex = FindSurfaceMaterialIndex(oldMaterialName);
API_AttributeIndex newSurfaceIndex = FindSurfaceMaterialIndex(newMaterialName);
API_Attribute attr;
BNZeroMemory(&attr, sizeof(API_Attribute));
attr.header.typeID = API_MaterialID;
attr.header.index = oldSurfaceIndex;
// Get the existing attribute
GSErrCode err = ACAPI_Attribute_Get(&attr);
if (err != NoError) {
ACAPI_WriteReport("Failed to get old attribute!", false);
return 0;
}
API_Element element;
API_ElementMemo memo;
memo = {};
API_Element mask;
ACAPI_ELEMENT_MASK_CLEAR(mask);
GS::Array<API_ElemTypeID> elementTypes = { API_WallID, API_SlabID, API_RoofID, API_ColumnID, API_BeamID, API_ObjectID, API_ShellID, API_WindowID, API_DoorID };
GS::Array<API_Guid> elementsToUpdate;
for (const auto& elemType : elementTypes) {
GS::Array<API_Guid> elemList;
ACAPI_Element_GetElemList(elemType, &elemList);
for (const auto& guid : elemList) {
BNZeroMemory(&element, sizeof(API_Element));
element.header.guid = guid;
//DBPrintf("ElemType: %d", elemType);
bool updated = false;
if (ACAPI_Element_Get(&element) == NoError) {
// Update material in different element types
if (element.header.type == API_WallID) {
if (element.wall.refMat.value == oldSurfaceIndex) {
element.wall.refMat.value = newSurfaceIndex;
updated = true;
}
if (element.wall.oppMat.value == oldSurfaceIndex) {
element.wall.oppMat.value = newSurfaceIndex;
updated = true;
}
if (element.wall.sidMat.value == oldSurfaceIndex) {
element.wall.sidMat.value = newSurfaceIndex;
updated = true;
}
}
if (element.header.type == API_SlabID) {
if (element.slab.topMat.value == oldSurfaceIndex) {
element.slab.topMat.value = newSurfaceIndex;
updated = true;
}
if (element.slab.botMat.value == oldSurfaceIndex) {
element.slab.botMat.value = newSurfaceIndex;
updated = true;
}
if (element.slab.sideMat.value == oldSurfaceIndex) {
element.slab.sideMat.value = newSurfaceIndex;
updated = true;
}
}
else if (element.header.type == API_RoofID) {
if (element.roof.shellBase.topMat.value == oldSurfaceIndex) {
element.roof.shellBase.topMat.value = newSurfaceIndex;
updated = true;
}
if (element.roof.shellBase.botMat.value == oldSurfaceIndex) {
element.roof.shellBase.botMat.value = newSurfaceIndex;
updated = true;
}
if (element.roof.shellBase.sidMat.value == oldSurfaceIndex) {
element.roof.shellBase.sidMat.value = newSurfaceIndex;
updated = true;
}
}
else if (element.header.type == API_ColumnID) {
ACAPI_Element_GetMemo(element.header.guid, &memo, APIMemoMask_ColumnSegment);
if (memo.columnSegments == nullptr)
return 0;
if (memo.columnSegments[0].extrusionSurfaceMaterial.value == oldSurfaceIndex) {
memo.columnSegments[0].extrusionSurfaceMaterial.value = newSurfaceIndex;
updated = true;
}
}
else if (element.header.type == API_ShellID) {
if (element.shell.shellBase.topMat.value == oldSurfaceIndex) {
element.shell.shellBase.topMat.value = newSurfaceIndex;
updated = true;
}
if (element.shell.shellBase.botMat.value == oldSurfaceIndex) {
element.shell.shellBase.botMat.value = newSurfaceIndex;
updated = true;
}
if (element.shell.shellBase.sidMat.value == oldSurfaceIndex) {
element.shell.shellBase.sidMat.value = newSurfaceIndex;
updated = true;
}
}
else if (element.header.type == API_WindowID || element.header.type == API_DoorID) {
// Check if new attribute already exists
API_Attribute newAttr;
BNZeroMemory(&newAttr, sizeof(API_Attribute));
newAttr.header.typeID = API_MaterialID;
newAttr.header.index = newSurfaceIndex;
err = ACAPI_Attribute_Get(&newAttr);
if (err != NoError) {
ACAPI_WriteReport("New attribute does not exist! Cannot proceed.", false);
return 0;
}
attr.header.index = newSurfaceIndex;
newAttr.header.index = oldSurfaceIndex;
err = ACAPI_Attribute_Modify(&attr, nullptr);
if (err != NoError) {
DBPrintf("Error: Failed to modify material attributes!\n");
return err;
}
err = ACAPI_Attribute_Modify(&newAttr, nullptr);
if (err != NoError) {
DBPrintf("Error: Failed to modify material attributes!\n");
return err;
}
}
// Modify the element if changes were made
if (updated) {
GSErrCode err = ACAPI_CallUndoableCommand("Change General Parameters", [&]() -> GSErrCode {
return ACAPI_Element_Change(&element, nullptr, &memo, 0, true);
});
}
}
}
}
ACAPI_WriteReport(" Updated %d elements to use new surface material!", false, elementsToUpdate.GetSize());
// Delete old surface material safely
err = ACAPI_Attribute_Delete(attr.header);
if (err == NoError) {
ACAPI_WriteReport("Successfully deleted old material (Index: %d)!", false, oldSurfaceIndex);
}
else {
ACAPI_WriteReport(" Failed to delete old material (Index: %d). It may still be in use!", false, oldSurfaceIndex);
}
return NoError;
}
Thursday - last edited Thursday
I think this code needs a rethink. For walls etc, the rendered surface depends on whether it's an override of the wall building material surface. And if you change the surface attribute, you also need to specify that it's an override.
With doors and windows it appears you're trying to swap the surface attributes without even checking if they are relevant to the door/window. And if there are multiple doors/windows, they would be repeatedly swapped - the end result would depend on whether it was an odd/even number of doors/windows.
And finally a delete is attempted on the old material - why is that necessary for a swap operation?