New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geometry serialization #71

Merged
merged 3 commits into from Jun 22, 2016

Conversation

Projects
None yet
2 participants
@aothms
Member

aothms commented May 19, 2016

#60

Serialization of arbitrary Open Cascade boundary representation (for example, from STEP data) to IFC. Due to the difference in required semantics, a 1-1 automatic conversion is not possible. The python script below creates the required IFC semantics (project->site->building decomposition) and transfers the STEP shape to IFC.

STEP
step file
IFC
ifc file

STEP
step file
IFC
ifc -> obj

Python script

import ifcopenshell
import ifcopenshell.geom

import time
import uuid
import tempfile

import OCC.gp
import OCC.BRepPrimAPI
import OCC.BRepAlgoAPI

from OCC.STEPControl import STEPControl_Reader
from OCC.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity

# Code below inspired by:
# http://academy.ifcopenshell.org/creating-a-simple-wall-with-property-set-and-quantity-information/

O = 0., 0., 0.
X = 1., 0., 0.
Y = 0., 1., 0.
Z = 0., 0., 1.

def get_shape_from_step(fn):
    step_reader = STEPControl_Reader()
    status = step_reader.ReadFile(fn)
    if status == IFSelect_RetDone:
        step_reader.TransferRoot(1)
        if step_reader.NbShapes() >= 1:
            return step_reader.Shape(1)

# Creates an IfcAxis2Placement3D from Location, Axis and RefDirection specified as Python tuples
def create_ifcaxis2placement(ifcfile, point=O, dir1=Z, dir2=X):
    point = ifcfile.createIfcCartesianPoint(point)
    dir1 = ifcfile.createIfcDirection(dir1)
    dir2 = ifcfile.createIfcDirection(dir2)
    axis2placement = ifcfile.createIfcAxis2Placement3D(point, dir1, dir2)
    return axis2placement

# Creates an IfcLocalPlacement from Location, Axis and RefDirection, specified as Python tuples, and relative placement
def create_ifclocalplacement(ifcfile, point=O, dir1=Z, dir2=X, relative_to=None):
    axis2placement = create_ifcaxis2placement(ifcfile,point,dir1,dir2)
    ifclocalplacement2 = ifcfile.createIfcLocalPlacement(relative_to,axis2placement)
    return ifclocalplacement2

# Creates a base64 encoded GlobalId string
create_guid = lambda: ifcopenshell.guid.compress(uuid.uuid1().hex)

# IFC template creation

filename = "step_serialize.ifc"
timestamp = time.time()
timestring = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(timestamp))
creator = "Thomas Krijnen"
organization = "AECgeeks"
application, application_version = "IfcOpenShell", "0.5"
project_globalid, project_name = create_guid(), "Serialize TopoDS"
schema_version = ifcopenshell.schema_identifier

# A template IFC file to quickly populate entity instances for an IfcProject with its dependencies
template = """ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
FILE_NAME('%(filename)s','%(timestring)s',('%(creator)s'),('%(organization)s'),'%(application)s','%(application)s','');
FILE_SCHEMA(('%(schema_version)s'));
ENDSEC;
DATA;
#1=IFCPERSON($,$,'%(creator)s',$,$,$,$,$);
#2=IFCORGANIZATION($,'%(organization)s',$,$,$);
#3=IFCPERSONANDORGANIZATION(#1,#2,$);
#4=IFCAPPLICATION(#2,'%(application_version)s','%(application)s','');
#5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,$,#3,#4,%(timestamp)s);
#6=IFCDIRECTION((1.,0.,0.));
#7=IFCDIRECTION((0.,0.,1.));
#8=IFCCARTESIANPOINT((0.,0.,0.));
#9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
#10=IFCDIRECTION((0.,1.,0.));
#11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
#12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
#13=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#14=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#15=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#16=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
#18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
#19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
#20=IFCPROJECT('%(project_globalid)s',#5,'%(project_name)s',$,$,$,$,(#11),#19);
ENDSEC;
END-ISO-10303-21;
""" % locals()

# Write the template to a temporary file 
temp_handle, temp_filename = tempfile.mkstemp(suffix=".ifc")
with open(temp_filename, "wb") as f:
    f.write(template)

# Obtain references to instances defined in template
ifcfile = ifcopenshell.open(temp_filename)
owner_history = ifcfile.by_type("IfcOwnerHistory")[0]
project = ifcfile.by_type("IfcProject")[0]
context = ifcfile.by_type("IfcGeometricRepresentationContext")[0]

context.Precision = 0.001

# Initialize display
d = ifcopenshell.geom.utils.initialize_display()
def display_shape(s):
    ifcopenshell.geom.utils.display_shape(s)
    d.FitAll()
    raw_input()
    d.EraseAll()


###############################

# Obtain shape from step
step_shape = get_shape_from_step("LOCAL2.stp")
display_shape(step_shape)

# Serialize to IFC instances
shape_representation = ifcfile.add(ifcopenshell.geom.serialise(step_shape))

# Link to representation context
for rep in shape_representation.Representations:
    rep.ContextOfItems = context

# IFC hierarchy creation
site_placement = create_ifclocalplacement(ifcfile)
site = ifcfile.createIfcSite(
    GlobalId        = create_guid(),
    OwnerHistory    = owner_history, 
    ObjectPlacement = site_placement
)

# Create building and assign representation
building_placement = create_ifclocalplacement(ifcfile, relative_to=site_placement)
building = ifcfile.createIfcBuilding(
    GlobalId        = create_guid(),
    OwnerHistory    = owner_history, 
    ObjectPlacement = building_placement,
    Representation  = shape_representation
)    

# Write output file
ifcfile.write(filename)

###############################

# Now open the IFC file and display again

f = ifcopenshell.open(filename)
b = f.by_type("IfcBuilding")[0]

s = ifcopenshell.geom.settings()
s.set(s.USE_PYTHON_OPENCASCADE, True)

ifc_shape = ifcopenshell.geom.create_shape(s, b)
display_shape(ifc_shape)
@yorikvanhavre

This comment has been minimized.

Show comment
Hide comment
@yorikvanhavre

yorikvanhavre May 29, 2016

Contributor

This looks crazily AWESOME Thomas!! No other IFC app will have that feature!

Does the serializer absolutely need pythonOCC? it gives me some error here, not sure if it is related or not...

>>> shapestr
'Shape : 34, FORWARD, location : 1\n\nDump of 34 TShapes\n\n------ [cut]
>>> import ifcopenshell
>>> from ifcopenshell import geom
PythonOCC not found - some functionality is disabled (I added this in pull request #75 )
>>> geom.serialise(shapestr)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/ifcopenshell/geom/main.py", line 97, in _
    return entity_instance_or_none(fn(string, *args))
RuntimeError: An unknown error occurred
>>>
Contributor

yorikvanhavre commented May 29, 2016

This looks crazily AWESOME Thomas!! No other IFC app will have that feature!

Does the serializer absolutely need pythonOCC? it gives me some error here, not sure if it is related or not...

>>> shapestr
'Shape : 34, FORWARD, location : 1\n\nDump of 34 TShapes\n\n------ [cut]
>>> import ifcopenshell
>>> from ifcopenshell import geom
PythonOCC not found - some functionality is disabled (I added this in pull request #75 )
>>> geom.serialise(shapestr)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/ifcopenshell/geom/main.py", line 97, in _
    return entity_instance_or_none(fn(string, *args))
RuntimeError: An unknown error occurred
>>>
@aothms

This comment has been minimized.

Show comment
Hide comment
@aothms

aothms May 29, 2016

Member

Thanks Yorik :) Hope you can put it to good use. Not exactly sure. In principal pythonOCC is not required, but to be honest have not tested without it. The error seems to suggest that somewhere in the C++ code an exception is thrown. Maybe you can provide the entire contents of shapestr? There might still be bugs in this code and for example as a result an OCC Standard_ConstructionError may be thrown, which is caught by the wrapper and returned as a non-descriptive error. Thanks for testing.

Member

aothms commented May 29, 2016

Thanks Yorik :) Hope you can put it to good use. Not exactly sure. In principal pythonOCC is not required, but to be honest have not tested without it. The error seems to suggest that somewhere in the C++ code an exception is thrown. Maybe you can provide the entire contents of shapestr? There might still be bugs in this code and for example as a result an OCC Standard_ConstructionError may be thrown, which is caught by the wrapper and returned as a non-descriptive error. Thanks for testing.

@yorikvanhavre

This comment has been minimized.

Show comment
Hide comment
@yorikvanhavre

yorikvanhavre May 29, 2016

Contributor

Ok, I'll make a series of test shapes for us to test... I'll also build OCC to see if there's any difference.

Contributor

yorikvanhavre commented May 29, 2016

Ok, I'll make a series of test shapes for us to test... I'll also build OCC to see if there's any difference.

@yorikvanhavre

This comment has been minimized.

Show comment
Hide comment
@yorikvanhavre

yorikvanhavre Jun 1, 2016

Contributor

Oho, I wasn't using the right way to obtain shape data from FreeCAD... Got it working now, this is amazing!!!

for who is interested to test from FreeCAD, this works with any Part-derived object:

ifcopenshell.geom.serialise( FreeCAD.ActiveDocument.myObject.Shape.exportBrepToString() )

I'll adapt the IFC exporter of FreeCAD to use this, so we can produce some baffling examples :)

Contributor

yorikvanhavre commented Jun 1, 2016

Oho, I wasn't using the right way to obtain shape data from FreeCAD... Got it working now, this is amazing!!!

for who is interested to test from FreeCAD, this works with any Part-derived object:

ifcopenshell.geom.serialise( FreeCAD.ActiveDocument.myObject.Shape.exportBrepToString() )

I'll adapt the IFC exporter of FreeCAD to use this, so we can produce some baffling examples :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment