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

Get Facade Panels in Order?

Anonymous
Not applicable
Hello Archicad community,

Based on the grid mesh I can get each facade panel's dimensions and convert it into a wall (attachment 1 is the input, attachment 2 the output), however, the panels I get do not seem to be in any particular order (attachment 2). If I loop over the panels I get from "memo.cWallPanels" (API_CWPanelType) and build a wall based on the panel's material (glass or opaque) the order is not the same (and there also does not seem to be any other order which I could correlate).

Is there any way I could retrieve the panels in such a way that I know their position in the grid mesh and assign the correct material accordingly?

Thanks,
- Dan
1 ACCEPTED SOLUTION

Accepted Solutions
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
In theory it's possible to get the panels in order, but unfortunately two bugs in the API prevent us to do that:
  • cWallPanelGridIDTable member of the API_ElementMemo is always nullptr. That member should contain the information about the positions of the panels in the grid.
  • API_CWPanelType structure does not contain the ID of the segment, so if the curtain wall has multiple segments then the panels cannot be sorted correctly (because each segment has individual grid and a gridCellID is not unique).

I added these bugs to our bug database.
The first will be fixed in an upcoming update for AC22 (buildnum higher than 5500).
The second one needs API interface modification: the filler_1[0] member of the API_CWPanelType structure will be reused, that will contain the index of the segment which contains the panel. I hope that modification can be done also in an upcoming update for AC22 (buildnum higher than 5500).

I will notify again you when the modifications were successfully done. After that you can use the following code to get the sorted panels:
GS::Array<API_CWPanelType> GetCurtainWallPanelsInOrder (const API_Guid& cwGuid)
{
	GS::Array<API_CWPanelType> result;

	API_Element		cwElement;
	BNZeroMemory (&cwElement, sizeof (API_Element));
	cwElement.header.guid = cwGuid;

	GSErrCode err = ACAPI_Element_Get (&cwElement);
	if (err == NoError) {
		API_ElementMemo	cwMemo;
		BNZeroMemory (&cwMemo, sizeof (API_ElementMemo));

		err = ACAPI_Element_GetMemo (cwElement.header.guid, &cwMemo, APIMemoMask_CWallSegments | APIMemoMask_CWallPanels);
		if (err == NoError && cwMemo.cWallSegments != nullptr && cwMemo.cWallPanelGridIDTable != nullptr) {
			GS::HashTable<API_Guid, API_CWPanelType> cwPanelTable;
			for (UIndex ii = 0; ii < cwElement.curtainWall.nPanels; ++ii) {
				const API_CWPanelType& cwPanel = cwMemo.cWallPanels[ii];
				cwPanelTable.Add (cwPanel.head.guid, cwPanel);
			}

			using CWPanelSegmentGridID = GS::Pair<UInt32, API_GridElemID>;
			GS::Array<CWPanelSegmentGridID> segmentGridIDs;
			GS::HashTable<CWPanelSegmentGridID, GS::Array<API_CWPanelType>> reversedCWPanelGridIDTable;
			for (auto it = cwMemo.cWallPanelGridIDTable->EnumeratePairs (); it != nullptr; ++it) {
				const API_CWPanelType& cwPanel = cwPanelTable[*it->key];
				const GS::Array<API_GridElemID>& cwPanelGridIDs = *it->value;

				for (auto it = cwPanelGridIDs.Enumerate (); it != nullptr; ++it) {
					const API_GridElemID& gridID = *it;

					CWPanelSegmentGridID segmentGridID (cwPanel.filler_1[0]/*cwPanel.segmentID*/, gridID);
					if (reversedCWPanelGridIDTable.ContainsKey (segmentGridID))
						reversedCWPanelGridIDTable[segmentGridID].Push (cwPanel);
					else
						reversedCWPanelGridIDTable.Add (segmentGridID, { cwPanel });

					segmentGridIDs.Push (segmentGridID);
				}
			}

			GS::Sort (segmentGridIDs.Begin (), segmentGridIDs.End (),
				[](const auto& lhs, const auto& rhs) {
					if (lhs.first == rhs.first)
						return lhs.second < rhs.second;
					return lhs.first < rhs.first;
				});

			for (auto it = segmentGridIDs.Enumerate (); it != nullptr; ++it) {
				const CWPanelSegmentGridID& segmentGridID = *it;
				result.Append (reversedCWPanelGridIDTable[segmentGridID]);
			}
		}

		ACAPI_DisposeElemMemoHdls (&cwMemo);
	}

	return result;
}

View solution in original post

3 REPLIES 3
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
Hi Dan,

You're right, it's not trivial to get the panels in order. But it's not impossible
I'm preparing an example code for you now. I will post it soon.

Regards,
Tibor
Solution
Tibor Lorantfy
Graphisoft Alumni
Graphisoft Alumni
In theory it's possible to get the panels in order, but unfortunately two bugs in the API prevent us to do that:
  • cWallPanelGridIDTable member of the API_ElementMemo is always nullptr. That member should contain the information about the positions of the panels in the grid.
  • API_CWPanelType structure does not contain the ID of the segment, so if the curtain wall has multiple segments then the panels cannot be sorted correctly (because each segment has individual grid and a gridCellID is not unique).

I added these bugs to our bug database.
The first will be fixed in an upcoming update for AC22 (buildnum higher than 5500).
The second one needs API interface modification: the filler_1[0] member of the API_CWPanelType structure will be reused, that will contain the index of the segment which contains the panel. I hope that modification can be done also in an upcoming update for AC22 (buildnum higher than 5500).

I will notify again you when the modifications were successfully done. After that you can use the following code to get the sorted panels:
GS::Array<API_CWPanelType> GetCurtainWallPanelsInOrder (const API_Guid& cwGuid)
{
	GS::Array<API_CWPanelType> result;

	API_Element		cwElement;
	BNZeroMemory (&cwElement, sizeof (API_Element));
	cwElement.header.guid = cwGuid;

	GSErrCode err = ACAPI_Element_Get (&cwElement);
	if (err == NoError) {
		API_ElementMemo	cwMemo;
		BNZeroMemory (&cwMemo, sizeof (API_ElementMemo));

		err = ACAPI_Element_GetMemo (cwElement.header.guid, &cwMemo, APIMemoMask_CWallSegments | APIMemoMask_CWallPanels);
		if (err == NoError && cwMemo.cWallSegments != nullptr && cwMemo.cWallPanelGridIDTable != nullptr) {
			GS::HashTable<API_Guid, API_CWPanelType> cwPanelTable;
			for (UIndex ii = 0; ii < cwElement.curtainWall.nPanels; ++ii) {
				const API_CWPanelType& cwPanel = cwMemo.cWallPanels[ii];
				cwPanelTable.Add (cwPanel.head.guid, cwPanel);
			}

			using CWPanelSegmentGridID = GS::Pair<UInt32, API_GridElemID>;
			GS::Array<CWPanelSegmentGridID> segmentGridIDs;
			GS::HashTable<CWPanelSegmentGridID, GS::Array<API_CWPanelType>> reversedCWPanelGridIDTable;
			for (auto it = cwMemo.cWallPanelGridIDTable->EnumeratePairs (); it != nullptr; ++it) {
				const API_CWPanelType& cwPanel = cwPanelTable[*it->key];
				const GS::Array<API_GridElemID>& cwPanelGridIDs = *it->value;

				for (auto it = cwPanelGridIDs.Enumerate (); it != nullptr; ++it) {
					const API_GridElemID& gridID = *it;

					CWPanelSegmentGridID segmentGridID (cwPanel.filler_1[0]/*cwPanel.segmentID*/, gridID);
					if (reversedCWPanelGridIDTable.ContainsKey (segmentGridID))
						reversedCWPanelGridIDTable[segmentGridID].Push (cwPanel);
					else
						reversedCWPanelGridIDTable.Add (segmentGridID, { cwPanel });

					segmentGridIDs.Push (segmentGridID);
				}
			}

			GS::Sort (segmentGridIDs.Begin (), segmentGridIDs.End (),
				[](const auto& lhs, const auto& rhs) {
					if (lhs.first == rhs.first)
						return lhs.second < rhs.second;
					return lhs.first < rhs.first;
				});

			for (auto it = segmentGridIDs.Enumerate (); it != nullptr; ++it) {
				const CWPanelSegmentGridID& segmentGridID = *it;
				result.Append (reversedCWPanelGridIDTable[segmentGridID]);
			}
		}

		ACAPI_DisposeElemMemoHdls (&cwMemo);
	}

	return result;
}
Anonymous
Not applicable
Interesting... I already stumbled upon cWallPanelGridIDTable in the code, but could not click on it in the docs and so didn't know how to use it - I guess this is related to the nullptr issue you mentioned.

Thanks a lot for the code already, it will be very useful as soon as I can use it

Have a great weekend!
- Dan

Didn't find the answer?

Check other topics in this Forum

Back to Forum

Read the latest accepted solutions!

Accepted Solutions

Start a new conversation!