Skip to content
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
Merged

Conversation

aothms
Copy link
Member

@aothms 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
Copy link
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
>>>

@aothms
Copy link
Member Author

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
Copy link
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.

@yorikvanhavre
Copy link
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 :)

@aothms aothms merged commit aebb9e3 into IfcOpenShell:master Jun 22, 2016
Andrej730 added a commit that referenced this pull request May 24, 2023
Currently "type" attribute in selector returns something like "#71=IfcWallType('20jY5HfLH4LBwMrDEoTdML',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.)" when the code was expecting the class name like "IfcSlab" or "IfcWall". Changed it to "class" atribute that returns just class name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants