Converting to status-coded polygon format

 Example objects for this post

A conversion is needed when you need to model data received from ARCHICAD as PolyOperations polygon, or when you have a status-coded polygon (a profile from a request for example) and need to do PolyOperations on it. This article explains the conversion from PolyOperations to status-coded polygon.

If there are no arcs in the polygon, it can be easily converted in a loop, deleting or adding status codes can be done by separating them into an edgeinfo array which can be handled by PolyOperations.

Converting arcs is not trivial, ProfileConverter macro is written to convert between the two polygon formats. It was written to work on status-coded polygons received from a profile request, so it only handles additional status codes -1, 900, 3000 and 4000 (and normal status codes 0-99). These are enough to describe any geometry, and generally these are the ones that can be used to define the geometry the easiest from the available parameters. It can handle one polygon only as input in one call. Multiple polygons can be converted in a loop. Closing points should always be given in the input and are always present in the output, with one exception: dict input closing points can be omitted. The macro has two modes: handling the PolyOperations polygon as an array or as a dictionary. This is set by the boolean parameter bPolyOpDict. This article will show you both the array and the dictionary handling as well.

Converting from arrays

Let’s work with the STAIR_TREAD_GEOMETRY global variable for a simple example. We will convert the global variable to extrude its geometry.

Showing array contents

First of all, create a library part as Model Element / Building Element / Design Stair / Stair 3D Component / Tread Component subtype.
In the 3D script, use the following code to print some debug information:

define style "debug" "Arial", 10, 1, 0
style "debug"

dim _printarray[][]
_printarray = STAIR_TREAD_GEOMETRY
_bThirdColumnInt = 0
gosub "printarray"
! -----------------------------
end
! -----------------------------

"printarray":
	addy -TREAD_THICKNESS
	rotx 90
	_prec = not(_bThirdColumnInt) * 2
	for i = 1 to vardim1(_printarray)
		addy -0.015
		text 0, 0, "[" + str(_printarray[i][1], 3, 2) + "\t " + str(_printarray[i][2], 3, 2) + "\t " +\
				 str(_printarray[i][3], _prec + 1, _prec) + "]"
	next i
	del i + 1
return

Lastly, place a stair without structure and risers, then select this library part for the Tread to see the geometric data received from ARCHICAD.

Using arrays to create prism_

Now let’s convert the geometric data to be able to model it using prism_. The contourends parameter of ProfileConverter should contain the end index of each contour in the polygon. A Tread can’t contain holes and can be a single polygon only, so it can be filled easily.

dim contourends[]
contourends[1] = vardim1(STAIR_TREAD_GEOMETRY)

dim outflat[]
call "ProfileConverter" parameters	bProfileToPolyOp	= 0,
					polygon			= STAIR_TREAD_GEOMETRY,
					contourends		= contourends,
		returned_parameters	nOut,
					outflat			! outpoly[][3], edgeinfo[]

When a macro returns multiple arrays, it will be received as a single one-dimensional array. We need some processing on it, because only status codes -1, 0, 900 and 4000 are used: the visibility status codes need to be set to 15 except on the final vertex. We are going to ignore the edgeinfo in this example. Also, let’s print the result of the conversion (the array format is the same, but the data has different meaning).

dim _printarray[][]
for i = 1 to nOut
	k = (i - 1) * 3
	_x = outflat[k + 1]
	_y = outflat[k + 2]
	_s = outflat[k + 3]
	if _s > -1 then _s = _s + 15
	put _x, _y, _s

	_printarray[i][1] = _x
	_printarray[i][2] = _y
	_printarray[i][3] = _s
next i

prism_ NSP / 3, -TREAD_THICKNESS, get(NSP)

addx 0.2
_bThirdColumnInt = 1
gosub "printarray"
del 1

Now, you can customize the tread contours in edit mode, and see how the data changes.
If you make the leading edge curved, our model has an unneeded extra part. One small correction is needed. In the input data the last edge has an angle, because it is a copy of the first. The macro doesn’t ignore this value, we need to set it to 0. A closing point is always needed in the array formats (and is always given in the result), this way the macro can handle open polylines too.

STAIR_TREAD_GEOMETRY[vardim1(STAIR_TREAD_GEOMETRY)][3] = 0

You can download the complete example SimpleTread 1.gsm file at top of the post.

Using arrays to create cprism_{3} with materials

Let’s rewrite the previous example by visualizing the edge flags. The edgeinfo array can be used to save flags or the vertex index in the input data to be able to pair them to converted edges. It refers to the edge starting at vertex. As we’ve seen in the previous example it’s optional, omitting the parameter will fill the output edgeinfo with zeros.

First let’s store the flag and print edgeinfo in our debug subroutine:

dim edgeinfo[]
for i = 1 to vardim1(STAIR_TREAD_FLAGS)
	edgeinfo[i] = STAIR_TREAD_FLAGS[i][1]
next i

dim _printarray[][]
_printarray = STAIR_TREAD_GEOMETRY
_bThirdColumnInt = 0
gosub "printarray"

! -----------------------------
end
! -----------------------------

"printarray":
	rotx 90
	addy -TREAD_THICKNESS
	_prec = not(_bThirdColumnInt) * 2
	for i = 1 to vardim1(_printarray)
		addy -0.015
		text 0, 0, "[" + str(_printarray[i][1], 3, 2) + "\t " + str(_printarray[i][2], 3, 2) + "\t " +\
				 str(_printarray[i][3], _prec + 1, _prec) + "] " + str(edgeinfo[i], 1, 0)
	next i
	del i + 1
return

The conversion call needs just one extra parameter:

dim outflat[]
call "ProfileConverter" parameters	bProfileToPolyOp	= 0,
					polygon			= STAIR_TREAD_GEOMETRY,
					edgeinfo		= edgeinfo,
					contourends		= contourends,
		returned_parameters	nOut,
					outflat			! outpoly[][3], edgeinfo[]

The resulting edgeinfo is at the end of the received flat array. We use a table to look up materials based on the value edgeinfo + 2. The data is put in the format expected by cprism_{3}

! material lookup table 
dim mats[5]
	mats[1] = 0
	mats[2] = matlead
	mats[3] = mattrail
	mats[4] = matleft
	mats[5] = matright

dim _printarray[][], edgeinfo[]
nCoords = 3 * nOut
for i = 1 to nOut
	k = (i - 1) * 3
	_x = outflat[k + 1]
	_y = outflat[k + 2]
	_s = outflat[k + 3]
	_info = outflat[nCoords + i]
	if _s > -1 then _s = _s + 15
	put _x, _y, 0, _s, mats[_info + 2]

	_printarray[i][1] = _x
	_printarray[i][2] = _y
	_printarray[i][3] = _s
	edgeinfo[i] = _info
next i

And finally model it:

cprism_{3} 0, 0, 0, 8,
	NSP / 5, -TREAD_THICKNESS, get(NSP)

addx 0.2
_bThirdColumnInt = 1
gosub "printarray"
del 1

Note that the default 0 edgeinfo doesn’t interfere with the leading edge 0 edgeinfo, because the new points in the status-coded polygon are the arc centers (900) and don’t have effect on the model using cprism_{3}. If you use edgeinfo for other purposes, be sure to reserve 0 for new edges.

You can download the complete example SimpleTread 2.gsm file at top of the post.

Converting from dictionaries

As mentioned earlier, the ProfileConverter can handle dictionaries as well. One of the global variables that has data as a dictionary is the Opening Symbol’s OPENING_SYMBOL_GEOMETRY.

Create a library part as Documentation Element / Drawing Symbol/ Opening Symbol subtype.
An Opening Symbol can’t display text, so we are printing in the session report window in this example.
The dictionary format doesn’t need to contain the endpoints of closed polylines/polygons. When the key .isClosed is true, the closing point is auto-detected with a fixed precision (1/10 mm), the conversion works with or without it too.

Use the following code in the 2D script.

if not(haskey(OPENING_SYMBOL_GEOMETRY.polygon2D)) then end
print OPENING_SYMBOL_GEOMETRY.polygon2D

! conversion to status-coded polygon 
dim outflat[]                   ! declare returned array
call "ProfileConverter" parameters  bProfileToPolyOp    = 0,
                                    bPolyOpDict         = 1,
                                    PolyOpPolygon       = OPENING_SYMBOL_GEOMETRY.polygon2D,
               returned_parameters  nOut,
                                    outflat		! outpoly[][3], edgeinfo[]

The expansion of the flat array is the same as in the previous example, only we add a different visibility status code for 2D.

dim _printarray[][]
for i = 1 to nOut
	k = (i - 1) * 3
	_x = outflat[k + 1]
	_y = outflat[k + 2]
	_s = outflat[k + 3]
	if _s > -1 then _s = _s + 1
	put _x, _y, _s

	_printarray[i][1] = _x
	_printarray[i][2] = _y
	_printarray[i][3] = _s
next i

_bThirdColumnInt = 1
gosub "printarray"

We draw a slightly shrunk polygon using coordinate transformations because the outlines are cut by ARCHICAD.

margin = 0.02
dict bb : bb = OPENING_SYMBOL_GEOMETRY.boundingBox2D
add2 margin, margin
mul2 1 - 2 * margin / (bb.xmax - bb.xmin),
     1 - 2 * margin / (bb.ymax - bb.ymin)

poly2_ NSP / 3, 1, get(NSP)

del 2

! -----------------------------
end
! -----------------------------

"printarray":
	_prec = not(_bThirdColumnInt) * 2
	for i = 1 to vardim1(_printarray)
		print "[" + str(_printarray[i][1], 3, 2) + "\t " + str(_printarray[i][2], 3, 2) + "\t " +\
				    str(_printarray[i][3], _prec + 1, _prec) + "] "
	next i
return

You can make a polygonal opening of any shape with a trick: make a polygonal slab, and create an opening that is larger than the slab itself. You can even draw holes in it, the conversion will handle the holes. However, they won’t be visible because of the coordinate transformation, and ARCHICAD cutting lines outside the opening. Change the sign of margin to make the holes visible only.
You can download the complete example SimpleOpening.gsm file at top of the post.