Tag Archives: LCS

IfcOpenShell – Placement

If previous article we used IfcOpenShell’s (IOS) to read an ifc geometry and convert it to a brep. When we read wikilab.ifc everything seemed to be at the right place but was it really ? When you use BIM in your project coordinates is always a subject to correctly discuss. In that matter I recommend you to read Dion Moult’s article IFC Coordinate Reference Systems and Revit and references cited in the article.

IfcOpenShell version used : 0.6.0a1

Placement in IFC schema

A geometry generally has its own Local Coordinate System (LCS). Why ? Take for example an air terminal. You often use a few types of air terminal in a project. The same air terminal geometry is replicated multiple time in each space. If your geometry is defined with World Coordinate System (WCS) you need to define a new geometry for each one. If your geometry is defined in its own LCS you can use same geometry everywhere and give its placement relative to another reference.

In ifc schema, an air terminal is a IfcAirTerminal (IFC4) or an IfcFlowTerminal (IFC2x3). Both are inherited from IfcProduct (as walls, windows, ducts, pipes etc…) which has an ObjectPlacement attribute of type IfcObjectPlacement.

Placement of the product in space, the placement can either be absolute (relative to the world coordinate system), relative (relative to the object placement of another product), or constraint (e.g. relative to grid axes). It is determined by the various subtypes of IfcObjectPlacement, which includes the axis placement information to determine the transformation for the object coordinate system.

ObjectPlacement attribute definition from IFC documentation

IfcLocalPlacement subtype, which I think is the most common one, has 2 attributes :

  • #1 PlacementRelTo of type IfcObjectPlacement. If filled placement is relative. If empty placement use WCS.
  • #2 RelativePlacement is basically a 2D or 3D coordinate.

Relative placement can be chained eg. AirTerminal << space << buildingstorey << building << Site. To retrieve an object placement relative to WCS you need to transform your first relative placement through the complete chain.

Placement in IfcOpenShell

IfcOpenShell can be used as a parser to get placement exactly as defined in IfcSchema. However it also has handy tools to avoid crawling the whole relative placement chain. The option to look at to understand this is USE_WORLD_COORDS :

/// Specifies whether to apply the local placements of building elements
/// directly to the coordinates of the representation mesh rather than
/// to represent the local placement in the 4x3 matrix, which will in that
/// case be the identity matrix.
USE_WORLD_COORDS = 1 << 1,

When you display geom as we did in previous articles coordinates were given using the geometry LCS. It was not visible as our wall placement was at coordinates 0,0,0. Let’s now use the script from ifcopenshell academy Creating a simple wall with property set and quantity information to generate a new wall with a placement at 20,10,3. By default, script also generate a wall at 0,0,0. To modify it’s relative placement we need to modify following line :

wall_placement = create_ifclocalplacement(ifcfile, relative_to=storey_placement)

Considering function definition we need to replace this line with :

wall_placement = create_ifclocalplacement(ifcfile, point=(20., 10., 3.), relative_to=storey_placement)

Now if you try to load geometry from this newly created hello_wall.ifc according to our previous script you will see that the wall is still at 0,0,0 which is wrong.

Wall incorrectly placed

To place it correctly we have 2 option using matrix transformation and USE_WORLD_COORDINATES settings.

Using placement matrix

As stated in its source code when you create a geom. Its location is given by a 4×3 matrix. But care, when displayed as a tuple IfcOpenShell matrix and FreeCAD matrix are transposed :

  • IfcOpenShell matrix values is a tuple of 4 consecutive vector’s xyz :
    format : (v1.x, v1.y, v1.z, v2.x, v2.y … , v4.z)
  • FreeCAD matrix constructor takes up to 16 float. 4 vectors grouped by x, y, z values :
    format : (v1.x, v2.x, v3.x, v4.x, v1.y, … , v4.z, 0, 0, 0, 1)

About the last 0, 0, 0, 1 of FreeCAD matrix. As greatly stated in matrices chapter from the OpenGL tutorial :

This will be more clear soon, but for now, just remember this :
If w == 1, then the vector (x,y,z,1) is a position in space.
If w == 0, then the vector (x,y,z,0) is a direction.
(In fact, remember this forever.)

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/

So we’ll use a function to transpose an IfcOpenShell matrix to a FreeCAD matrix :

def ios_to_fc_matrix(ios_matrix):
    m_l = list()
    for i in range(3):
        line = list(ios_matrix[i::3])
        line[-1] *= SCALE
        m_l.extend(line)
    return FreeCAD.Matrix(*m_l)

After our shape creation we call then call it to give our object a placement :

    # Create FreeCAD shape from Open Cascade BREP
    fc_shape = Part.Shape()
    fc_shape.importBrepFromString(occ_shape)
    
    # Ifc lenght internal unit : meter. FreeCAD internal unit : mm.
    fc_shape.scale(SCALE)
    # Shape scale must be applied before shape placement else placement scale would be doubled
    fc_shape.Placement = ios_shape_to_fc_placement(ios_shape)

This way we get a correctly placed wall :

Wall placement using matrix transformation

USE_WOLD_COORDINATES

To define settings still the same process :

settings.set(settings.USE_WORLD_COORDS, True)

We can keep the same code base as it will apply the identity matrix which doesn’t change anything. By running the script you’ll get again a correctly placed wall but this time position will still be 0, 0, 0 which means that the geometry vertices coordinates are defined with their absolute coordinates :

Wall placement using USE_WORLD_COORDINATES setting

For reason explained in first part I tend to say that this solution is not the best choice for CAD/BIM work in HVAC domain. Currently FreeCAD import and export ifc this way I will investigate to see why and maybe launch a this topic on forum.

Full source code

Source code is available in ifc_placement.py