xBimTeam / XbimGeometry

XbimGeometry contains the CLR interop libraries and the c++ engine used to compute the 3D geometry of models.
https://xbimteam.github.io/
Other
260 stars 131 forks source link

[Question] Get opening direction from IfcDoor? #410

Closed emilaz closed 1 year ago

emilaz commented 1 year ago

I'm attempting to get the opening direction from an IfcDoor. As per the documentation, this should be equal to the Y axis of ObjectPlacement. However, it seems that the Y axis of ObjectPlacement does not return what one would expect (i.e. the opening direction of the door). Rather, it always returns one of (0,1,0) or (0,-1,0), which from my intuition is not correct for doors that are not perfectly horizontally placed. For example given a vertically placed door (opening to the left or right), this should return (-1,0,0) and (1,0,0), respectively. Am I looking in the wrong place?

Assemblies and versions affected:

Xbim.Ifc4.Interfaces

Steps (or code) to reproduce the issue:

Code to execute given a door

using Xbim.Ifc4.Interfaces;

public XBimVector3D FacingOrientation
{
    get
    {
        // return ortho;
        var placement = _door.ObjectPlacement as IIfcLocalPlacement;
        var axes = placement.RelativePlacement as IIfcAxis2Placement3D;
        var orientation = axes.P[1]; // as per IfcDoor definition, see buildingSmart documentation
        return orientation
    }
}

Minimal file to reproduce the issue:

Simply consider the two entrance doors here. HOTEL_2_2_floors.zip

Expected behavior:

Entrance doors having an opening direction of (-1,0,0) and (1,0,0), respectively.

Actual behavior or exception details:

Opening direction of (0,1,0).

Additional Details

Note that I'm NOT looking for the vector pointing towards the hinge of the door. That is given by the associated IfcDoorType rather than the IfcDoor itself anyways.

mathieuwybrecht commented 1 year ago

You missed the "relative" part of the RelativePlacement.

My guess is that your placement is relative to the wall placement. Your wall placement may be relative to the BuildingStorey placement and so on ...

In IFC, every representation part may be relative to something, and that is quite difficult sometimes to figure out what every vertex or vector may be in a global coordinates system.

emilaz commented 1 year ago

Does that mean that I have to foreach loop each and every object for its parents' orientations if I want to find its own orientation? That doesn't sound like the most efficient and robust way to go about it. Do you happen to have a routine at hand that would give me the door's absolute orientation by any chance?

mathieuwybrecht commented 1 year ago

yep, dealing with things that are all relatives to each others may look like a real pain, but in fact it is quite straightforward as you only have to get what let you change coordinates from a coordinates system to another parent coordinates system.

That is all for the matematic part, and I may have a piece of code that does something quite similar, at the end of this reply. But don't go there to fast, as you may be interested by another way to put your hands on the transformation that you are searching for.

As you may already know, xBim process all those kind of complex representation data inside its geometry engine, and in the end is able to provide already processed shapes and geometric infos (in order to show them for example). So if you happen to put your hand on the particular shapeinstance that is related to your door, chances are high (but without any guarantees) that its shape is drawned like it is perfectly aligned on a cartesian coordinates system, then transformed to be displayed at the right place and with the proper orientation.

There are the two pieces of code you may be interested to look at :

    public static XbimMatrix3D? GetTransformationFromShapeInstance(IfcStore model,  IIfcProduct p)
    {
        var geomContext = new Xbim3DModelContext(model);
        geomContext.CreateContext();
        var instancesOfProduct = geomContext.ShapeInstances().Where(instance => instance.IfcProductLabel == p.EntityLabel).ToList();
        return instancesOfProduct.FirstOrDefault()?.Transformation;
    }

    public static XbimMatrix3D GetTransformationFromRelativePlacement(IIfcProduct product)
    {
        if (product?.ObjectPlacement as IIfcLocalPlacement == null) { return XbimMatrix3D.Identity; }

        List<XbimMatrix3D> matrixList = new List<XbimMatrix3D>();
        XbimMatrix3D result = new XbimMatrix3D();
        IIfcLocalPlacement lp = product.ObjectPlacement as IIfcLocalPlacement;
        IIfcLocalPlacement relPlace = lp.PlacementRelTo as IIfcLocalPlacement;

        XbimMatrix3D objTr = lp.RelativePlacement.ToMatrix3D();
        matrixList.Add(objTr);

        while (relPlace != null)
        {
            XbimMatrix3D tr = relPlace.RelativePlacement.ToMatrix3D();
            matrixList.Add(tr);
            relPlace = relPlace.PlacementRelTo as IIfcLocalPlacement;
        }

        foreach (XbimMatrix3D m in matrixList) { result = XbimMatrix3D.Multiply(result, m); }

        return result;
    }

Please note that they are not clean pieces of code, they require to be recrafted

emilaz commented 1 year ago

Thanks so much! I went with the more robust second version (not a fan of "chances are high" pieces of code 😄 ). For future reference, to then get the opening direction, use

XbimMatrix3D axes = GetTransformationFromRelativePlacement(_door);
var openingDirection= axes.Up;