Element Overview

 

Applicable types of elements are enumerated in the API_ElemTypeID structure. The definition of an element is described in the API_Element structure, which is a union of all element types. All of the definitions begin with a common header structure named API_Elem_Head.

  • The typeID field identifies the type of the element.
  • The variationID field is an extension of typeID in case of API_ObjectID, API_LampID, API_WindowID, API_DoorID. It identifies the subcategory (e.g. Stair) of the element.
  • The guid field gives a globally unique identifier of the element, which is constant through the whole life of the project. These values are automatically generated and there is no way to control which unique ID is assigned.
  • The modiStamp field gives you a stamp to check if the element was modified.
  • The guid and groupGuid fields contain the grouping information. The first one identifies the group which the given element belongs to. The second one identifies the root-parent group in case of nested groups.
  • The floorInd field identifies the story that the element belongs to (not applicable to all element types).
  • The layer field is the attribute index of the layer selected for the element (not applicable to all element types).
  • The hasMemo field is a flag, which shows that the given element has additional data, described in the structure API_Element_Memo.
  • The drwIndex field determines the drawing order of elements, and can be adjusted by the user or from the API with the Send to Back/Bring to Front commands. Elements which have bigger drwIndex will be drawn later.

Every structure which describes a particular type of element (like API_WallType, API_LineType etc.) are logically separated into different parts.

  • The first part is the header. It contains common information which is element type independent.
  • The next part corresponds to the parameters which can be set in the tool setting dialog boxes or in the Info Box. They do not depend on the geometry, just describe general parameters like attributes, control flags and so on.
  • The last part corresponds to the user clicks done while the element was placed on the floor plan. This part is a pure geometrical description.

Many element related functions use the API_Element structure on the parameter list. As a general rule, you have to fill the required fields in the union. Archicad parses the request based on the values, and passes the return parameters in the same structure. This is why most of the functions do not have the const directive in their prototypes.


Retrieving an element from the database

Please note that from API 11 we start to move away from the typeID/index identification towards a GUID-based identification. In API 12 the GUID way has precedence. From API 18 the GUID way is only available, typeID/index identification was removed.

Let’s look at a simple example. Assume you are interested in getting the data of a given element of the database. In this case you should use a variable of type API_Element.


API_Element  element;
GSErrCode    err;

BNZeroMemory (&element, sizeof (API_Element));
element.header.guid = GSGuid2APIGuid (GS::Guid ("EF7A21F7-F841-4030-B6DC-C1DC8DA2F1E6"));

err = ACAPI_Element_Get (&element);

The data of the wall is returned in the element.wall part of the union. You should check the hasMemo field in the returned header. If it is set you are informed that additional data is available for the element, such as gables and polygon nodes. Since producing this data may be a relatively a long conversion process, this information can be obtained with an additional function call.


GSErrCode       err;
API_ElementMemo memo;

err = ACAPI_Element_GetMemo (element.header.guid, &memo, APIMemoMask_All);

In this case, you have got all of the additional data related to the particular element. The memo structure is a collection of several handles and pointer (dynamic memory). If any is allocated (i.e. its value is not nullptr it contains data. If you do not need the data any more you are required to free the allocated memory blocks. The suggested way is the following:


ACAPI_DisposeElemMemoHdls (&memo);

If you use this function, it is ensured that all of the dynamic data will be freed, and also there will be no compatibility problems if your add-on is installed to a newer version of Archicad.

It is very important to examine the error codes returned by the API functions, and interpret the returned value.

It is usual that you want to go through the instances of a particular type of element in the database. The following example gives a good template to do that:  12


GS::Array<API_Guid> elemList;
GSErrCode err = ACAPI_Element_GetElemList (API_WallID, &elemList);
for (Int32 i = 0; i < elemList.GetSize () && err == NoError; ++i) {
    API_Element element;
    BNZeroMemory (&element, sizeof (API_Element));
    element.header.guid = elemList[i];
    if (ACAPI_Element_Get (&element) == NoError) {
        /* do what you want */
    }
}

A better and suggested way to set up such a loop is to use the element filter service of the API. It is common that you are interested in elements being on the active story only or being on a visible layer, or just for editable elements, and so on. If you call the ACAPI_Element_Get function in a loop, Archicad is requested to convert all of the elements to API form, even if you do not need them. This algorithm can be very slow.

In the next example you see a very effective, fast way to get the elements only you are interested in:  12


GS::Array<API_Guid> elemList;
ACAPI_Element_GetElemList (API_WallID, &elemList, APIFilt_OnActFloor);
for (GS::Array<API_Guid>::ConstIterator it = elemList.Enumerate (); it != nullptr; ++it) {
    API_Element element;
    BNZeroMemory (&element, sizeof (API_Element));
    element.header.guid = *it;
    if (ACAPI_Element_Get (&element) == NoError) {
        /* do what you want */
    }
}

You now see that the APIERR_DELETED return code is not checked, since all elements are filtered which do not meet your needs as well as the deleted ones.

You can also enumerate elements by drawing order and creation order with the APIDb_DrawOrderGetNextID and APIDb_CreationOrderGetNextID database functions respectively.


Creating an element

The element creation procedure is also fairly simple. All you have to do is to fill the appropriate part of the API_Element and optionally, the API_ElementMemo structure. The ACAPI_Element_Create function does the following:

  • It checks the data for possible inconsistency. If some problem is encountered it may be corrected automatically (like an invalid attribute reference) or an error code is generated.
  • It ignores, for example, guid field in the header structure, because they are automatically generated. These values are returned to you in the appropriate field.
  • The generated element is placed in the data structure.

It is very important that Archicad does not free any dynamic data structure you have allocated and passed to Archicad. They must be freed by your code.

An other important note is the following. Every data structure filled by you must be initialized to zero, because of forward compatibility. Fillers may be used in the later versions of the API, so if they are not initialized to zero the result may be unpredictable.

The template to create an element should be the following:


API_Element     element;
API_ElementMemo memo;
API_Guid        wallGuid;
GSErrCode       err;

BNZeroMemory (&element, sizeof (API_Element));
BNZeroMemory (&memo, sizeof (API_ElementMemo));

element.header.typeID = API_WallID;
/* fill element.wall */
/* fill memo for polygonal walls */

err = ACAPI_Element_Create (&element, &memo);

wallGuid = element.header.guid;

ACAPI_DisposeElemMemoHdls (&memo);

Many elements refer to each other and these cross-references can be set up very easily. You just have to follow the general logic of Archicad, which means that referred elements must exist. For example first you create a wall, then windows are placed in it, and also you can assign associated labels.

You also have to do the same using the API functions. The ACAPI_Element_Create function returns the guid of the created element, which can be used in references. If you want to place a window in the created wall, and assign a label do the following:


BNZeroMemory (&element, sizeof (API_Element));
element.header.typeID = API_WindowID;

element.window.owner = wallGuid;
/* set other parameters... */

err = ACAPI_Element_Create (&element, nullptr);

BNZeroMemory (&element, sizeof (API_Element));
element.header.typeID = API_LabelID;

element.label.parent = wallGuid;
/* set other parameters... */

err = ACAPI_Element_Create (&element, nullptr);

Controlling the selection

You can control the active selection also.

There are two basic type of selection methods in Archicad:

  • The selection may be controlled by individually selected elements.
  • The other possibility is to rely on the marquee area.

Both of them are supported by the API. The possible values of the active selection method are enumerated in the API_SelTypeID structure. The API_SelEmpty identifier means that nothing is selected in the current database in Archicad. The API_SelElems identifier corresponds to the first method when elements are individually selected. Other values identify that the selection is done by some kind of marquee shape.

In case of individually selected elements, Archicad returns all the elements which are selected. In the case of marquee based selection, only those elements will be returned which have an intersection with, or are within the marquee area.

The element selection procedure is very easy. You just have to call the ACAPI_Element_Select function to select an individual element, or a group of elements. You have the possibility either to open a new selection, or append one more element to the actual selection.

 


Internal parts of an element: the API_Neig structure

Both the ACAPI_Element_Select and ACAPI_Selection_Get functions use the API_Neig structure. This data structure makes possible to distinguish the internal parts of the elements.

You can see how smart the cursor is, when you touch one of the element’s nodes or edges while moving it through the shape of a given element. All of these points or edges can be described by an API_Neig structure.

  • The neigID field identifies the element type. Correspondence can be set up between the possible values of API_NeigID and the API_ElemTypeID structures; the first one is a sub-kind of the second one. Use the APIAny_NeigToCoordID function for conversion purposes.
  • The guid field is the element guid.
  • The inIndex field identifies the internal index of special point or edge.
  • The flags field just gives information e.g. if the described point or edge belongs to a subcontour or the main contour of a polygonal element (API_NeigFlg_HoleSel).

Let’s see an example. According to the roofs the special interests are organized into three subclasses. They are:

 

neigID

inIndex range

Description

APINeig_Roof

[1…n]

Identifies one of the vertices of the roof polygon. The inIndex value corresponds to the position of the vertex in the vertex array. The holeSel bit in the flags is not set if the point belongs to the main contour of the roof polygon.

APINeig_RoofOn

[1…n]

Identifies one of the edges of the roof polygon. The inIndex value corresponds to the position of the starting vertex in the vertex array. The holeSel bit in the flags is set if the edge belongs to a subcontour of the roof polygon.

APINeig_RBL

[1…2]

Identifies the reference line of the roof. The inIndex value 1 and 2 means the start and end point of it, respectively.

When you go through the selected elements, you get the information which point or edge was clicked by the mouse when the selection was made. The only useful information according to the selection is whether a whole polygonal element is selected or just one of its sub-contours.

If you search the API_Neig structure in the APIdefs_Elements.h file, you will see that it is used in many places.

It is used to give you information on which point was clicked in the input functions; see the APIIo_GetPointID function.


The 3D model  obsolete

12 The recommended way of accessing the 3D Model is now illustrated in the ModelAccess example.

The 3D representation of the actual database can be accessed through the API_Component3D structure.

Applicable types of elements are enumerated in the API_3DTypeID structure. The definition of a 3D component is described in the API_Component3D structure, which is in fact a union of all component types. All of the definitions begins with a common header structure, API_3D_Head.

  • The typeID field identifies the type of the component.
  • The index field gives the current database index of the given component. Note that this number is not constant for a particular element through the whole life of the project.

Before you try to understand how the 3D model corresponds to the API structures, please read carefully the Primitive Elements chapter in the GDL Reference Manual.

The 3D representation of a particular element is based on the API_BodyType structure. One element may hold one or more bodies to describe the 3D geometry. Generally a body is built up from a number of vertices, edges, polygons and normal vectors. This data structure gives you the number of the internal components as well as some status bits, surface properties and a global transformation matrix. You also get a reference to the parent element through an API_Elem_Head structure.

The 3D geometry itself is described by the API_PgonType, API_PedgType, API_EdgeType, API_VertType and API_VectType structures. They are documented in the GDL Reference Manual.

The 3D representation also owns a common pool of the surface materials. The polygon instances of the geometry description refer to these materials by indices. One surface definition is described by the API_UmatType structure. Surface materials may come from two sources. The list of materials contains all the global material attributes which are referenced by any polygon of the 3D geometry. Materials may be defined in GDL scripts also.

Elements placed with the light tool on the floor plan may contain light emissions, defined via GDL scripts. All of the light sources present in the 3D geometry are described with the API_LghtType structure.


Getting the whole 3D model  obsolete

12 The recommended way of accessing the 3D Model is now illustrated in the ModelAccess example.

If you are interested in the whole 3D model that actually exists, you should use the following template:


API_Component3D   comp3D;
Int32             nBody, i;
GSErrCode         err;

err = ACAPI_3D_GetNum (API_BodyID, &nBody);
BNZeroMemory (&comp3D, sizeof (API_Component3D));
comp3D.header.typeID = API_BodyID;
for (i = 1; i <= nBody && err == NoError; i++) {
    comp3D.header.index = i;
    err = ACAPI_3D_GetComponent (&comp3D);
    if (err == NoError) {
        /* explode the body into pgon, vert etc... */
    }
    if (err == APIERR_DELETED)
        err = NoError;
}

It is very important to organize your algorithm on a loop based on the component API_BodyType for the following reasons:

  • Body indices are continuous in all cases, others are not.
  • The 3D model also can have data from deleted floor plan elements. They can be identified only through the deleted bodies.
  • Floor plan elements which have the same geometry may share the primitives of the 3D representation data, but not the bodies. All the bodies contain a transformation matrix which transforms the referenced shared vertices and normal vectors to the right places and directions.

Once you have the definition of a body, you can set up inner loops to go through the primitives you are interested in. In the API_BodyType record you get the number of all internal components. Indices should start from 1.

The light and material components can be obtained in their own loops, because they are global (not body relative) components of the 3D model.

It is very important, that you get the data of the 3D model of Archicad that actually exists. This data is often not consistent with the floor plan elements. The update mechanism of the 3D window depends on many things, such as the switches in the preferences dialogs. As a general rule, you get the model which can be seen in the 3D window, if you use the above template.


Getting the 3D representation of a particular element

You also have the possibility to get the 3D representation of any particular floor plan element you are interested for. The basic mechanism is the same as in the previous example. The only difference is that you need the information which bodies belong to the given element.

Bodies assigned to a specific floor plan element can be obtained with the function ACAPI_Element_Get3DInfo. This function fills an API_ElemInfo3D structure to pass the index range of the main 3D data containers.

  • The fbody and lbody field defines the first and last body indices. This range of indices should be used to get the body definitions.
  • The flight and llight fields defines the first and last light indices. The 3D GDL script of objects may contain the LIGHT commands which determines that the given element has light emission. This data can be obtained through the API_LghtType, based on these indices.

Important note: in this way the data you get is independent from the actually existing 3D model of Archicad. If the existing data is not consistent, Archicad converts the given element to 3D again to ensure the right result. As a general rule, you get the model which is fully synchronized to the actual floor plan data.

You should choose between the above mentioned ways to get the 3D model very carefully.

If you need consistent data you should choose the second way, setting up a loop which goes through the floor plan elements you are interested for. In this case you can get the 3D model element by element.

You often have to choose the first method. For example, if your add-on is called from the “Save As 3D” dialog, the data user wants to save is the data actually can be seen in the 3D window.