mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 10:33:20 +00:00
Added test submodule
This commit is contained in:
+8
-8
@@ -1,7 +1,7 @@
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
os: linux
|
||||
dist: xenial
|
||||
dist: bionic
|
||||
sudo: required
|
||||
|
||||
before_install:
|
||||
@@ -17,11 +17,11 @@ install:
|
||||
build-essential cmake python2.7 libpython2.7-dev swig zlib1g liblzma5 opencollada-dev wget apt-transport-https
|
||||
- sudo mkdir -p /usr/include/json/nlohmann/
|
||||
- sudo wget https://github.com/nlohmann/json/releases/download/v3.6.1/json.hpp -O /usr/include/json/nlohmann/json.hpp
|
||||
- sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
|
||||
- sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
|
||||
- sudo apt-get update -qq && sudo apt-get install -y dart
|
||||
- export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
|
||||
- git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
|
||||
# - sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
|
||||
# - sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
|
||||
# - sudo apt-get update -qq && sudo apt-get install -y dart
|
||||
# - export PATH=$PATH:/usr/lib/dart/bin:$HOME/.pub-cache/bin
|
||||
# - git clone https://github.com/KhronosGroup/glTF-Validator && pushd glTF-Validator && pub get && pub global activate --source path ./ && popd
|
||||
|
||||
script:
|
||||
- pwd
|
||||
@@ -36,7 +36,7 @@ script:
|
||||
-DPYTHON_INCLUDE_DIR=/usr/include/python2.7 \
|
||||
-DPYTHON_EXECUTABLE=/usr/bin/python2.7 \
|
||||
-DLIBXML2_INCLUDE_DIR=/usr/include/libxml2 \
|
||||
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.so;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.55;/usr/lib/x86_64-linux-gnu/libicudata.so.55" \
|
||||
-DLIBXML2_LIBRARIES="/usr/lib/x86_64-linux-gnu/libxml2.so;/lib/x86_64-linux-gnu/libz.so.1;/lib/x86_64-linux-gnu/liblzma.so.5;/usr/lib/x86_64-linux-gnu/libicuuc.so.60;/usr/lib/x86_64-linux-gnu/libicudata.so.60" \
|
||||
-DGLTF_SUPPORT=On \
|
||||
-DJSON_INCLUDE_DIR=/usr/include/json \
|
||||
..
|
||||
@@ -48,7 +48,7 @@ script:
|
||||
- cd input
|
||||
|
||||
- /usr/local/bin/IfcConvert -yv acad2010_walls.ifc acad2010_walls.glb
|
||||
- gltf_validator acad2010_walls.glb
|
||||
# - gltf_validator acad2010_walls.glb
|
||||
|
||||
- /usr/bin/python2.7 -c "from __future__ import print_function; from io import open; import ifcopenshell; f = ifcopenshell.open('encoding.ifc'); assert list(map(ord, f[1][0])) == [39, 97, 39, 32, 49, 109, 179, 32, 8804, 32, 53, 109, 179, 32, 8805, 32, 49, 48, 109, 179]"
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ ENDIF()
|
||||
|
||||
SET(OPENCASCADE_LIBRARY_NAMES
|
||||
TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO
|
||||
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset
|
||||
TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES TKOffset TKHLR
|
||||
)
|
||||
|
||||
IF("${OCC_LIBRARY_DIR}" STREQUAL "")
|
||||
|
||||
+57
-33
@@ -78,7 +78,6 @@ class CA2IFC:
|
||||
ifcElements[i] = self.f.createIfcStructuralCurveMember(self.guid(), ownerHistory, el['name'], None, None, localPlacement, prodDefShape, el['predefinedType'], localZAxis)
|
||||
|
||||
if el['geometryType'] == 'surface':
|
||||
# element
|
||||
ifcElements[i] = self.f.createIfcStructuralSurfaceMember(self.guid(), ownerHistory, el['name'], None, None, localPlacement, prodDefShape, el['predefinedType'], el['thickness'])
|
||||
|
||||
# create structural point connections
|
||||
@@ -86,19 +85,34 @@ class CA2IFC:
|
||||
for i,conn in enumerate(self.data['connections']):
|
||||
# geometry - product definition shape
|
||||
prodDefShape = self.create_geometry(conn)
|
||||
# local axes
|
||||
localAxes = self.create_orientation(conn['orientation'])
|
||||
|
||||
# boundary conditions
|
||||
if conn['appliedCondition']:
|
||||
bc = self.create_node_applied_conditions(conn['appliedCondition'])
|
||||
appliedCondition = self.f.createIfcBoundaryNodeCondition(None, bc['dx'], bc['dy'], bc['dz'], bc['drx'], bc['dry'], bc['drz'])
|
||||
bc = self.create_applied_conditions(conn['appliedCondition'], conn['geometryType'])
|
||||
if conn['geometryType'] == 'point':
|
||||
appliedCondition = self.f.createIfcBoundaryNodeCondition(None, bc['dx'], bc['dy'], bc['dz'], bc['drx'], bc['dry'], bc['drz'])
|
||||
if conn['geometryType'] == 'line':
|
||||
appliedCondition = self.f.createIfcBoundaryEdgeCondition(None, bc['dx'], bc['dy'], bc['dz'], bc['drx'], bc['dry'], bc['drz'])
|
||||
if conn['geometryType'] == 'surface':
|
||||
appliedCondition = self.f.createIfcBoundaryFaceCondition(None, bc['dx'], bc['dy'], bc['dz'])
|
||||
else:
|
||||
appliedCondition = None
|
||||
|
||||
# connection
|
||||
if conn['geometryType'] == 'point':
|
||||
# local axes
|
||||
localAxes = self.create_orientation(conn['orientation'])
|
||||
# connection
|
||||
ifcConnections[i] = self.f.createIfcStructuralPointConnection(self.guid(), ownerHistory, conn['name'], None, None, localPlacement, prodDefShape, appliedCondition, localAxes)
|
||||
|
||||
if conn['geometryType'] == 'line':
|
||||
# z axis TODO: group by elements
|
||||
localZAxis = self.f.createIfcDirection(tuple(conn['orientation'][2]))
|
||||
# connection
|
||||
ifcConnections[i] = self.f.createIfcStructuralCurveConnection(self.guid(), ownerHistory, conn['name'], None, None, localPlacement, prodDefShape, appliedCondition, localZAxis)
|
||||
|
||||
if conn['geometryType'] == 'surface':
|
||||
ifcConnections[i] = self.f.createIfcStructuralSurfaceConnection(self.guid(), ownerHistory, conn['name'], None, None, localPlacement, prodDefShape, appliedCondition)
|
||||
|
||||
# assign material-profile-sets
|
||||
for i,mpSet in enumerate(mpSets):
|
||||
groupOfElements = []
|
||||
@@ -121,20 +135,34 @@ class CA2IFC:
|
||||
# create connections with elements
|
||||
for i,el in enumerate(self.data['elements']):
|
||||
for conn in el['connections']:
|
||||
localAxes = self.create_orientation(conn['orientation'])
|
||||
j = [c['ifcName'] for c in self.data['connections']].index(conn['relatedConnection'])
|
||||
geometryType = self.data['connections'][j]['geometryType']
|
||||
|
||||
if conn['appliedCondition']:
|
||||
bc = self.create_node_applied_conditions(conn['appliedCondition'])
|
||||
appliedCondition = self.f.createIfcBoundaryNodeCondition(None, bc['dx'], bc['dy'], bc['dz'], bc['drx'], bc['dry'], bc['drz'])
|
||||
bc = self.create_applied_conditions(conn['appliedCondition'], geometryType)
|
||||
if geometryType == 'point':
|
||||
appliedCondition = self.f.createIfcBoundaryNodeCondition(None, bc['dx'], bc['dy'], bc['dz'], bc['drx'], bc['dry'], bc['drz'])
|
||||
if geometryType == 'line':
|
||||
appliedCondition = self.f.createIfcBoundaryEdgeCondition(None, bc['dx'], bc['dy'], bc['dz'], bc['drx'], bc['dry'], bc['drz'])
|
||||
if geometryType == 'surface':
|
||||
appliedCondition = self.f.createIfcBoundaryFaceCondition(None, bc['dx'], bc['dy'], bc['dz'])
|
||||
else:
|
||||
appliedCondition = None
|
||||
j = [c['ifcName'] for c in self.data['connections']].index(conn['relatedConnection'])
|
||||
if not conn['eccentricity']:
|
||||
|
||||
# local axes
|
||||
localAxes = self.create_orientation(conn['orientation'])
|
||||
|
||||
if geometryType == 'point':
|
||||
if not conn['eccentricity']:
|
||||
self.f.createIfcRelConnectsStructuralMember(self.guid(), ownerHistory, None, None, ifcElements[i], ifcConnections[j], appliedCondition, None, None, localAxes)
|
||||
else:
|
||||
pointOnElement = self.f.createIfcCartesianPoint(tuple(conn['eccentricity']['pointOnElement']))
|
||||
vector = conn['eccentricity']['vector']
|
||||
connPointEcc = self.f.createIfcConnectionPointEccentricity(pointOnElement, None, vector[0], vector[1], vector[2])
|
||||
self.f.createIfcRelConnectsWithEccentricity(self.guid(), ownerHistory, None, None, ifcElements[i], ifcConnections[j], appliedCondition, None, None, localAxes, connPointEcc)
|
||||
|
||||
if geometryType in ['line', 'surface']:
|
||||
self.f.createIfcRelConnectsStructuralMember(self.guid(), ownerHistory, None, None, ifcElements[i], ifcConnections[j], appliedCondition, None, None, localAxes)
|
||||
else:
|
||||
pointOnElement = self.f.createIfcCartesianPoint(tuple(conn['eccentricity']['pointOnElement']))
|
||||
vector = conn['eccentricity']['vector']
|
||||
connPointEcc = self.f.createIfcConnectionPointEccentricity(pointOnElement, None, vector[0], vector[1], vector[2])
|
||||
self.f.createIfcRelConnectsWithEccentricity(self.guid(), ownerHistory, None, None, ifcElements[i], ifcConnections[j], appliedCondition, None, None, localAxes, connPointEcc)
|
||||
|
||||
# assign elements and connections to group
|
||||
self.f.createIfcRelAssignsToGroup(self.guid(), ownerHistory, None, None, tuple(ifcElements + ifcConnections), None, model)
|
||||
@@ -287,38 +315,34 @@ class CA2IFC:
|
||||
|
||||
return faceProdDefShape
|
||||
|
||||
def create_node_applied_conditions(self, bc):
|
||||
def create_applied_conditions(self, bc, geometryType):
|
||||
for dof in ['dx', 'dy', 'dz']:
|
||||
if isinstance(bc[dof], bool):
|
||||
bc[dof] = self.f.createIfcBoolean(bc[dof])
|
||||
else:
|
||||
bc[dof] = self.f.createIfcLinearStiffnessMeasure(bc[dof])
|
||||
if geometryType == 'point':
|
||||
bc[dof] = self.f.createIfcLinearStiffnessMeasure(bc[dof])
|
||||
if geometryType == 'line':
|
||||
bc[dof] = self.f.createIfcModulusOfLinearSubgradeReactionMeasure(bc[dof])
|
||||
if geometryType == 'surface':
|
||||
bc[dof] = self.f.createIfcModulusOfSubgradeReactionMeasure(bc[dof])
|
||||
|
||||
for dof in ['drx', 'dry', 'drz']:
|
||||
if isinstance(bc[dof], bool):
|
||||
bc[dof] = self.f.createIfcBoolean(bc[dof])
|
||||
else:
|
||||
bc[dof] = self.f.createIfcRotationalStiffnessMeasure(bc[dof])
|
||||
if geometryType == 'point':
|
||||
bc[dof] = self.f.createIfcRotationalStiffnessMeasure(bc[dof])
|
||||
if geometryType == 'line':
|
||||
bc[dof] = self.f.createIfcModulusOfRotationalSubgradeReactionMeasure(bc[dof])
|
||||
|
||||
return bc
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
inputFilename = 'grid_of_beams.json'
|
||||
outputFilename = 'grid_of_beams.ifc'
|
||||
inputFilename = 'structure_01.json'
|
||||
outputFilename = 'structure_01.ifc'
|
||||
|
||||
ca2ifc = CA2IFC(inputFilename, outputFilename)
|
||||
ca2ifc.convert()
|
||||
#
|
||||
# inputFilename = 'portal_01.json'
|
||||
# outputFilename = 'portal_01.ifc'
|
||||
#
|
||||
# ca2ifc = CA2IFC(inputFilename, outputFilename)
|
||||
# ca2ifc.convert()
|
||||
#
|
||||
# inputFilename = 'building_01.json' # json file to read
|
||||
# outputFilename = 'building_01.ifc' # ifc file to write
|
||||
#
|
||||
# ca2ifc = CA2IFC(inputFilename, outputFilename)
|
||||
# ca2ifc.convert()
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
## A change log of the ifc2ca files
|
||||
|
||||
##### 14/10/20
|
||||
- Add curve connections betweeen elements and as supports according to the defined orientation - stiffness is not yet considered
|
||||
|
||||
##### 17/08/20
|
||||
- Add rigid links for point connection to elements
|
||||
|
||||
##### 14/07/20
|
||||
- Add internal springs for point connection to elements and external springs for point connections according to the defined orientation
|
||||
- Add internal springs for point connection to elements according to the defined orientation
|
||||
- Add external springs for point connections according to the defined orientation
|
||||
|
||||
##### 27/05/20
|
||||
- Add orientation for point, curve and surface geometries
|
||||
|
||||
+65
-13
@@ -98,7 +98,7 @@ class IFC2CA:
|
||||
for c in connections:
|
||||
if c['eccentricity']:
|
||||
if np.linalg.norm(np.array(c['eccentricity']['pointOnElement'])) > length + self.tol:
|
||||
print((np.linalg.norm(np.array(c['eccentricity']['pointOnElement'])), '>', length))
|
||||
print(np.linalg.norm(np.array(c['eccentricity']['pointOnElement'])), '>', length)
|
||||
self.warnings.append('Eccentricity in %s corrected' % (item.is_a() + '|' + str(item.id())))
|
||||
c['eccentricity']['pointOnElement'][0] = length
|
||||
# End <--
|
||||
@@ -181,7 +181,32 @@ class IFC2CA:
|
||||
'geometryType': 'point',
|
||||
'geometry': geometry,
|
||||
'orientation': orientation,
|
||||
'appliedCondition': self.get_connection_input(item),
|
||||
'appliedCondition': self.get_connection_input(item, 'point'),
|
||||
'relatedElements': [con.is_a() + '|' + str(con.id()) for con in item.ConnectsStructuralMembers]
|
||||
}
|
||||
|
||||
elif item.is_a('IfcStructuralCurveConnection'):
|
||||
representation = self.get_representation(item, 'Edge')
|
||||
if not representation:
|
||||
self.warnings.append('No representation defined for %s. Connection excluded' % (item.is_a() + '|' + str(item.id())))
|
||||
return
|
||||
|
||||
geometry = self.get_geometry(representation)
|
||||
orientation = self.get_1D_orientation(geometry, item.Axis)
|
||||
if not orientation:
|
||||
orientation = np.eye(3).tolist()
|
||||
if transformation:
|
||||
geometry = self.transform_vectors(geometry, transformation)
|
||||
orientation = self.transform_vectors(orientation, transformation, include_translation=False)
|
||||
|
||||
return {
|
||||
'ifcName': item.is_a() + '|' + str(item.id()),
|
||||
'name': item.Name,
|
||||
'id': item.GlobalId,
|
||||
'geometryType': 'line',
|
||||
'geometry': geometry,
|
||||
'orientation': orientation,
|
||||
'appliedCondition': self.get_connection_input(item, 'line'),
|
||||
'relatedElements': [con.is_a() + '|' + str(con.id()) for con in item.ConnectsStructuralMembers]
|
||||
}
|
||||
|
||||
@@ -412,7 +437,7 @@ class IFC2CA:
|
||||
'relatingElement': rel.RelatingStructuralMember.is_a() + '|' + str(rel.RelatingStructuralMember.id()),
|
||||
'relatedConnection': rel.RelatedStructuralConnection.is_a() + '|' + str(rel.RelatedStructuralConnection.id()),
|
||||
'orientation': self.get_0D_orientation(rel.ConditionCoordinateSystem),
|
||||
'appliedCondition': self.get_connection_input(rel),
|
||||
'appliedCondition': self.get_connection_input(rel, self.get_geometry_type_from_connection(rel.RelatedStructuralConnection)),
|
||||
'eccentricity': None if not rel.is_a('IfcRelConnectsWithEccentricity') else {
|
||||
'vector': [
|
||||
0.0 if not rel.ConnectionConstraint.EccentricityInX else rel.ConnectionConstraint.EccentricityInX,
|
||||
@@ -423,16 +448,43 @@ class IFC2CA:
|
||||
}
|
||||
} for rel in itemList]
|
||||
|
||||
def get_connection_input(self, connection):
|
||||
def get_geometry_type_from_connection(self, connection):
|
||||
if connection.is_a('IfcStructuralPointConnection'):
|
||||
return 'point'
|
||||
if connection.is_a('IfcStructuralCurveConnection'):
|
||||
return 'line'
|
||||
if connection.is_a('IfcStructuralSurfaceConnection'):
|
||||
return 'surface'
|
||||
|
||||
def get_connection_input(self, connection, geometryType):
|
||||
if connection.AppliedCondition:
|
||||
return {
|
||||
'dx': connection.AppliedCondition.TranslationalStiffnessX.wrappedValue,
|
||||
'dy': connection.AppliedCondition.TranslationalStiffnessY.wrappedValue,
|
||||
'dz': connection.AppliedCondition.TranslationalStiffnessZ.wrappedValue,
|
||||
'drx': connection.AppliedCondition.RotationalStiffnessX.wrappedValue,
|
||||
'dry': connection.AppliedCondition.RotationalStiffnessY.wrappedValue,
|
||||
'drz': connection.AppliedCondition.RotationalStiffnessZ.wrappedValue
|
||||
}
|
||||
if geometryType == 'point':
|
||||
return {
|
||||
'dx': connection.AppliedCondition.TranslationalStiffnessX.wrappedValue,
|
||||
'dy': connection.AppliedCondition.TranslationalStiffnessY.wrappedValue,
|
||||
'dz': connection.AppliedCondition.TranslationalStiffnessZ.wrappedValue,
|
||||
'drx': connection.AppliedCondition.RotationalStiffnessX.wrappedValue,
|
||||
'dry': connection.AppliedCondition.RotationalStiffnessY.wrappedValue,
|
||||
'drz': connection.AppliedCondition.RotationalStiffnessZ.wrappedValue
|
||||
}
|
||||
|
||||
if geometryType == 'line':
|
||||
return {
|
||||
'dx': connection.AppliedCondition.TranslationalStiffnessByLengthX.wrappedValue,
|
||||
'dy': connection.AppliedCondition.TranslationalStiffnessByLengthY.wrappedValue,
|
||||
'dz': connection.AppliedCondition.TranslationalStiffnessByLengthZ.wrappedValue,
|
||||
'drx': connection.AppliedCondition.RotationalStiffnessByLengthX.wrappedValue,
|
||||
'dry': connection.AppliedCondition.RotationalStiffnessByLengthY.wrappedValue,
|
||||
'drz': connection.AppliedCondition.RotationalStiffnessByLengthZ.wrappedValue
|
||||
}
|
||||
|
||||
if geometryType == 'surface':
|
||||
return {
|
||||
'dx': connection.AppliedCondition.TranslationalStiffnessByAreaX.wrappedValue,
|
||||
'dy': connection.AppliedCondition.TranslationalStiffnessByAreaY.wrappedValue,
|
||||
'dz': connection.AppliedCondition.TranslationalStiffnessByAreaZ.wrappedValue
|
||||
}
|
||||
|
||||
return connection.AppliedCondition
|
||||
|
||||
def get_i_section_properties(self, profile, profileShape):
|
||||
@@ -455,7 +507,7 @@ class IFC2CA:
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
fileNames = ['cantilever_01', 'portal_01', 'grid_of_beams']
|
||||
fileNames = ['cantilever_01', 'portal_01', 'grid_of_beams', 'slab_01', 'structure_01']
|
||||
files = fileNames
|
||||
|
||||
for fileName in files:
|
||||
|
||||
+130
-14
@@ -33,16 +33,25 @@ class COMMANDFILE:
|
||||
for el in elements:
|
||||
for rel in el['connections']:
|
||||
conn = [c for c in connections if c['ifcName'] == rel['relatedConnection']][0]
|
||||
rel['conn_string'] = None
|
||||
if conn['geometryType'] == 'point':
|
||||
rel['groupName1'] = self.getGroupName(rel['relatingElement']) + '_0DC_' + self.getGroupName(rel['relatedConnection'])
|
||||
if rel['eccentricity']:
|
||||
rel['groupName2'] = self.getGroupName(rel['relatedConnection']) + '_0DC_' + self.getGroupName(rel['relatingElement'])
|
||||
rel['index'] = len(conn['relatedElements']) + 1
|
||||
rel['unifiedGroupName'] = self.getGroupName(rel['relatedConnection']) + '_0DC_%g' % rel['index']
|
||||
else:
|
||||
rel['groupName2'] = self.getGroupName(rel['relatedConnection'])
|
||||
rel['conn_string'] = '_0DC_'
|
||||
rel['springGroupName'] = self.getGroupName(rel['relatingElement']) + '_1DS_' + self.getGroupName(rel['relatedConnection'])
|
||||
self.calculateConstraints(rel)
|
||||
if conn['geometryType'] == 'line':
|
||||
rel['conn_string'] = '_1DC_'
|
||||
rel['springGroupName'] = None
|
||||
if conn['geometryType'] == 'surface':
|
||||
rel['conn_string'] = '_2DC_'
|
||||
rel['springGroupName'] = None
|
||||
|
||||
rel['groupName1'] = self.getGroupName(rel['relatingElement']) + rel['conn_string'] + self.getGroupName(rel['relatedConnection'])
|
||||
if rel['eccentricity']:
|
||||
rel['groupName2'] = self.getGroupName(rel['relatedConnection']) + '_0DC_' + self.getGroupName(rel['relatingElement'])
|
||||
rel['index'] = len(conn['relatedElements']) + 1
|
||||
rel['unifiedGroupName'] = self.getGroupName(rel['relatedConnection']) + '_0DC_%g' % rel['index']
|
||||
else:
|
||||
rel['groupName2'] = self.getGroupName(rel['relatedConnection'])
|
||||
self.calculateConstraints(rel)
|
||||
conn['relatedElements'].append(rel)
|
||||
# End <--
|
||||
|
||||
@@ -51,8 +60,9 @@ class COMMANDFILE:
|
||||
|
||||
edgeGroupNames = tuple([self.getGroupName(el['ifcName']) for el in elements if el['geometryType'] == 'line'])
|
||||
faceGroupNames = tuple([self.getGroupName(el['ifcName']) for el in elements if el['geometryType'] == 'surface'])
|
||||
point0DGroupNames = tuple([self.getGroupName(el['ifcName']) for el in connections if el['geometryType'] == 'point'])
|
||||
spring1DGroupNames = tuple(flatten([[rel['springGroupName'] for rel in el['connections']] for el in elements]))
|
||||
point0DGroupNames = tuple([self.getGroupName(el['ifcName']) + '_0D' for el in connections if el['geometryType'] == 'point'])
|
||||
spring1DGroupNames = tuple(flatten([[rel['springGroupName'] for rel in el['connections'] if rel['springGroupName']] for el in elements]))
|
||||
point1DGroupNames = tuple([self.getGroupName(el['ifcName']) + '_0D' for el in connections if el['geometryType'] == 'line'])
|
||||
|
||||
unifiedConnection = False
|
||||
rigidLinkGroupNames = []
|
||||
@@ -156,6 +166,21 @@ model = AFFE_MODELE(
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if point1DGroupNames:
|
||||
template = \
|
||||
'''
|
||||
_F(
|
||||
GROUP_MA = {groupNames},
|
||||
PHENOMENE = 'MECANIQUE',
|
||||
MODELISATION = 'DIS_TR'
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupNames': point1DGroupNames
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
if rigidLinkGroupNames:
|
||||
template = \
|
||||
'''
|
||||
@@ -365,7 +390,7 @@ element = AFFE_CARA_ELEM(
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupName': self.getGroupName(conn['ifcName']),
|
||||
'groupName': self.getGroupName(conn['ifcName']) + '_0D',
|
||||
'stiffnesses': conn['stiffnesses']
|
||||
}
|
||||
|
||||
@@ -389,6 +414,23 @@ element = AFFE_CARA_ELEM(
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for conn in [conn for conn in connections if conn['geometryType'] == 'line']:
|
||||
|
||||
template = \
|
||||
'''
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'K_TR_D_N',
|
||||
VALE = {stiffnesses},
|
||||
REPERE = 'LOCAL'
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupName': self.getGroupName(conn['ifcName']) + '_0D',
|
||||
'stiffnesses': conn['stiffnesses']
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
'''
|
||||
@@ -428,7 +470,7 @@ element = AFFE_CARA_ELEM(
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupName': self.getGroupName(conn['ifcName']),
|
||||
'groupName': self.getGroupName(conn['ifcName']) + '_0D',
|
||||
'localAxesXY': tuple(conn['orientation'][0] + conn['orientation'][1])
|
||||
}
|
||||
|
||||
@@ -451,6 +493,23 @@ element = AFFE_CARA_ELEM(
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for conn in [conn for conn in connections if conn['geometryType'] == 'line']:
|
||||
|
||||
template = \
|
||||
'''
|
||||
_F(
|
||||
GROUP_MA = '{groupName}',
|
||||
CARA = 'VECT_X_Y',
|
||||
VALE = {localAxesXY}
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupName': self.getGroupName(conn['ifcName']) + '_0D',
|
||||
'localAxesXY': tuple(conn['orientation'][0] + conn['orientation'][1])
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
'''
|
||||
),'''
|
||||
@@ -472,7 +531,7 @@ liaisons = AFFE_CHAR_MECA(
|
||||
LIAISON_DDL = ('''
|
||||
)
|
||||
|
||||
for conn in connections:
|
||||
for conn in [conn for conn in connections if conn['geometryType'] == 'point']:
|
||||
if conn['appliedCondition']:
|
||||
for i in range(len(conn['liaisons']['coeffs'])):
|
||||
template = \
|
||||
@@ -516,6 +575,63 @@ liaisons = AFFE_CHAR_MECA(
|
||||
),'''
|
||||
)
|
||||
|
||||
f.write(
|
||||
'''
|
||||
LIAISON_GROUP = ('''
|
||||
)
|
||||
|
||||
for conn in [conn for conn in connections if conn['geometryType'] == 'line']:
|
||||
if conn['appliedCondition']:
|
||||
for i in range(len(conn['liaisons']['coeffs'])):
|
||||
template = \
|
||||
'''
|
||||
_F(
|
||||
GROUP_NO_1 = {groupName_1},
|
||||
GROUP_NO_2 = {groupName_1},
|
||||
DDL_1 = {dofs},
|
||||
DDL_2 = {dofs},
|
||||
COEF_MULT_1 = {coeffs},
|
||||
COEF_MULT_2 = (0.0, 0.0, 0.0),
|
||||
COEF_IMPO = 0.0
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupName_1': tuple([conn['liaisons']['groupNames'][0]]),
|
||||
'dofs': conn['liaisons']['dofs'][i],
|
||||
'coeffs': conn['liaisons']['coeffs'][i]
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
for rel in conn['relatedElements']:
|
||||
for i in range(len(rel['liaisons']['coeffs'])):
|
||||
template = \
|
||||
'''
|
||||
_F(
|
||||
GROUP_NO_1 = {groupName_1},
|
||||
GROUP_NO_2 = {groupName_2},
|
||||
DDL_1 = {dofs},
|
||||
DDL_2 = {dofs},
|
||||
COEF_MULT_1 = {coeffs_1},
|
||||
COEF_MULT_2 = {coeffs_2},
|
||||
COEF_IMPO = 0.0
|
||||
),'''
|
||||
|
||||
context = {
|
||||
'groupName_1': tuple([rel['liaisons']['groupNames'][0]]),
|
||||
'groupName_2': tuple([rel['liaisons']['groupNames'][3]]),
|
||||
'dofs': tuple(list(rel['liaisons']['dofs'][i])[:3]),
|
||||
'coeffs_1': tuple(list(rel['liaisons']['coeffs'][i])[:3]),
|
||||
'coeffs_2': tuple(list(rel['liaisons']['coeffs'][i])[3:]),
|
||||
}
|
||||
|
||||
f.write(template.format(**context))
|
||||
|
||||
f.write(
|
||||
'''
|
||||
),'''
|
||||
)
|
||||
|
||||
if unifiedConnection:
|
||||
f.write(
|
||||
'''
|
||||
@@ -825,7 +941,7 @@ FIN()
|
||||
conn['stiffnesses'] = tuple(stiffnesses)
|
||||
|
||||
if __name__ == '__main__':
|
||||
fileNames = ['cantilever_01', 'portal_01', 'grid_of_beams']
|
||||
fileNames = ['cantilever_01', 'portal_01', 'grid_of_beams', 'slab_01', 'structure_01']
|
||||
files = fileNames
|
||||
|
||||
for fileName in files:
|
||||
|
||||
+39
-24
@@ -160,20 +160,19 @@ class MODEL:
|
||||
if rel['eccentricity']:
|
||||
rel['index'] = len(conn['relatedElements']) + 1
|
||||
conn['relatedElements'].append(rel)
|
||||
if conn['geometryType'] == 'point':
|
||||
if not rel['eccentricity']:
|
||||
el['connObjs'][j] = self.makeObject(conn['geometry'], conn['geometryType'])
|
||||
else:
|
||||
|
||||
if not rel['eccentricity']:
|
||||
el['connObjs'][j] = self.makeObject(conn['geometry'], conn['geometryType'])
|
||||
else:
|
||||
if conn['geometryType'] == 'point':
|
||||
geometry = self.getLinkGeometry(rel['eccentricity'], el['orientation'], conn['geometry'])
|
||||
el['connObjs'][j] = self.makeObject(geometry[0], conn['geometryType'])
|
||||
|
||||
el['linkPointObjs'][j][0] = self.geompy.MakeVertex(geometry[0][0], geometry[0][1], geometry[0][2])
|
||||
el['linkPointObjs'][j][1] = self.geompy.MakeVertex(geometry[1][0], geometry[1][1], geometry[1][2])
|
||||
el['linkObjs'][j] = self.geompy.MakeLineTwoPnt(el['linkPointObjs'][j][0], el['linkPointObjs'][j][1])
|
||||
elif conn['geometryType'] == 'line':
|
||||
pass
|
||||
elif conn['geometryType'] == 'surface':
|
||||
pass
|
||||
else:
|
||||
print('Eccentricity defined for a %s geometryType' %conn['geometryType'])
|
||||
|
||||
el['partObj'] = self.makePartition([el['elemObj']] + el['connObjs'], el['geometryType'])
|
||||
|
||||
@@ -199,7 +198,15 @@ class MODEL:
|
||||
# geompy.addToStudy(el['partObj'], self.getGroupName(el['ifcName']))
|
||||
geompy.addToStudyInFather(el['partObj'], el['elemObj'], self.getGroupName(el['ifcName']))
|
||||
for j,rel in enumerate(el['connections']):
|
||||
geompy.addToStudyInFather(el['partObj'], el['connObjs'][j], self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection']))
|
||||
conn = [c for c in connections if c['ifcName'] == rel['relatedConnection']][0]
|
||||
rel['conn_string'] = None
|
||||
if conn['geometryType'] == 'point':
|
||||
rel['conn_string'] = '_0DC_'
|
||||
if conn['geometryType'] == 'line':
|
||||
rel['conn_string'] = '_1DC_'
|
||||
if conn['geometryType'] == 'surface':
|
||||
rel['conn_string'] = '_2DC_'
|
||||
geompy.addToStudyInFather(el['partObj'], el['connObjs'][j], self.getGroupName(el['ifcName']) + rel['conn_string'] + self.getGroupName(rel['relatedConnection']))
|
||||
if rel['eccentricity']:
|
||||
pass
|
||||
# geompy.addToStudy(el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DR_' + self.getGroupName(rel['relatedConnection']))
|
||||
@@ -240,8 +247,8 @@ class MODEL:
|
||||
geompy.addToStudyInFather(bldComp, el['elemObj'], self.getGroupName(el['ifcName']))
|
||||
|
||||
for j,rel in enumerate(el['connections']):
|
||||
geompy.addToStudyInFather(bldComp, el['connObjs'][j], self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection']))
|
||||
if rel['eccentricity']:
|
||||
geompy.addToStudyInFather(bldComp, el['connObjs'][j], self.getGroupName(el['ifcName']) + rel['conn_string'] + self.getGroupName(rel['relatedConnection']))
|
||||
if rel['eccentricity']: # point geometry
|
||||
geompy.addToStudyInFather(bldComp, el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DR_' + self.getGroupName(rel['relatedConnection']))
|
||||
geompy.addToStudyInFather(bldComp, el['linkPointObjs'][j][0], self.getGroupName(rel['relatedConnection']) + '_0DC_' + self.getGroupName(el['ifcName']))
|
||||
geompy.addToStudyInFather(bldComp, el['linkPointObjs'][j][1], self.getGroupName(rel['relatedConnection']) + '_0DC_%g' % rel['index'])
|
||||
@@ -318,8 +325,8 @@ class MODEL:
|
||||
smesh.SetName(tempgroup, self.getGroupName(el['ifcName']))
|
||||
|
||||
for j,rel in enumerate(el['connections']):
|
||||
tempgroup = bldMesh.GroupOnGeom(el['connObjs'][j], self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection']), SMESH.NODE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(el['ifcName']) + '_0DC_' + self.getGroupName(rel['relatedConnection']))
|
||||
tempgroup = bldMesh.GroupOnGeom(el['connObjs'][j], self.getGroupName(el['ifcName']) + rel['conn_string'] + self.getGroupName(rel['relatedConnection']), SMESH.NODE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(el['ifcName']) + rel['conn_string'] + self.getGroupName(rel['relatedConnection']))
|
||||
rel['node'] = (bldMesh.GetIDSource(tempgroup.GetNodeIDs(), SMESH.NODE)).GetIDs()[0]
|
||||
if rel['eccentricity']:
|
||||
tempgroup = bldMesh.GroupOnGeom(el['linkObjs'][j], self.getGroupName(el['ifcName']) + '_1DR_' + self.getGroupName(rel['relatedConnection']), SMESH.EDGE)
|
||||
@@ -334,24 +341,32 @@ class MODEL:
|
||||
|
||||
|
||||
for conn in connections:
|
||||
# if conn['appliedCondition']:
|
||||
tempgroup = bldMesh.GroupOnGeom(conn['connObj'], self.getGroupName(conn['ifcName']), SMESH.NODE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn['ifcName']))
|
||||
nodesId = bldMesh.GetIDSource(tempgroup.GetNodeIDs(), SMESH.NODE)
|
||||
tempgroup = bldMesh.Add0DElementsToAllNodes(nodesId, self.getGroupName(conn['ifcName']))
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn['ifcName']))
|
||||
conn['node'] = nodesId.GetIDs()[0]
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn['ifcName'] + '_0D'))
|
||||
if conn['geometryType'] == 'point':
|
||||
conn['node'] = nodesId.GetIDs()[0]
|
||||
if conn['geometryType'] == 'line':
|
||||
tempgroup = bldMesh.GroupOnGeom(conn['connObj'], self.getGroupName(conn['ifcName']), SMESH.EDGE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn['ifcName']))
|
||||
if conn['geometryType'] == 'surface':
|
||||
tempgroup = bldMesh.GroupOnGeom(conn['connObj'], self.getGroupName(conn['ifcName']), SMESH.FACE)
|
||||
smesh.SetName(tempgroup, self.getGroupName(conn['ifcName']))
|
||||
|
||||
# create 1D SEG2 spring elements
|
||||
for el in elements:
|
||||
for j,rel in enumerate(el['connections']):
|
||||
grpName = bldMesh.CreateEmptyGroup(SMESH.EDGE, self.getGroupName(el['ifcName']) + '_1DS_' + self.getGroupName(rel['relatedConnection']))
|
||||
smesh.SetName(grpName, self.getGroupName(el['ifcName']) + '_1DS_' + self.getGroupName(rel['relatedConnection']))
|
||||
if not rel['eccentricity']:
|
||||
conn = [conn for conn in connections if conn['ifcName'] == rel['relatedConnection']][0]
|
||||
grpName.Add([bldMesh.AddEdge([conn['node'], rel['node']])])
|
||||
else:
|
||||
grpName.Add([bldMesh.AddEdge([rel['eccNode'], rel['node']])])
|
||||
conn = [c for c in connections if c['ifcName'] == rel['relatedConnection']][0]
|
||||
if conn['geometryType'] == 'point':
|
||||
grpName = bldMesh.CreateEmptyGroup(SMESH.EDGE, self.getGroupName(el['ifcName']) + '_1DS_' + self.getGroupName(rel['relatedConnection']))
|
||||
smesh.SetName(grpName, self.getGroupName(el['ifcName']) + '_1DS_' + self.getGroupName(rel['relatedConnection']))
|
||||
if not rel['eccentricity']:
|
||||
conn = [conn for conn in connections if conn['ifcName'] == rel['relatedConnection']][0]
|
||||
grpName.Add([bldMesh.AddEdge([conn['node'], rel['node']])])
|
||||
else:
|
||||
grpName.Add([bldMesh.AddEdge([rel['eccNode'], rel['node']])])
|
||||
|
||||
self.mesh = bldMesh
|
||||
self.meshNodes = bldMesh.GetNodesId()
|
||||
@@ -385,7 +400,7 @@ class MODEL:
|
||||
print('ALL Operations Completed in %g sec' % (elapsed_time))
|
||||
|
||||
if __name__ == '__main__':
|
||||
fileNames = ['cantilever_01']
|
||||
fileNames = ['structure_01']
|
||||
files = fileNames
|
||||
|
||||
meshSize = 0.1
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
from behave import step
|
||||
from utils import IfcFile, assert_attribute, assert_type
|
||||
|
||||
def check_geocode_attribute(guid, ifc_class, name, value):
|
||||
|
||||
def get_ifc_class_from_spatial_type(spatial_type):
|
||||
if spatial_type == 'site':
|
||||
return 'IfcSite'
|
||||
elif spatial_type == 'building':
|
||||
return 'IfcBuilding'
|
||||
return 'IfcFacility'
|
||||
|
||||
|
||||
def check_geocode_attribute(guid, spatial_type, name, value):
|
||||
element = IfcFile.by_guid(guid)
|
||||
assert_type(element, ifc_class)
|
||||
assert_type(element, get_ifc_class_from_spatial_type(spatial_type))
|
||||
assert_attribute(element, name, value)
|
||||
|
||||
|
||||
def check_geocode_address(guid, ifc_class, name, value):
|
||||
def check_geocode_address(guid, spatial_type, name, value):
|
||||
element = IfcFile.by_guid(guid)
|
||||
ifc_class = get_ifc_class_from_spatial_type(spatial_type)
|
||||
assert_type(element, ifc_class)
|
||||
if ifc_class == 'IfcSite':
|
||||
address_name = 'SiteAddress'
|
||||
@@ -20,50 +30,48 @@ def check_geocode_address(guid, ifc_class, name, value):
|
||||
|
||||
use_step_matcher('re')
|
||||
@step('The (site|building|facility) (?P<guid>.*) has a name of (?P<name>.*)')
|
||||
def step_impl(context, _unused, guid, name):
|
||||
check_geocode_attribute(guid, 'IfcSite', 'Name', name)
|
||||
def step_impl(context, spatial_type, guid, name):
|
||||
check_geocode_attribute(guid, spatial_type, 'Name', name)
|
||||
|
||||
|
||||
@step('The (site|building|facility) (?P<guid>.*) has a description of (?P<description>.*)')
|
||||
def step_impl(context, _unused, guid, description):
|
||||
check_geocode_attribute(guid, 'IfcSite', 'Description', description)
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) has a land title number of (?P<land_title_number>.*)')
|
||||
def step_impl(context, _unused, guid, land_title_number):
|
||||
check_geocode_attribute(guid, 'IfcSite', 'LandTitleNumber', land_title_number)
|
||||
@step('The (site|building|facility) (?P<guid>.*) has a description of "(?P<description>.*)"')
|
||||
def step_impl(context, spatial_type, guid, description):
|
||||
check_geocode_attribute(guid, spatial_type, 'Description', description)
|
||||
|
||||
@step('The site (?P<guid>.*) has a land title number of (?P<land_title_number>.*)')
|
||||
def step_impl(context, guid, land_title_number):
|
||||
check_geocode_attribute(guid, 'site', 'LandTitleNumber', land_title_number)
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) has the address "(?P<address_lines>.*)"')
|
||||
def step_impl(context, _unused, guid, address_lines):
|
||||
check_geocode_address(guid, 'IfcSite', 'AddressLines', address_lines.split('\\n'))
|
||||
def step_impl(context, spatial_type, guid, address_lines):
|
||||
check_geocode_address(guid, spatial_type, 'AddressLines', address_lines.split('\\n'))
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) has a postal box of (?P<postal_box>.*)')
|
||||
def step_impl(context, _unused, guid, postal_box):
|
||||
check_geocode_address(guid, 'IfcSite', 'PostalBox', postal_box)
|
||||
def step_impl(context, spatial_type, guid, postal_box):
|
||||
check_geocode_address(guid, spatial_type, 'PostalBox', postal_box)
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) is in the town (?P<town>.*)')
|
||||
def step_impl(context, _unused, guid, town):
|
||||
check_geocode_address(guid, 'IfcSite', 'Town', town)
|
||||
def step_impl(context, spatial_type, guid, town):
|
||||
check_geocode_address(guid, spatial_type, 'Town', town)
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) is in the region (?P<region>.*)')
|
||||
def step_impl(context, _unused, guid, region):
|
||||
check_geocode_address(guid, 'IfcSite', 'Region', region)
|
||||
def step_impl(context, spatial_type, guid, region):
|
||||
check_geocode_address(guid, spatial_type, 'Region', region)
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) has a post code of (?P<post_code>.*)')
|
||||
def step_impl(context, _unused, guid, post_code):
|
||||
check_geocode_address(guid, 'IfcSite', 'PostalCode', post_code)
|
||||
def step_impl(context, spatial_type, guid, post_code):
|
||||
check_geocode_address(guid, spatial_type, 'PostalCode', post_code)
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) is in the country (?P<country>.*)')
|
||||
def step_impl(context, _unused, guid, country):
|
||||
check_geocode_address(guid, 'IfcSite', 'Country', country)
|
||||
def step_impl(context, spatial_type, guid, country):
|
||||
check_geocode_address(guid, spatial_type, 'Country', country)
|
||||
|
||||
|
||||
@step('The (site|building) (?P<guid>.*) has an address description of (?P<description>.*)')
|
||||
def step_impl(context, _unused, guid, description):
|
||||
check_geocode_address(guid, 'IfcSite', 'Description', description)
|
||||
@step('The (site|building) (?P<guid>.*) has an address description of "(?P<description>.*)"')
|
||||
def step_impl(context, spatial_type, guid, description):
|
||||
check_geocode_address(guid, spatial_type, 'Description', description)
|
||||
|
||||
@@ -3,6 +3,7 @@ from utils import IfcFile, assert_number, assert_pset, assert_attribute
|
||||
import math
|
||||
import ifcopenshell.util
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.geolocation
|
||||
|
||||
@step(u'There must be at least one {ifc_class} element')
|
||||
def step_impl(context, ifc_class):
|
||||
@@ -108,6 +109,8 @@ def step_impl(context, unit):
|
||||
assert_pset(site, 'EPset_ProjectedCRS', 'MapUnit', unit)
|
||||
return
|
||||
actual_value = check_ifc4_geolocation('IfcProjectedCRS', 'MapUnit', should_assert=False)
|
||||
if not actual_value:
|
||||
assert False, 'A unit was not provided in the projected CRS'
|
||||
if actual_value.is_a('IfcSIUnit'):
|
||||
prefix = actual_value.Prefix if actual_value.Prefix else ''
|
||||
actual_value = prefix + actual_value.Name
|
||||
@@ -161,8 +164,7 @@ def step_impl(context, number):
|
||||
return check_ifc2x3_geolocation('EPset_MapConversion', 'Height', number)
|
||||
abscissa = check_ifc4_geolocation('IfcMapConversion', 'XAxisAbscissa', should_assert=False)
|
||||
ordinate = check_ifc4_geolocation('IfcMapConversion', 'XAxisOrdinate', should_assert=False)
|
||||
# TODO: migrate to geolocation util
|
||||
actual_value = round(math.degrees(math.atan2(ordinate, abscissa)) - 90, 3)
|
||||
actual_value = round(ifcopenshell.util.geolocation.xy2angle(abscissa, ordinate), 3)
|
||||
value = round(number, 3)
|
||||
assert actual_value == value, 'We expected a value of "{}" but instead got "{}"'.format(value, actual_value)
|
||||
|
||||
@@ -183,6 +185,8 @@ def step_impl(context, guid, number):
|
||||
site = IfcFile.by_guid(guid)
|
||||
if not site.is_a('IfcSite'):
|
||||
assert False, 'The element {} is not an IfcSite'.format(site)
|
||||
ref = assert_attribute(site, 'RefLongitude')
|
||||
number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4))
|
||||
assert_attribute(site, 'RefLongitude', number)
|
||||
|
||||
|
||||
@@ -192,6 +196,8 @@ def step_impl(context, guid, number):
|
||||
site = IfcFile.by_guid(guid)
|
||||
if not site.is_a('IfcSite'):
|
||||
assert False, 'The element {} is not an IfcSite'.format(site)
|
||||
ref = assert_attribute(site, 'RefLatitude')
|
||||
number = ifcopenshell.util.geolocation.dd2dms(number, use_ms=(len(ref) == 4))
|
||||
assert_attribute(site, 'RefLatitude', number)
|
||||
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ def assert_attribute(element, name, value=None):
|
||||
if not value:
|
||||
if getattr(element, name) is None:
|
||||
assert False, 'The element {} does not have a value for the attribute {}'.format(element, name)
|
||||
return
|
||||
return getattr(element, name)
|
||||
if value == 'NULL':
|
||||
value = None
|
||||
actual_value = getattr(element, name)
|
||||
|
||||
@@ -57,7 +57,9 @@
|
||||
<span class="step-time">{{time}}s</span>
|
||||
{{^is_success}}
|
||||
<p class="failure{{#is_unspecified}} unspecified{{/is_unspecified}}">
|
||||
{{error_message}}
|
||||
{{#error_message}}
|
||||
{{.}}<br />
|
||||
{{/error_message}}
|
||||
</p>
|
||||
{{/is_success}}
|
||||
</li>
|
||||
|
||||
@@ -26,7 +26,7 @@ endif
|
||||
cp -r blenderbim/* dist/blenderbim/
|
||||
|
||||
# Provides IfcOpenShell Python functionality
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-c15fdc7-$(PLATFORM)64.zip
|
||||
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-ca6adff-$(PLATFORM)64.zip
|
||||
cd dist/working && unzip ifcblender*
|
||||
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
|
||||
# See bug #812
|
||||
@@ -243,6 +243,8 @@ endif
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/element_classes.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/geocoding.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/geolocation.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/geometric_detail.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/model_federation.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/project_setup.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/steps.py
|
||||
cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/utils.py
|
||||
|
||||
@@ -133,7 +133,6 @@ if bpy is not None:
|
||||
operator.SelectClashSource,
|
||||
operator.ExecuteIfcClash,
|
||||
operator.SelectIfcClashResults,
|
||||
operator.AssignContext,
|
||||
operator.SwitchContext,
|
||||
operator.RemoveContext,
|
||||
operator.OpenUpstream,
|
||||
@@ -186,7 +185,6 @@ if bpy is not None:
|
||||
operator.SaveDrawingStyle,
|
||||
operator.ActivateDrawingStyle,
|
||||
operator.EditVectorStyle,
|
||||
operator.PurgeProjectClassifications,
|
||||
operator.RemoveSheet,
|
||||
operator.AddSchedule,
|
||||
operator.RemoveSchedule,
|
||||
@@ -196,6 +194,14 @@ if bpy is not None:
|
||||
operator.SetViewportShadowFromSun,
|
||||
operator.SetNorthOffset,
|
||||
operator.GetNorthOffset,
|
||||
operator.AddDrawingStyleAttribute,
|
||||
operator.RemoveDrawingStyleAttribute,
|
||||
operator.CopyPropertyToSelection,
|
||||
operator.CreateShapeFromStepId,
|
||||
operator.SelectHighPolygonMeshes,
|
||||
operator.RefreshDrawingList,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.UpdateIfcRepresentation,
|
||||
prop.StrProperty,
|
||||
prop.Variable,
|
||||
prop.Role,
|
||||
@@ -224,12 +230,14 @@ if bpy is not None:
|
||||
prop.BcfTopicRelatedTopic,
|
||||
prop.Subcontext,
|
||||
prop.BIMProperties,
|
||||
prop.BIMDebugProperties,
|
||||
prop.BCFProperties,
|
||||
prop.DocProperties,
|
||||
prop.BIMLibrary,
|
||||
prop.MapConversion,
|
||||
prop.TargetCRS,
|
||||
prop.Attribute,
|
||||
prop.IfcParameter,
|
||||
prop.BoundaryCondition,
|
||||
prop.PsetQto,
|
||||
prop.GlobalId,
|
||||
@@ -264,6 +272,7 @@ if bpy is not None:
|
||||
ui.BIM_PT_cobie,
|
||||
ui.BIM_PT_patch,
|
||||
ui.BIM_PT_mvd,
|
||||
ui.BIM_PT_debug,
|
||||
ui.BIM_PT_material,
|
||||
ui.BIM_PT_mesh,
|
||||
ui.BIM_PT_object,
|
||||
@@ -287,7 +296,6 @@ if bpy is not None:
|
||||
ui.BIM_UL_document_references,
|
||||
ui.BIM_UL_topics,
|
||||
ui.BIM_UL_classifications,
|
||||
ui.BIM_UL_representation_items,
|
||||
ui.BIM_ADDON_preferences,
|
||||
covetool_prop.CoveToolProject,
|
||||
covetool_prop.CoveToolSimpleAnalysis,
|
||||
@@ -316,7 +324,7 @@ if bpy is not None:
|
||||
|
||||
def on_register(scene):
|
||||
prop.setDefaultProperties(scene)
|
||||
bpy.app.handlers.scene_update_post.remove(on_register)
|
||||
bpy.app.handlers.depsgraph_update_post.remove(on_register)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
@@ -326,6 +334,7 @@ if bpy is not None:
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||
bpy.types.Scene.BIMProperties = bpy.props.PointerProperty(type=prop.BIMProperties)
|
||||
bpy.types.Scene.BIMDebugProperties = bpy.props.PointerProperty(type=prop.BIMDebugProperties)
|
||||
bpy.types.Scene.BCFProperties = bpy.props.PointerProperty(type=prop.BCFProperties)
|
||||
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
|
||||
bpy.types.Scene.BIMLibrary = bpy.props.PointerProperty(type=prop.BIMLibrary)
|
||||
@@ -355,6 +364,7 @@ if bpy is not None:
|
||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
del(bpy.types.Scene.BIMProperties)
|
||||
del(bpy.types.Scene.BIMDebugProperties)
|
||||
del(bpy.types.Scene.BCFProperties)
|
||||
del(bpy.types.Scene.DocProperties)
|
||||
del(bpy.types.Scene.MapConversion)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import re
|
||||
import math
|
||||
import time
|
||||
import numpy
|
||||
@@ -150,6 +151,7 @@ def do_cut(process_data):
|
||||
|
||||
class IfcCutter:
|
||||
def __init__(self):
|
||||
self.time = None
|
||||
self.selector = ifcopenshell.util.selector.Selector()
|
||||
self.product_shapes = []
|
||||
self.background_elements = []
|
||||
@@ -185,52 +187,36 @@ class IfcCutter:
|
||||
}
|
||||
|
||||
def cut(self):
|
||||
start_time = time.time()
|
||||
print('# Load files')
|
||||
self.profile_code('Starting cut process')
|
||||
self.load_ifc_files()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Extract template variables')
|
||||
self.profile_code('Load IFC files')
|
||||
self.get_template_variables()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Get product shapes')
|
||||
self.profile_code('Get template variables')
|
||||
self.get_product_shapes()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Create section box')
|
||||
self.profile_code('Get product shapes')
|
||||
self.create_section_box()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Get cut polygons')
|
||||
self.profile_code('Create section box')
|
||||
self.get_cut_polygons()
|
||||
print('# Get annotation')
|
||||
self.profile_code('Get cut polygons')
|
||||
self.get_annotation()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Get cut polygon metadata')
|
||||
self.profile_code('Get annotation')
|
||||
self.get_cut_polygon_metadata()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
self.profile_code('Get cut polygon metadata')
|
||||
|
||||
# should_get_background is False in production as this is experimental
|
||||
if not self.should_get_background:
|
||||
return
|
||||
|
||||
start_time = time.time()
|
||||
print('# Get background elements')
|
||||
self.get_background_elements()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Sort background elements')
|
||||
self.sort_background_elements(reverse=True)
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Merge background_elements')
|
||||
self.merge_background_elements()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
start_time = time.time()
|
||||
print('# Sort background elements')
|
||||
self.sort_background_elements()
|
||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||
|
||||
def profile_code(self, message):
|
||||
if not self.time:
|
||||
self.time = time.time()
|
||||
print('{} :: {:.2f}'.format(message, time.time() - self.time))
|
||||
self.time = time.time()
|
||||
|
||||
def load_ifc_files(self):
|
||||
if not self.should_recut and not self.should_extract:
|
||||
@@ -299,7 +285,7 @@ class IfcCutter:
|
||||
|
||||
products.extend(self.selector.parse(ifc_file, self.cut_objects))
|
||||
|
||||
include_elements = []
|
||||
selected_elements = []
|
||||
for i, product in enumerate(products):
|
||||
if product.is_a('IfcOpeningElement') \
|
||||
or product.is_a('IfcSite') \
|
||||
@@ -309,20 +295,20 @@ class IfcCutter:
|
||||
try:
|
||||
if self.should_recut_selected \
|
||||
and product.GlobalId in self.selected_global_ids:
|
||||
include_elements.append(product)
|
||||
selected_elements.append(product)
|
||||
elif product.GlobalId in shape_map:
|
||||
shape = shape_map[product.GlobalId]
|
||||
self.product_shapes.append((product, shape))
|
||||
self.add_product_shape(product, shape)
|
||||
else:
|
||||
include_elements.append(product)
|
||||
selected_elements.append(product)
|
||||
except:
|
||||
print('Failed to create shape for {}'.format(product))
|
||||
|
||||
if include_elements:
|
||||
if selected_elements:
|
||||
total = 0
|
||||
checkpoint = time.time()
|
||||
iterator = ifcopenshell.geom.iterator(
|
||||
settings, ifc_file, multiprocessing.cpu_count(), include=include_elements)
|
||||
settings, ifc_file, multiprocessing.cpu_count(), include=selected_elements)
|
||||
valid_file = iterator.initialize()
|
||||
if valid_file:
|
||||
while True:
|
||||
@@ -332,13 +318,16 @@ class IfcCutter:
|
||||
checkpoint = time.time()
|
||||
shape = iterator.get()
|
||||
shape_map[shape.data.guid] = shape.geometry
|
||||
self.product_shapes.append((ifc_file.by_guid(shape.data.guid), shape.geometry))
|
||||
self.add_product_shape(ifc_file.by_guid(shape.data.guid), shape.geometry)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
with open(shape_pickle, 'wb') as shape_file:
|
||||
pickle.dump(shape_map, shape_file, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
def add_product_shape(self, product, shape):
|
||||
self.product_shapes.append((product, shape))
|
||||
|
||||
def has_annotation(self, element):
|
||||
for representation in element.Representation.Representations:
|
||||
if representation.ContextOfItems.ContextType == 'Plan' \
|
||||
@@ -807,8 +796,17 @@ class IfcCutter:
|
||||
classes = [position, element.is_a()]
|
||||
for association in element.HasAssociations:
|
||||
if association.is_a('IfcRelAssociatesMaterial'):
|
||||
classes.append('material-{}'.format(self.get_material_name(association.RelatingMaterial)))
|
||||
classes.append('material-{}'.format(
|
||||
re.sub('[^0-9a-zA-Z]+', '', self.get_material_name(association.RelatingMaterial))
|
||||
))
|
||||
classes.append('globalid-{}'.format(element.GlobalId))
|
||||
for attribute in self.attributes:
|
||||
result = self.selector.get_element_value(element, attribute)
|
||||
if result:
|
||||
classes.append('{}-{}'.format(
|
||||
re.sub('[^0-9a-zA-Z]+', '', attribute),
|
||||
re.sub('[^0-9a-zA-Z]+', '', result)
|
||||
))
|
||||
return classes
|
||||
|
||||
def get_material_name(self, element):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* { stroke-linecap: round; }
|
||||
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; }
|
||||
.background { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||
@@ -23,3 +24,4 @@
|
||||
.material-sand { fill: url(#sand); }
|
||||
.material-concrete { fill: url(#concrete); stroke-width: 0.5; }
|
||||
.material-boundary { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12,4,3,4,3,4; }
|
||||
.IfcSpace { fill: none; stroke: none; }
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
.stair { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
|
||||
.break { fill: white; }
|
||||
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
|
||||
.IfcSpace { fill: none; stroke: none; }
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<pattern id="demolish" width="1" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="1" style="stroke:red; stroke-width:0.25" />
|
||||
</pattern>
|
||||
<pattern id="diagonal1" width="1" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />
|
||||
</pattern>
|
||||
@@ -9,6 +12,18 @@
|
||||
<pattern id="diagonal3" width="3" height="3" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:0.25" />
|
||||
</pattern>
|
||||
<pattern id="square1" width="1" height="1" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />
|
||||
<line x1="0" y1="0" x2="1" y2="0" style="stroke:black; stroke-width:0.25" />
|
||||
</pattern>
|
||||
<pattern id="square2" width="2" height="2" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="2" style="stroke:black; stroke-width:0.25" />
|
||||
<line x1="0" y1="0" x2="2" y2="0" style="stroke:black; stroke-width:0.25" />
|
||||
</pattern>
|
||||
<pattern id="square3" width="3" height="3" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:0.25" />
|
||||
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:0.25" />
|
||||
</pattern>
|
||||
<pattern id="crosshatch1" width="1" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="1" style="stroke:black; stroke-width:0.25" />
|
||||
<line x1="0" y1="0" x2="1" y2="0" style="stroke:black; stroke-width:0.25" />
|
||||
@@ -27,6 +42,7 @@
|
||||
<line x1="2" y1="0" x2="2" y2="3" style="stroke:black; stroke-width:0.25" />
|
||||
</pattern>
|
||||
<pattern id="earth" width="6" height="6" patternUnits="userSpaceOnUse">
|
||||
<path style="fill: white;" d="M 0 0 6 0 6 6 0 6" />
|
||||
<line x1="0" y1="0" x2="0" y2="2" style="stroke:black; stroke-width:0.25" />
|
||||
<line x1="1" y1="0" x2="1" y2="2" style="stroke:black; stroke-width:0.25" />
|
||||
<line x1="2" y1="0" x2="2" y2="2" style="stroke:black; stroke-width:0.25" />
|
||||
|
||||
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
@@ -8,8 +8,9 @@ import zipfile
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from mathutils import Vector, Matrix
|
||||
from .helper import SIUnitHelper
|
||||
from .helper import SIUnitHelper, get_representation_elements
|
||||
from . import schema
|
||||
from . import ifc
|
||||
import ifcopenshell
|
||||
import addon_utils
|
||||
|
||||
@@ -86,8 +87,9 @@ class IfcParser():
|
||||
if not self.projects:
|
||||
self.setup_project()
|
||||
self.projects = self.get_projects()
|
||||
|
||||
self.project = self.projects[0]
|
||||
if not selected_objects:
|
||||
selected_objects = self.get_all_objects_in_project(self.project['raw'])
|
||||
self.units = self.get_units()
|
||||
self.unit_scale = self.get_unit_scale()
|
||||
self.people = self.get_people()
|
||||
@@ -124,25 +126,31 @@ class IfcParser():
|
||||
self.spatial_structure_elements_tree.extend(self.get_spatial_structure_elements_tree(project))
|
||||
|
||||
def get_units(self):
|
||||
return {
|
||||
units = {
|
||||
'length': {
|
||||
'ifc': None,
|
||||
'is_metric': bpy.context.scene.unit_settings.system == 'METRIC',
|
||||
'is_metric': bpy.context.scene.unit_settings.system != 'IMPERIAL',
|
||||
'raw': bpy.context.scene.unit_settings.length_unit
|
||||
},
|
||||
'area': {
|
||||
'ifc': None,
|
||||
'is_metric': bpy.context.scene.unit_settings.system == 'METRIC',
|
||||
'is_metric': bpy.context.scene.unit_settings.system != 'IMPERIAL',
|
||||
'raw': bpy.context.scene.unit_settings.length_unit
|
||||
},
|
||||
'volume': {
|
||||
'ifc': None,
|
||||
'is_metric': bpy.context.scene.unit_settings.system == 'METRIC',
|
||||
'is_metric': bpy.context.scene.unit_settings.system != 'IMPERIAL',
|
||||
'raw': bpy.context.scene.unit_settings.length_unit
|
||||
}}
|
||||
for data in units.values():
|
||||
if data['raw'] == 'ADAPTIVE':
|
||||
if data['is_metric']:
|
||||
data['raw'] = 'METERS'
|
||||
else:
|
||||
data['raw'] = 'FEET'
|
||||
return units
|
||||
|
||||
def get_unit_scale(self):
|
||||
unit_settings = bpy.context.scene.unit_settings
|
||||
conversions = {
|
||||
'KILOMETERS': 1e3,
|
||||
'CENTIMETERS': 1e-2,
|
||||
@@ -150,12 +158,13 @@ class IfcParser():
|
||||
'MICROMETERS': 1e-6,
|
||||
'FEET': 0.3048,
|
||||
'INCHES': 0.0254}
|
||||
if unit_settings.system in {'METRIC', 'IMPERIAL'}:
|
||||
scale = unit_settings.scale_length
|
||||
if unit_settings.length_unit in conversions.keys():
|
||||
scale *= conversions[unit_settings.length_unit]
|
||||
return scale
|
||||
return 1
|
||||
if bpy.context.scene.unit_settings.system in {'METRIC', 'IMPERIAL'}:
|
||||
scale = bpy.context.scene.unit_settings.scale_length
|
||||
else:
|
||||
scale = 1
|
||||
if self.units['length']['raw'] in conversions.keys():
|
||||
scale *= conversions[self.units['length']['raw']]
|
||||
return scale
|
||||
|
||||
def get_object_attributes(self, obj):
|
||||
attributes = {'Name': self.get_ifc_name(obj.name)}
|
||||
@@ -348,10 +357,10 @@ class IfcParser():
|
||||
'ifc': None,
|
||||
'raw': obj,
|
||||
'class': self.get_ifc_class(obj.name),
|
||||
'attributes': self.get_object_attributes(obj),
|
||||
'relating_structure': None,
|
||||
'relating_host': None,
|
||||
'relating_qtos_key': None,
|
||||
'attributes': self.get_object_attributes(obj),
|
||||
'has_boundary_condition': obj.BIMObjectProperties.has_boundary_condition,
|
||||
'boundary_condition_class': None,
|
||||
'boundary_condition_attributes': {},
|
||||
@@ -463,6 +472,8 @@ class IfcParser():
|
||||
relationships.setdefault(item_key, []).append(product)
|
||||
|
||||
def add_automatic_qtos(self, ifc_class, obj):
|
||||
if not obj.data:
|
||||
return
|
||||
qto_names = self.get_applicable_qtos(ifc_class)
|
||||
for name in qto_names:
|
||||
if name not in schema.ifc.qtos:
|
||||
@@ -486,7 +497,7 @@ class IfcParser():
|
||||
|
||||
def get_applicable_qtos(self, ifc_class):
|
||||
results = []
|
||||
empty = ifcopenshell.file()
|
||||
empty = ifcopenshell.file(schema=self.ifc_export_settings.schema)
|
||||
element = empty.create_entity(ifc_class)
|
||||
for ifc_class, qto_names in schema.ifc.applicable_qtos.items():
|
||||
if element.is_a(ifc_class):
|
||||
@@ -626,27 +637,30 @@ class IfcParser():
|
||||
def get_classifications(self):
|
||||
results = {}
|
||||
for classification in bpy.context.scene.BIMProperties.classifications:
|
||||
schema.ifc.load_classification(classification.filename)
|
||||
results[classification.filename] = {
|
||||
if classification.name not in schema.ifc.classification_files:
|
||||
schema.ifc.classification_files[classification.name] = ifcopenshell.file.from_string(classification.data)
|
||||
results[classification.name] = {
|
||||
'ifc': None,
|
||||
'raw': classification,
|
||||
'raw_element': schema.ifc.classification_files[classification.filename].by_type('IfcClassification')[0]
|
||||
'raw_element': schema.ifc.classification_files[classification.name].by_type('IfcClassification')[0]
|
||||
}
|
||||
return results
|
||||
|
||||
def get_classification_reference_maps(self):
|
||||
results = {}
|
||||
for filename, classification in self.classifications.items():
|
||||
ifc_file = schema.ifc.classification_files[filename]
|
||||
for name, classification in self.classifications.items():
|
||||
ifc_file = schema.ifc.classification_files[name]
|
||||
if ifc_file.schema == 'IFC2X3':
|
||||
results[filename] = { e.ItemReference: e for e in ifc_file.by_type('IfcClassificationReference')}
|
||||
results[name] = { e.ItemReference: e for e in ifc_file.by_type('IfcClassificationReference')}
|
||||
else:
|
||||
results[filename] = { e.Identification: e for e in ifc_file.by_type('IfcClassificationReference')}
|
||||
results[name] = { e.Identification: e for e in ifc_file.by_type('IfcClassificationReference')}
|
||||
return results
|
||||
|
||||
def get_classification_references(self):
|
||||
results = {}
|
||||
for product in self.selected_products:
|
||||
for product in self.selected_products \
|
||||
+ self.selected_types \
|
||||
+ self.selected_spatial_structure_elements:
|
||||
for reference in product['raw'].BIMObjectProperties.classifications:
|
||||
results[reference.name] = {
|
||||
'ifc': None,
|
||||
@@ -691,6 +705,11 @@ class IfcParser():
|
||||
'suffix_titles': 'SuffixTitles',
|
||||
}
|
||||
results = []
|
||||
|
||||
if self.ifc_export_settings.schema == 'IFC2X3' \
|
||||
and not bpy.context.scene.BIMProperties.people:
|
||||
bpy.ops.bim.add_person()
|
||||
|
||||
for person in bpy.context.scene.BIMProperties.people:
|
||||
attributes = {}
|
||||
for key, value in data_map.items():
|
||||
@@ -714,6 +733,11 @@ class IfcParser():
|
||||
'description': 'Description',
|
||||
}
|
||||
results = []
|
||||
|
||||
if self.ifc_export_settings.schema == 'IFC2X3' \
|
||||
and not bpy.context.scene.BIMProperties.organisations:
|
||||
bpy.ops.bim.add_organisation()
|
||||
|
||||
for organisation in bpy.context.scene.BIMProperties.organisations:
|
||||
attributes = {}
|
||||
for key, value in data_map.items():
|
||||
@@ -749,6 +773,11 @@ class IfcParser():
|
||||
|
||||
def get_addresses(self, addresses):
|
||||
results = []
|
||||
for address in addresses:
|
||||
results.append(self.get_address(address))
|
||||
return results
|
||||
|
||||
def get_address(self, address):
|
||||
address_data_map = {
|
||||
'purpose': 'Purpose',
|
||||
'description': 'Description',
|
||||
@@ -772,28 +801,26 @@ class IfcParser():
|
||||
'electronic_mail_addresses': 'ElectronicMailAddresses',
|
||||
'messaging_ids': 'MessagingIDs',
|
||||
}
|
||||
for address in addresses:
|
||||
attributes = {}
|
||||
if 'IfcPostalAddress' in address.name:
|
||||
merged_data_map = {**address_data_map, **postal_data_map}
|
||||
if address.address_lines:
|
||||
attributes['AddressLines'] = address.address_lines.split('/')
|
||||
elif 'IfcTelecomAddress' in address.name:
|
||||
merged_data_map = {**address_data_map, **telecom_data_map}
|
||||
for key, value in telecom_list_data_map.items():
|
||||
if getattr(address, key):
|
||||
attributes[value] = getattr(address, key).split(',')
|
||||
for key, value in merged_data_map.items():
|
||||
attributes = {}
|
||||
if 'IfcPostalAddress' in address.name:
|
||||
merged_data_map = {**address_data_map, **postal_data_map}
|
||||
if address.address_lines:
|
||||
attributes['AddressLines'] = address.address_lines.split('/')
|
||||
elif 'IfcTelecomAddress' in address.name:
|
||||
merged_data_map = {**address_data_map, **telecom_data_map}
|
||||
for key, value in telecom_list_data_map.items():
|
||||
if getattr(address, key):
|
||||
attributes[value] = getattr(address, key)
|
||||
results.append({
|
||||
'ifc': None,
|
||||
'raw': address,
|
||||
'is_postal': 'IfcPostalAddress' in address.name,
|
||||
'is_telecom': 'IfcTelecomAddress' in address.name,
|
||||
'attributes': attributes
|
||||
})
|
||||
return results
|
||||
attributes[value] = getattr(address, key).split(',')
|
||||
for key, value in merged_data_map.items():
|
||||
if getattr(address, key):
|
||||
attributes[value] = getattr(address, key)
|
||||
return {
|
||||
'ifc': None,
|
||||
'raw': address,
|
||||
'is_postal': 'IfcPostalAddress' in address.name,
|
||||
'is_telecom': 'IfcTelecomAddress' in address.name,
|
||||
'attributes': attributes
|
||||
}
|
||||
|
||||
def get_document_references(self):
|
||||
results = {}
|
||||
@@ -860,6 +887,13 @@ class IfcParser():
|
||||
})
|
||||
return results
|
||||
|
||||
def get_all_objects_in_project(self, collection):
|
||||
results = []
|
||||
results.extend(list(collection.objects))
|
||||
for child in collection.children:
|
||||
results.extend(self.get_all_objects_in_project(child))
|
||||
return results
|
||||
|
||||
def setup_project(self):
|
||||
bpy.ops.bim.quick_project_setup()
|
||||
for collection in bpy.data.collections:
|
||||
@@ -944,10 +978,12 @@ class IfcParser():
|
||||
'ifc': None,
|
||||
'raw': obj,
|
||||
'class': self.get_ifc_class(obj.name),
|
||||
'attributes': self.get_object_attributes(obj)
|
||||
'attributes': self.get_object_attributes(obj),
|
||||
'address': self.get_address(obj.BIMObjectProperties.address)
|
||||
}
|
||||
self.append_product_attributes(element, obj)
|
||||
self.get_product_psets_qtos(element, obj, is_pset=True)
|
||||
self.get_product_psets_qtos(element, obj, is_qto=True)
|
||||
elements.append(element)
|
||||
return elements
|
||||
|
||||
@@ -1011,6 +1047,9 @@ class IfcParser():
|
||||
self.representations['Model/Body/MODEL_VIEW/{}'.format(obj.data.name)] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Body', 'MODEL_VIEW')
|
||||
if 'Model/Box/MODEL_VIEW' in self.generated_subcontexts:
|
||||
if self.ifc_export_settings.should_roundtrip_native \
|
||||
and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return
|
||||
self.representations['Model/Box/MODEL_VIEW/{}'.format(obj.data.name)] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Box', 'MODEL_VIEW')
|
||||
|
||||
@@ -1048,11 +1087,13 @@ class IfcParser():
|
||||
self.representations[mesh_name] = self.get_representation(
|
||||
mesh, obj, context, subcontext, target_view)
|
||||
if 'Model/Box/MODEL_VIEW' in self.generated_subcontexts \
|
||||
and context == 'Model' \
|
||||
and subcontext == 'Body' \
|
||||
and target_view == 'MODEL_VIEW':
|
||||
self.representations['Model/Box/MODEL_VIEW/{}'.format(mesh_name.split('/')[3])] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Box', 'MODEL_VIEW')
|
||||
and context_prefix == 'Model/Body/MODEL_VIEW':
|
||||
if self.ifc_export_settings.should_roundtrip_native \
|
||||
and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
pass
|
||||
else:
|
||||
self.representations['Model/Box/MODEL_VIEW/{}'.format(mesh_name.split('/')[3])] = self.get_representation(
|
||||
obj.data, obj, 'Model', 'Box', 'MODEL_VIEW')
|
||||
elif context_prefix == 'Model/Body/MODEL_VIEW' \
|
||||
and obj.data \
|
||||
and not self.is_mesh_context_sensitive(obj.data.name):
|
||||
@@ -1081,12 +1122,15 @@ class IfcParser():
|
||||
'context': context,
|
||||
'subcontext': subcontext,
|
||||
'target_view': target_view,
|
||||
'has_ifc_definition': False if not hasattr(mesh, 'BIMMeshProperties') else (mesh.BIMMeshProperties.ifc_definition or mesh.BIMMeshProperties.ifc_definition_id),
|
||||
'ifc_definition': mesh.BIMMeshProperties.ifc_definition if hasattr(mesh, 'BIMMeshProperties') else None,
|
||||
'ifc_definition_id': mesh.BIMMeshProperties.ifc_definition_id if hasattr(mesh, 'BIMMeshProperties') else None,
|
||||
'is_parametric': mesh.BIMMeshProperties.is_parametric if hasattr(mesh, 'BIMMeshProperties') else False,
|
||||
'is_curve': isinstance(mesh, bpy.types.Curve),
|
||||
'is_point_cloud': self.is_point_cloud(obj),
|
||||
'is_structural': self.is_structural(obj),
|
||||
'is_text': isinstance(mesh, bpy.types.TextCurve),
|
||||
'is_wireframe': self.is_wireframe_mesh(mesh),
|
||||
'is_wireframe': self.is_wireframe_mesh(mesh, obj),
|
||||
'is_native': mesh.BIMMeshProperties.is_native if hasattr(mesh, 'BIMMeshProperties') else False,
|
||||
'is_swept_solid': mesh.BIMMeshProperties.is_swept_solid if hasattr(mesh, 'BIMMeshProperties') else False,
|
||||
'is_generated': False,
|
||||
@@ -1094,9 +1138,12 @@ class IfcParser():
|
||||
'attributes': {'Name': mesh.name}
|
||||
}
|
||||
|
||||
def is_wireframe_mesh(self, mesh):
|
||||
def is_wireframe_mesh(self, mesh, obj):
|
||||
if isinstance(mesh, bpy.types.Mesh) and not mesh.polygons:
|
||||
return True
|
||||
modifiers = [m.type for m in obj.modifiers]
|
||||
# SCREW and SKIN can create faces, so it is not a wireframe mesh
|
||||
if 'SCREW' not in modifiers and 'SKIN' not in modifiers:
|
||||
return True
|
||||
if isinstance(mesh, bpy.types.Curve) and not mesh.bevel_object and not mesh.bevel_depth:
|
||||
return True
|
||||
return False
|
||||
@@ -1147,7 +1194,9 @@ class IfcParser():
|
||||
parsed_data_names = []
|
||||
for product in self.selected_products + self.type_products:
|
||||
obj = product['raw']
|
||||
if obj.data is None or obj.data.name in parsed_data_names:
|
||||
if obj.data is None \
|
||||
or obj.data.name in parsed_data_names \
|
||||
or obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
continue
|
||||
parsed_data_names.append(obj.data.name)
|
||||
for slot in obj.material_slots:
|
||||
@@ -1525,12 +1574,15 @@ class IfcExporter():
|
||||
def create_addresses(self, addresses):
|
||||
results = []
|
||||
for address in addresses:
|
||||
if self.schema == 'IFC2X3' and 'MessagingIDs' in address['attributes']:
|
||||
del address['attributes']['MessagingIDs']
|
||||
results.append(self.file.create_entity('IfcPostalAddress' if
|
||||
address['is_postal'] else 'IfcTelecomAddress', **address['attributes']))
|
||||
results.append(self.create_address(address))
|
||||
return results
|
||||
|
||||
def create_address(self, address):
|
||||
if self.schema == 'IFC2X3' and 'MessagingIDs' in address['attributes']:
|
||||
del address['attributes']['MessagingIDs']
|
||||
return self.file.create_entity('IfcPostalAddress' if
|
||||
address['is_postal'] else 'IfcTelecomAddress', **address['attributes'])
|
||||
|
||||
def create_library_information(self):
|
||||
information = self.ifc_parser.library_information
|
||||
if not information:
|
||||
@@ -1642,7 +1694,8 @@ class IfcExporter():
|
||||
for name, data in templates.items():
|
||||
if name not in pset['raw']:
|
||||
continue
|
||||
if data.TemplateType == 'P_SINGLEVALUE':
|
||||
if data.TemplateType == 'P_SINGLEVALUE' \
|
||||
or data.TemplateType == 'P_ENUMERATEDVALUE':
|
||||
if data.PrimaryMeasureType:
|
||||
value_type = data.PrimaryMeasureType
|
||||
else:
|
||||
@@ -1839,6 +1892,12 @@ class IfcExporter():
|
||||
'ObjectPlacement': placement,
|
||||
'Representation': self.get_product_shape(element)
|
||||
})
|
||||
|
||||
if element['class'] == 'IfcSite':
|
||||
element['attributes'].update({'SiteAddress': self.create_address(element['address'])})
|
||||
elif element['class'] == 'IfcBuilding':
|
||||
element['attributes'].update({'BuildingAddress': self.create_address(element['address'])})
|
||||
|
||||
element['ifc'] = self.file.create_entity(element['class'], **element['attributes'])
|
||||
related_objects.append(element['ifc'])
|
||||
self.create_spatial_structure_elements(node['children'], element['ifc'])
|
||||
@@ -2144,11 +2203,15 @@ class IfcExporter():
|
||||
def get_product_shape_representations(self, product):
|
||||
results = []
|
||||
for representation_name in product['representations']:
|
||||
results.append(self.get_product_mapped_geometry(product, representation_name))
|
||||
representation = self.ifc_parser.representations[representation_name]
|
||||
if self.ifc_export_settings.should_roundtrip_native and representation['has_ifc_definition']:
|
||||
results.append(representation['ifc'])
|
||||
else:
|
||||
results.append(self.get_product_mapped_geometry(product, representation))
|
||||
return results
|
||||
|
||||
def get_product_mapped_geometry(self, product, representation_name):
|
||||
mapping_source = self.ifc_parser.representations[representation_name]['ifc']
|
||||
def get_product_mapped_geometry(self, product, representation):
|
||||
mapping_source = representation['ifc']
|
||||
shape_representation = mapping_source.MappedRepresentation
|
||||
if product['has_scale']:
|
||||
if not product['has_mirror']:
|
||||
@@ -2194,6 +2257,8 @@ class IfcExporter():
|
||||
self.file.createIfcDirection((forward.x, forward.y, forward.z)))
|
||||
|
||||
def create_representation(self, representation):
|
||||
if self.ifc_export_settings.should_roundtrip_native and representation['has_ifc_definition']:
|
||||
return self.create_representation_from_definition(representation)
|
||||
self.ifc_vertices = []
|
||||
self.ifc_edges = []
|
||||
if representation['context'] == 'Model':
|
||||
@@ -2203,6 +2268,34 @@ class IfcExporter():
|
||||
elif representation['context'] == 'NotDefined':
|
||||
return self.create_variable_representation(representation)
|
||||
|
||||
def create_representation_from_definition(self, representation):
|
||||
if representation['ifc_definition']:
|
||||
print('Authoring an IFC definition directly is not yet implemented')
|
||||
return
|
||||
elif representation['ifc_definition_id']:
|
||||
entry = self.file.add(ifc.IfcStore.get_file().by_id(representation['ifc_definition_id']))
|
||||
substitutions = []
|
||||
for element in get_representation_elements(
|
||||
ifc.IfcStore.get_file(), representation['ifc_definition_id']):
|
||||
added_element = self.file.add(element)
|
||||
if added_element.is_a('IfcGeometricRepresentationContext'):
|
||||
substitutions.append(added_element)
|
||||
for element in substitutions:
|
||||
if element.is_a() == 'IfcGeometricRepresentationContext':
|
||||
new_element = [e for e in
|
||||
self.file.by_type('IfcGeometricRepresentationContext')
|
||||
if e.ContextType == element.ContextType][0]
|
||||
elif element.is_a() == 'IfcGeometricRepresentationSubContext':
|
||||
new_element = [e for e in
|
||||
self.file.by_type('IfcGeometricRepresentationContext')
|
||||
if e.ContextType == element.ContextType and
|
||||
e.ContextIdentifier == element.ContextIdentifier][0]
|
||||
for inverse in self.file.get_inverse(element):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, element, new_element)
|
||||
# TODO: Work out how and when to purge this
|
||||
#self.file.remove(element)
|
||||
return entry
|
||||
|
||||
def create_model_representation(self, representation):
|
||||
if representation['subcontext'] == 'Annotation':
|
||||
return self.file.createIfcRepresentationMap(self.origin,
|
||||
@@ -2290,9 +2383,9 @@ class IfcExporter():
|
||||
obj.bound_box[0][1],
|
||||
obj.bound_box[0][2]
|
||||
),
|
||||
obj.dimensions[0],
|
||||
obj.dimensions[1],
|
||||
obj.dimensions[2]
|
||||
self.convert_si_to_unit(obj.dimensions[0]),
|
||||
self.convert_si_to_unit(obj.dimensions[1]),
|
||||
self.convert_si_to_unit(obj.dimensions[2])
|
||||
)
|
||||
return self.file.createIfcShapeRepresentation(
|
||||
self.ifc_rep_context[representation['context']][representation['subcontext']][
|
||||
@@ -3035,6 +3128,7 @@ class IfcExportSettings:
|
||||
settings.should_use_presentation_style_assignment = scene_bim.export_should_use_presentation_style_assignment
|
||||
settings.should_guess_quantities = scene_bim.export_should_guess_quantities
|
||||
settings.should_force_faceted_brep = scene_bim.export_should_force_faceted_brep
|
||||
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||
settings.context_tree = []
|
||||
for ifc_context in ['model', 'plan']:
|
||||
if getattr(scene_bim, 'has_{}_context'.format(ifc_context)):
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
import math
|
||||
import bpy
|
||||
|
||||
# TODO: figure out where this should go
|
||||
def get_representation_elements(ifc_file, step_id):
|
||||
results = []
|
||||
for child in ifc_file.traverse(ifc_file.by_id(step_id)):
|
||||
if hasattr(child, 'StyledByItem') and child.StyledByItem:
|
||||
for styled_by_item in child.StyledByItem:
|
||||
for style in styled_by_item.Styles:
|
||||
for style_child in ifc_file.traverse(style):
|
||||
results.append(style_child)
|
||||
results.append(child)
|
||||
return results
|
||||
|
||||
|
||||
# TODO: Deprecate this in favour of ifcopenshell.util.unit
|
||||
|
||||
@@ -18,12 +32,12 @@ class SIUnitHelper:
|
||||
'yard': 0.914,
|
||||
'mile': 1609,
|
||||
'square inch': 0.0006452,
|
||||
'square foot': 0.09290,
|
||||
'square foot': 0.09290304,
|
||||
'square yard': 0.83612736,
|
||||
'acre': 4046.86,
|
||||
'square mile': 2588881,
|
||||
'cubic inch': 0.00001639,
|
||||
'cubic foot': 0.02832,
|
||||
'cubic foot': 0.02831684671168849,
|
||||
'cubic yard': 0.7636,
|
||||
'litre': 0.001,
|
||||
'fluid ounce UK': 0.0000284130625,
|
||||
@@ -101,3 +115,144 @@ class SIUnitHelper:
|
||||
value *= (1 / SIUnitHelper.get_prefix_multiplier(to_prefix))
|
||||
value *= (1 / SIUnitHelper.get_prefix_multiplier(to_prefix))
|
||||
return value
|
||||
|
||||
|
||||
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
|
||||
# MeasureIt-ARCH is GPL-v3
|
||||
# In the future I will need to rewrite this to allow the user to have custom
|
||||
# settings for each annotation object, not read from Blender.
|
||||
def format_distance(value, isArea=False, hide_units=True):
|
||||
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
|
||||
|
||||
# Get Scene Unit Settings
|
||||
scaleFactor = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
imperial_precision = 32
|
||||
# (('1', "1\"", "1 Inch"),
|
||||
# ('2', "1/2\"", "1/2 Inch"),
|
||||
# ('4', "1/4\"", "1/4 Inch"),
|
||||
# ('8', "1/8\"", "1/8th Inch"),
|
||||
# ('16', "1/16\"", "1/16th Inch"),
|
||||
# ('32', "1/32\"", "1/32th Inch"),
|
||||
# ('64', "1/64\"", "1/64th Inch")),
|
||||
|
||||
toInches = 39.3700787401574887
|
||||
inPerFoot = 11.999
|
||||
|
||||
if isArea:
|
||||
toInches = 1550
|
||||
inPerFoot = 143.999
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
# Imperial Formating
|
||||
if unit_system == "IMPERIAL":
|
||||
base = int(imperial_precision)
|
||||
decInches = value * toInches
|
||||
|
||||
# Seperate ft and inches
|
||||
# Unless Inches are the specified Length Unit
|
||||
if unit_length != 'INCHES':
|
||||
feet = math.floor(decInches/inPerFoot)
|
||||
decInches -= feet*inPerFoot
|
||||
else:
|
||||
feet = 0
|
||||
|
||||
|
||||
#Seperate Fractional Inches
|
||||
inches = math.floor(decInches)
|
||||
if inches != 0:
|
||||
frac = round(base*(decInches-inches))
|
||||
else:
|
||||
frac = round(base*(decInches))
|
||||
|
||||
#Set proper numerator and denominator
|
||||
if frac != base:
|
||||
numcycles = int(math.log2(base))
|
||||
for i in range(numcycles):
|
||||
if frac%2 == 0:
|
||||
frac = int(frac/2)
|
||||
base = int(base/2)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
frac = 0
|
||||
inches += 1
|
||||
|
||||
# Check values and compose string
|
||||
if inches == 12:
|
||||
feet += 1
|
||||
inches = 0
|
||||
|
||||
if inches !=0:
|
||||
inchesString = str(inches)
|
||||
if frac != 0: inchesString += "-"
|
||||
else: inchesString += "\""
|
||||
else: inchesString = ""
|
||||
|
||||
if feet != 0:
|
||||
feetString = str(feet) + "' "
|
||||
else: feetString = ""
|
||||
|
||||
if frac != 0:
|
||||
fracString = str(frac) + "/" + str(base) +"\""
|
||||
else: fracString = ""
|
||||
|
||||
if not isArea:
|
||||
tx_dist = feetString + inchesString + fracString
|
||||
else:
|
||||
tx_dist = str('%1.3f' % (value*toInches/inPerFoot)) + " sq. ft."
|
||||
|
||||
|
||||
# METRIC FORMATING
|
||||
elif unit_system == "METRIC":
|
||||
|
||||
# Meters
|
||||
if unit_length == 'METERS':
|
||||
fmt = '%1.3f'
|
||||
if hide_units is False:
|
||||
fmt += " m"
|
||||
tx_dist = fmt % value
|
||||
# Centimeters
|
||||
elif unit_length == 'CENTIMETERS':
|
||||
fmt = '%1.1f'
|
||||
if hide_units is False:
|
||||
fmt += " cm"
|
||||
d_cm = value * (100)
|
||||
tx_dist = fmt % d_cm
|
||||
#Millimeters
|
||||
elif unit_length == 'MILLIMETERS':
|
||||
fmt = '%1.0f'
|
||||
if hide_units is False:
|
||||
fmt += " mm"
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
|
||||
# Otherwise Use Adaptive Units
|
||||
else:
|
||||
if round(value, 2) >= 1.0:
|
||||
fmt = '%1.3f'
|
||||
if hide_units is False:
|
||||
fmt += " m"
|
||||
tx_dist = fmt % value
|
||||
else:
|
||||
if round(value, 2) >= 0.01:
|
||||
fmt = '%1.1f'
|
||||
if hide_units is False:
|
||||
fmt += " cm"
|
||||
d_cm = value * (100)
|
||||
tx_dist = fmt % d_cm
|
||||
else:
|
||||
fmt = '%1.0f'
|
||||
if hide_units is False:
|
||||
fmt += " mm"
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
if isArea:
|
||||
tx_dist += s_code
|
||||
else:
|
||||
tx_dist = fmt % value
|
||||
|
||||
|
||||
return tx_dist
|
||||
|
||||
@@ -107,7 +107,6 @@ class MaterialCreator():
|
||||
return
|
||||
if len(obj.material_slots) == 1:
|
||||
return
|
||||
slots = [self.canonicalise_material_name(s.name) for s in obj.material_slots]
|
||||
material_to_slot = {}
|
||||
for i, material in enumerate(mesh['ios_materials']):
|
||||
if material == 'NULLMAT':
|
||||
@@ -116,13 +115,16 @@ class MaterialCreator():
|
||||
material = material.split('-')[2]
|
||||
if len(bytes(material, 'utf-8')) > 63: # Blender material names are up to 63 UTF-8 bytes
|
||||
material = bytes(material, 'utf-8')[0:63].decode('utf-8')
|
||||
try:
|
||||
material_to_slot[i] = slots.index(material)
|
||||
except:
|
||||
# If the material name duplicates, a `.001` is added, this
|
||||
# reduces the maxmium characters for the material name to 59.
|
||||
|
||||
slot_index = obj.material_slots.find(material)
|
||||
if slot_index == -1:
|
||||
# If we can't find the material, it is possible that the
|
||||
# material name is duplicated, and so a '.001' is added.
|
||||
# The maximum characters for the material name is 59 in this
|
||||
# scenario.
|
||||
material = material[0:59]
|
||||
material_to_slot[i] = slots.index(material)
|
||||
slot_index = [self.canonicalise_material_name(s.name) for s in obj.material_slots].index(material)
|
||||
material_to_slot[i] = slot_index
|
||||
|
||||
if len(mesh.polygons) == len(mesh['ios_material_ids']):
|
||||
material_index = [(material_to_slot[mat_id] if mat_id != -1
|
||||
@@ -150,6 +152,10 @@ class MaterialCreator():
|
||||
self.create_single(material)
|
||||
elif material.is_a('IfcMaterialLayerSet'):
|
||||
self.create_layer_set(material)
|
||||
elif material.is_a('IfcMaterialConstituentSet'):
|
||||
self.create_constituent_set(material)
|
||||
elif material.is_a('IfcMaterialList'):
|
||||
self.create_material_list(material)
|
||||
|
||||
def create_single(self, material):
|
||||
if material.Name not in self.materials:
|
||||
@@ -164,8 +170,26 @@ class MaterialCreator():
|
||||
self.create_new_single(layer.Material)
|
||||
self.assign_material_to_mesh(self.materials[layer.Material.Name])
|
||||
|
||||
def create_constituent_set(self, constituent_set):
|
||||
for constituent in constituent_set.MaterialConstituents:
|
||||
if constituent.Material:
|
||||
if constituent.Material.Name not in self.materials:
|
||||
# TODO import rest of the layer set data
|
||||
self.create_new_single(constituent.Material)
|
||||
self.assign_material_to_mesh(self.materials[constituent.Material.Name])
|
||||
|
||||
def create_material_list(self, material_list):
|
||||
for material in material_list.Materials:
|
||||
if material.Material:
|
||||
if material.Material.Name not in self.materials:
|
||||
# TODO import rest of the layer set data
|
||||
self.create_new_single(material.Material)
|
||||
self.assign_material_to_mesh(self.materials[material.Material.Name])
|
||||
|
||||
def create_new_single(self, material):
|
||||
self.materials[material.Name] = bpy.data.materials.new(material.Name)
|
||||
self.materials[material.Name] = obj = bpy.data.materials.new(material.Name)
|
||||
for pset in getattr(material, "HasProperties", ()):
|
||||
self.add_pset(pset, obj)
|
||||
if not material.HasRepresentation \
|
||||
or not material.HasRepresentation[0].Representations:
|
||||
return
|
||||
@@ -175,13 +199,30 @@ class MaterialCreator():
|
||||
for item in representation.Items:
|
||||
if not item.is_a('IfcStyledItem'):
|
||||
continue
|
||||
self.parse_styled_item(item, self.materials[material.Name])
|
||||
self.parse_styled_item(item, obj)
|
||||
|
||||
def add_pset(self, pset, obj):
|
||||
new_pset = obj.BIMMaterialProperties.psets.add()
|
||||
new_pset.name = pset.Name
|
||||
if new_pset.name in schema.ifc.psets:
|
||||
for prop_name in schema.ifc.psets[new_pset.name]['HasPropertyTemplates'].keys():
|
||||
prop = new_pset.properties.add()
|
||||
prop.name = prop_name
|
||||
for prop in pset.Properties:
|
||||
if prop.is_a('IfcPropertySingleValue') and prop.NominalValue:
|
||||
index = new_pset.properties.find(prop.Name)
|
||||
if index >= 0:
|
||||
new_pset.properties[index].string_value = str(prop.NominalValue.wrappedValue)
|
||||
else:
|
||||
new_prop = new_pset.properties.add()
|
||||
new_prop.name = prop.Name
|
||||
new_prop.string_value = str(prop.NominalValue.wrappedValue)
|
||||
|
||||
def get_material_name(self, styled_item):
|
||||
if styled_item.Name:
|
||||
return styled_item.Name
|
||||
styled_item = self.resolve_presentation_style_assignment(styled_item)
|
||||
for style in styled_item.Styles:
|
||||
styles = self.get_styled_item_styles(styled_item)
|
||||
for style in styles:
|
||||
if not style.is_a('IfcSurfaceStyle'):
|
||||
continue
|
||||
if style.Name:
|
||||
@@ -190,8 +231,8 @@ class MaterialCreator():
|
||||
return str(styled_item.id())
|
||||
|
||||
def parse_styled_item(self, styled_item, material):
|
||||
styled_item = self.resolve_presentation_style_assignment(styled_item)
|
||||
for style in styled_item.Styles:
|
||||
styles = self.get_styled_item_styles(styled_item)
|
||||
for style in styles:
|
||||
if not style.is_a('IfcSurfaceStyle'):
|
||||
continue
|
||||
external_style = None
|
||||
@@ -217,11 +258,14 @@ class MaterialCreator():
|
||||
|
||||
# IfcPresentationStyleAssignment is deprecated as of IFC4
|
||||
# However it is still widely used thanks to Revit :(
|
||||
def resolve_presentation_style_assignment(self, styled_item):
|
||||
def get_styled_item_styles(self, styled_item):
|
||||
styles = []
|
||||
for style in styled_item.Styles:
|
||||
if style.is_a('IfcPresentationStyleAssignment'):
|
||||
return style
|
||||
return styled_item
|
||||
styles.extend(self.get_styled_item_styles(style))
|
||||
else:
|
||||
styles.append(style)
|
||||
return styles
|
||||
|
||||
def resolve_mapped_representation_items(self, representation):
|
||||
items = []
|
||||
@@ -246,6 +290,9 @@ class IfcImporter():
|
||||
self.diff = None
|
||||
self.file = None
|
||||
self.settings = ifcopenshell.geom.settings()
|
||||
self.settings.set_deflection_tolerance(self.ifc_import_settings.deflection_tolerance)
|
||||
# Uncomment this when the latest IfcOpenBot build is ready
|
||||
# self.settings.set_angular_tolerance(self.ifc_import_settings.angular_tolerance)
|
||||
if self.ifc_import_settings.should_import_curves:
|
||||
self.settings.set(self.settings.INCLUDE_CURVES, True)
|
||||
self.settings_native = ifcopenshell.geom.settings()
|
||||
@@ -868,6 +915,7 @@ class IfcImporter():
|
||||
cumulative_vertex_index += item['total_vertices']
|
||||
|
||||
def create_native_mesh(self, element, shape):
|
||||
# TODO This should be split off into its own module for run-time native mesh conversion
|
||||
data = self.native_elements[element.GlobalId]
|
||||
materials = []
|
||||
items = []
|
||||
@@ -953,9 +1001,6 @@ class IfcImporter():
|
||||
mesh['ios_material_ids'] = material_ids
|
||||
mesh['ios_items'] = representation_items
|
||||
mesh.BIMMeshProperties.is_native = True
|
||||
for representation_item in representation_items:
|
||||
new = mesh.BIMMeshProperties.representation_items.add()
|
||||
new.name = representation_item['name']
|
||||
return mesh
|
||||
|
||||
def get_representation_item_material_name(self, item):
|
||||
@@ -1456,13 +1501,8 @@ class IfcImporter():
|
||||
for entity in entities_to_add:
|
||||
classification_file.add(entity)
|
||||
|
||||
classification_filename = '{}-{}'.format(
|
||||
Path(os.path.basename(self.ifc_import_settings.input_file)).stem, element.Name)
|
||||
classification_file.write(os.path.join(
|
||||
bpy.context.scene.BIMProperties.schema_dir, 'project_classifications',
|
||||
'{}.ifc'.format(classification_filename)))
|
||||
classification.filename = classification_filename
|
||||
self.classifications[classification.filename] = classification
|
||||
classification.data = classification_file.to_string()
|
||||
self.classifications[classification.name] = classification
|
||||
|
||||
self.schema_dir = bpy.context.scene.BIMProperties.schema_dir
|
||||
from . import prop
|
||||
@@ -1550,6 +1590,7 @@ class IfcImporter():
|
||||
parent.children.link(collection)
|
||||
obj = self.create_product(element)
|
||||
if obj:
|
||||
self.spatial_structure_elements[global_id]['blender_obj'] = obj
|
||||
collection.objects.link(obj)
|
||||
del self.added_data[element.GlobalId]
|
||||
if element.IsDecomposedBy:
|
||||
@@ -1699,8 +1740,15 @@ class IfcImporter():
|
||||
if element.Decomposes[0].RelatingObject.is_a('IfcProject'):
|
||||
collection = self.project['blender']
|
||||
elif element.Decomposes[0].RelatingObject.is_a('IfcSpatialStructureElement'):
|
||||
global_id = element.Decomposes[0].RelatingObject.GlobalId
|
||||
if element.is_a('IfcSpatialStructureElement') and not element.is_a('IfcSpace'):
|
||||
global_id = element.GlobalId
|
||||
else:
|
||||
global_id = element.Decomposes[0].RelatingObject.GlobalId
|
||||
if global_id in self.spatial_structure_elements:
|
||||
if element.is_a('IfcSpatialStructureElement') \
|
||||
and not element.is_a('IfcSpace') \
|
||||
and 'blender_obj' in self.spatial_structure_elements[global_id]:
|
||||
bpy.data.objects.remove(self.spatial_structure_elements[global_id]['blender_obj'])
|
||||
collection = self.spatial_structure_elements[global_id]['blender']
|
||||
# This may occur if we are nesting an IfcSpace (which is special
|
||||
# since it does not have a collection within an IfcSpace
|
||||
@@ -1762,9 +1810,6 @@ class IfcImporter():
|
||||
def get_referenced_source_name(self, element):
|
||||
if not hasattr(element, 'ReferencedSource') or not element.ReferencedSource:
|
||||
if element.is_a('IfcClassification'):
|
||||
for filename, classification in self.classifications.items():
|
||||
if classification.name == element.Name:
|
||||
return filename
|
||||
return element.Name
|
||||
else:
|
||||
return element.Identification
|
||||
@@ -1821,6 +1866,11 @@ class IfcImporter():
|
||||
results.append({ 'raw': representation, 'matrix': self.scale_matrix(matrix) })
|
||||
return results
|
||||
|
||||
def get_representation_of_context(self, representations, context):
|
||||
for representation in representations:
|
||||
if representation.RepresentationIdentifier == context:
|
||||
return representation
|
||||
|
||||
def scale_matrix(self, matrix):
|
||||
matrix[0][3] *= self.unit_scale
|
||||
matrix[1][3] *= self.unit_scale
|
||||
@@ -1907,10 +1957,23 @@ class IfcImporter():
|
||||
ios_materials.append(mat.name)
|
||||
mesh['ios_materials'] = ios_materials
|
||||
mesh['ios_material_ids'] = geometry.material_ids
|
||||
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
|
||||
self.store_representation_source(mesh, element, shape)
|
||||
return mesh
|
||||
except:
|
||||
self.ifc_import_settings.logger.error('Could not create mesh for {}'.format(element))
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
def store_representation_source(self, mesh, element, shape):
|
||||
# TODO Refactor to specialist class
|
||||
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
|
||||
if not self.ifc_import_settings.should_roundtrip_native:
|
||||
return
|
||||
if element.is_a('IfcRepresentation'):
|
||||
representation = element
|
||||
else:
|
||||
representation = self.get_representation_of_context(element.Representation.Representations, shape.context)
|
||||
mesh.BIMMeshProperties.ifc_definition_id = int(representation.id())
|
||||
|
||||
def create_curve(self, geometry):
|
||||
curve = bpy.data.curves.new(geometry.id, type='CURVE')
|
||||
@@ -2057,6 +2120,7 @@ class IfcImportSettings:
|
||||
settings.should_use_cpu_multiprocessing = scene_bim.import_should_use_cpu_multiprocessing
|
||||
settings.should_import_with_profiling = scene_bim.import_should_import_with_profiling
|
||||
settings.should_import_native = scene_bim.import_should_import_native
|
||||
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
|
||||
settings.should_use_legacy = scene_bim.import_should_use_legacy
|
||||
settings.should_import_aggregates = scene_bim.import_should_import_aggregates
|
||||
settings.should_merge_aggregates = scene_bim.import_should_merge_aggregates
|
||||
@@ -2064,4 +2128,6 @@ class IfcImportSettings:
|
||||
settings.should_merge_by_material = scene_bim.import_should_merge_by_material
|
||||
settings.should_merge_materials_by_colour = scene_bim.import_should_merge_materials_by_colour
|
||||
settings.should_clean_mesh = scene_bim.import_should_clean_mesh
|
||||
settings.deflection_tolerance = scene_bim.import_deflection_tolerance
|
||||
settings.angular_tolerance = scene_bim.import_angular_tolerance
|
||||
return settings
|
||||
|
||||
@@ -1,22 +1,39 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatVectorProperty, FloatProperty
|
||||
from bpy.props import FloatVectorProperty, FloatProperty, BoolProperty
|
||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||
from mathutils import Vector
|
||||
|
||||
def add_object(self, context):
|
||||
verts = [
|
||||
Vector((0, 0, 0)),
|
||||
Vector((0, 0, self.height)),
|
||||
Vector((self.length, 0, self.height)),
|
||||
Vector((self.length, 0, 0)),
|
||||
]
|
||||
edges = []
|
||||
faces = [[0, 1, 2, 3]]
|
||||
if self.use_plane:
|
||||
verts = [
|
||||
Vector((0, 0, 0)),
|
||||
Vector((0, 0, self.height)),
|
||||
Vector((self.length, 0, self.height)),
|
||||
Vector((self.length, 0, 0)),
|
||||
]
|
||||
edges = []
|
||||
faces = [[0, 1, 2, 3]]
|
||||
else:
|
||||
verts = [
|
||||
Vector((0, 0, 0)),
|
||||
Vector((self.length, 0, 0)),
|
||||
]
|
||||
edges = [[0, 1]]
|
||||
faces = []
|
||||
|
||||
mesh = bpy.data.meshes.new(name="Dumb Wall")
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
obj = object_data_add(context, mesh, operator=self)
|
||||
if not self.use_plane:
|
||||
modifier = obj.modifiers.new('Wall Height', 'SCREW')
|
||||
modifier.angle = 0
|
||||
modifier.screw_offset = self.height
|
||||
modifier.use_smooth_shade = False
|
||||
modifier.use_normal_calculate = True
|
||||
modifier.use_normal_flip = True
|
||||
modifier.steps = 1
|
||||
modifier.render_steps = 1
|
||||
modifier = obj.modifiers.new('Wall Width', 'SOLIDIFY')
|
||||
modifier.use_even_offset = True
|
||||
modifier.thickness = self.width
|
||||
@@ -34,6 +51,7 @@ class BIM_OT_add_object(Operator, AddObjectHelper):
|
||||
height: FloatProperty(name='Height', default=3)
|
||||
length: FloatProperty(name='Length', default=1)
|
||||
width: FloatProperty(name='Width', default=.2)
|
||||
use_plane: BoolProperty(name='Use Plane', default=False)
|
||||
|
||||
def execute(self, context):
|
||||
add_object(self, context)
|
||||
|
||||
@@ -5,6 +5,7 @@ import time
|
||||
import json
|
||||
import logging
|
||||
import webbrowser
|
||||
import subprocess
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.geolocation
|
||||
@@ -50,12 +51,26 @@ def depsgraph_update_pre_handler(scene):
|
||||
|
||||
def set_active_camera_resolution(scene):
|
||||
if not scene.camera \
|
||||
or '/' not in scene.camera.name:
|
||||
or '/' not in scene.camera.name \
|
||||
or not scene.DocProperties.drawings:
|
||||
return
|
||||
if scene.render.resolution_x != scene.camera.data.BIMCameraProperties.raster_x \
|
||||
or scene.render.resolution_y != scene.camera.data.BIMCameraProperties.raster_y:
|
||||
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x
|
||||
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y
|
||||
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
|
||||
if scene.camera != current_drawing.camera:
|
||||
scene.DocProperties.current_drawing_index = scene.DocProperties.drawings.find(scene.camera.name.split('/')[1])
|
||||
bpy.ops.bim.activate_view(drawing_index=scene.DocProperties.current_drawing_index)
|
||||
|
||||
|
||||
def open_with_user_command(user_command, path):
|
||||
if user_command:
|
||||
commands = eval(user_command)
|
||||
for command in commands:
|
||||
subprocess.run(command)
|
||||
else:
|
||||
webbrowser.open('file://' + path)
|
||||
|
||||
|
||||
class ExportIFC(bpy.types.Operator):
|
||||
@@ -94,6 +109,8 @@ class ExportIFC(bpy.types.Operator):
|
||||
if not bpy.context.scene.DocProperties.ifc_files:
|
||||
new = bpy.context.scene.DocProperties.ifc_files.add()
|
||||
new.name = output_file
|
||||
if not bpy.context.scene.BIMProperties.ifc_file:
|
||||
bpy.context.scene.BIMProperties.ifc_file = output_file
|
||||
return {'FINISHED'}
|
||||
|
||||
class ImportIFC(bpy.types.Operator, ImportHelper):
|
||||
@@ -1905,11 +1922,18 @@ class ExplodeAggregate(bpy.types.Operator):
|
||||
class LoadClassification(bpy.types.Operator):
|
||||
bl_idname = 'bim.load_classification'
|
||||
bl_label = 'Load Classification'
|
||||
is_file: bpy.props.BoolProperty()
|
||||
classification_index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
from . import prop
|
||||
prop.ClassificationView.raw_data = schema.ifc.load_classification(
|
||||
context.scene.BIMProperties.classification)
|
||||
if self.is_file:
|
||||
prop.ClassificationView.raw_data = schema.ifc.load_classification(
|
||||
context.scene.BIMProperties.classification)
|
||||
else:
|
||||
prop.ClassificationView.raw_data = schema.ifc.load_classification(
|
||||
context.scene.BIMProperties.classifications[self.classification_index].name,
|
||||
self.classification_index)
|
||||
context.scene.BIMProperties.classification_references.root = ''
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -1932,7 +1956,7 @@ class AddClassification(bpy.types.Operator):
|
||||
for key, value in data_map.items():
|
||||
if hasattr(data, value) and getattr(data, value):
|
||||
setattr(classification, key, str(getattr(data, value)))
|
||||
classification.filename = context.scene.BIMProperties.classification
|
||||
classification.data = schema.ifc.classification_files[context.scene.BIMProperties.classification].to_string()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -1962,7 +1986,7 @@ class AssignClassification(bpy.types.Operator):
|
||||
for key in ['location', 'description']:
|
||||
if data[key]:
|
||||
setattr(classification, key, data[key])
|
||||
classification.referenced_source = bpy.context.scene.BIMProperties.classification
|
||||
classification.referenced_source = bpy.context.scene.BIMProperties.active_classification_name
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -2089,9 +2113,9 @@ class OpenView(bpy.types.Operator):
|
||||
view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
webbrowser.open('file://' + os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir, 'diagrams',
|
||||
self.view + '.svg'))
|
||||
open_with_user_command(
|
||||
bpy.context.preferences.addons['blenderbim'].preferences.svg_command,
|
||||
os.path.join(bpy.context.scene.BIMProperties.data_dir, 'diagrams', self.view + '.svg'))
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -2103,16 +2127,15 @@ class CutSection(bpy.types.Operator):
|
||||
camera = bpy.context.scene.camera
|
||||
if not (camera.type == 'CAMERA' and camera.data.type == 'ORTHO'):
|
||||
return {'FINISHED'}
|
||||
bpy.ops.bim.activate_view(drawing_index=bpy.context.scene.DocProperties.drawings.find(camera.name.split('/')[1]))
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles[camera.data.BIMCameraProperties.active_drawing_style_index]
|
||||
self.diagram_name = camera.name.split('/')[1]
|
||||
bpy.context.scene.render.filepath = os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir,
|
||||
'diagrams',
|
||||
'{}.png'.format(self.diagram_name)
|
||||
)
|
||||
if bpy.context.scene.DocProperties.should_render == 'DEFAULT':
|
||||
bpy.ops.render.render(write_still=True)
|
||||
elif bpy.context.scene.DocProperties.should_render == 'VIEWPORT':
|
||||
bpy.ops.render.opengl(write_still=True)
|
||||
self.create_raster(camera, drawing_style)
|
||||
location = camera.location
|
||||
render = bpy.context.scene.render
|
||||
if self.is_landscape():
|
||||
@@ -2130,7 +2153,7 @@ class CutSection(bpy.types.Operator):
|
||||
import ifccsv
|
||||
ifc_cutter.ifc_filenames = [i.name for i in bpy.context.scene.DocProperties.ifc_files]
|
||||
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
|
||||
ifc_cutter.vector_style = bpy.context.scene.DocProperties.drawing_styles[camera.data.BIMCameraProperties.active_drawing_style_index].vector_style
|
||||
ifc_cutter.vector_style = drawing_style.vector_style
|
||||
ifc_cutter.diagram_name = self.diagram_name
|
||||
ifc_cutter.background_image = bpy.context.scene.render.filepath
|
||||
if camera.data.BIMCameraProperties.cut_objects == 'CUSTOM':
|
||||
@@ -2148,7 +2171,17 @@ class CutSection(bpy.types.Operator):
|
||||
ifc_cutter.section_level_obj = None
|
||||
ifc_cutter.grid_objs = []
|
||||
ifc_cutter.text_objs = []
|
||||
ifc_cutter.misc_objs = []
|
||||
ifc_cutter.attributes = [a.name for a in drawing_style.attributes]
|
||||
for obj in camera.users_collection[0].objects:
|
||||
if 'IfcGrid' in obj.name:
|
||||
ifc_cutter.grid_objs.append(obj)
|
||||
elif 'IfcGroup' in obj.name and obj.type == 'CAMERA':
|
||||
ifc_cutter.camera_obj = obj
|
||||
|
||||
if 'IfcAnnotation/' not in obj.name:
|
||||
continue
|
||||
|
||||
if 'Leader' in obj.name:
|
||||
ifc_cutter.leader_obj = (obj, obj.data)
|
||||
elif 'Stair' in obj.name:
|
||||
@@ -2163,16 +2196,14 @@ class CutSection(bpy.types.Operator):
|
||||
ifc_cutter.hidden_objs.append((obj, obj.data))
|
||||
elif 'Solid' in obj.name:
|
||||
ifc_cutter.solid_objs.append((obj, obj.data))
|
||||
elif 'IfcGrid' in obj.name:
|
||||
ifc_cutter.grid_objs.append(obj)
|
||||
elif 'Plan Level' in obj.name:
|
||||
ifc_cutter.plan_level_obj = obj
|
||||
elif 'Section Level' in obj.name:
|
||||
ifc_cutter.section_level_obj = obj
|
||||
elif obj.type == 'CAMERA':
|
||||
ifc_cutter.camera_obj = obj
|
||||
elif obj.type == 'FONT':
|
||||
ifc_cutter.text_objs.append(obj)
|
||||
else:
|
||||
ifc_cutter.misc_objs.append(obj)
|
||||
|
||||
ifc_cutter.section_box = {
|
||||
'projection': tuple(projection),
|
||||
@@ -2216,9 +2247,53 @@ class CutSection(bpy.types.Operator):
|
||||
bpy.ops.bim.open_view(view=self.diagram_name)
|
||||
return {'FINISHED'}
|
||||
|
||||
def create_raster(self, camera, drawing_style):
|
||||
if drawing_style.render_type == 'NONE':
|
||||
return
|
||||
|
||||
if drawing_style.render_type == 'DEFAULT':
|
||||
return bpy.ops.render.render(write_still=True)
|
||||
|
||||
previous_visibility = {}
|
||||
for obj in camera.users_collection[0].objects:
|
||||
previous_visibility[obj.name] = obj.hide_get()
|
||||
obj.hide_set(True)
|
||||
for obj in bpy.context.visible_objects:
|
||||
if not obj.data \
|
||||
or isinstance(obj.data, bpy.types.Camera) \
|
||||
or 'IfcGrid/' in obj.name \
|
||||
or 'IfcGridAxis/' in obj.name \
|
||||
or 'IfcOpeningElement/' in obj.name \
|
||||
or self.does_obj_have_target_view_representation(obj, camera):
|
||||
previous_visibility[obj.name] = obj.hide_get()
|
||||
obj.hide_set(True)
|
||||
|
||||
space = self.get_view_3d()
|
||||
previous_shading = space.shading.type
|
||||
space.shading.type = 'RENDERED'
|
||||
bpy.ops.render.opengl(write_still=True)
|
||||
space.shading.type = previous_shading
|
||||
|
||||
for name, value in previous_visibility.items():
|
||||
bpy.data.objects[name].hide_set(value)
|
||||
|
||||
|
||||
def does_obj_have_target_view_representation(self, obj, camera):
|
||||
return camera.data.BIMCameraProperties.target_view in [c.target_view for c in obj.BIMObjectProperties.representation_contexts]
|
||||
|
||||
def is_landscape(self):
|
||||
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
|
||||
|
||||
def get_view_3d(self):
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type != 'VIEW_3D':
|
||||
continue
|
||||
for space in area.spaces:
|
||||
if space.type != 'VIEW_3D':
|
||||
continue
|
||||
return space
|
||||
|
||||
|
||||
|
||||
class AddSheet(bpy.types.Operator):
|
||||
bl_idname = 'bim.add_sheet'
|
||||
@@ -2239,9 +2314,11 @@ class OpenSheet(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.DocProperties
|
||||
webbrowser.open('file://' + os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir, 'sheets',
|
||||
props.sheets[props.active_sheet_index].name + '.svg'))
|
||||
open_with_user_command(
|
||||
bpy.context.preferences.addons['blenderbim'].preferences.svg_command,
|
||||
os.path.join(
|
||||
bpy.context.scene.BIMProperties.data_dir, 'sheets',
|
||||
props.sheets[props.active_sheet_index].name + '.svg'))
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -2253,9 +2330,12 @@ class AddDrawingToSheet(bpy.types.Operator):
|
||||
props = bpy.context.scene.DocProperties
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
|
||||
sheet_builder.add_drawing(
|
||||
props.drawings[props.active_drawing_index].name,
|
||||
props.sheets[props.active_sheet_index].name)
|
||||
try:
|
||||
sheet_builder.add_drawing(
|
||||
props.drawings[props.active_drawing_index].name,
|
||||
props.sheets[props.active_sheet_index].name)
|
||||
except:
|
||||
self.report({'ERROR'}, 'Drawings need to be created before being added to a sheet')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -2271,8 +2351,9 @@ class CreateSheets(bpy.types.Operator):
|
||||
sheet_builder.build(name)
|
||||
|
||||
svg2pdf_command = bpy.context.preferences.addons['blenderbim'].preferences.svg2pdf_command
|
||||
svg2dxf_command = bpy.context.preferences.addons['blenderbim'].preferences.svg2dxf_command
|
||||
|
||||
if svg2pdf_command:
|
||||
import subprocess
|
||||
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, 'build', name)
|
||||
svg = os.path.join(path, name + '.svg')
|
||||
pdf = os.path.join(path, name + '.pdf')
|
||||
@@ -2282,9 +2363,7 @@ class CreateSheets(bpy.types.Operator):
|
||||
for command in commands:
|
||||
subprocess.run(command)
|
||||
|
||||
svg2dxf_command = bpy.context.preferences.addons['blenderbim'].preferences.svg2dxf_command
|
||||
if svg2dxf_command:
|
||||
import subprocess
|
||||
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, 'build', name)
|
||||
svg = os.path.join(path, name + '.svg')
|
||||
eps = os.path.join(path, name + '.eps')
|
||||
@@ -2298,18 +2377,21 @@ class CreateSheets(bpy.types.Operator):
|
||||
|
||||
|
||||
if svg2pdf_command:
|
||||
webbrowser.open('file://' + os.path.join(pdf))
|
||||
open_with_user_command(bpy.context.preferences.addons['blenderbim'].preferences.pdf_command, pdf)
|
||||
else:
|
||||
webbrowser.open('file://' + os.path.join(bpy.context.scene.BIMProperties.data_dir, 'build', name, name + '.svg'))
|
||||
open_with_user_command(
|
||||
bpy.context.preferences.addons['blenderbim'].preferences.svg_command,
|
||||
os.path.join(bpy.context.scene.BIMProperties.data_dir, 'build', name, name + '.svg'))
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ActivateView(bpy.types.Operator):
|
||||
bl_idname = 'bim.activate_view'
|
||||
bl_label = 'Activate View'
|
||||
drawing_index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
camera = bpy.context.scene.DocProperties.drawings[bpy.context.scene.DocProperties.active_drawing_index].camera
|
||||
camera = bpy.context.scene.DocProperties.drawings[self.drawing_index].camera
|
||||
if not camera:
|
||||
return {'FINISHED'}
|
||||
bpy.context.scene.camera = camera
|
||||
@@ -2321,38 +2403,10 @@ class ActivateView(bpy.types.Operator):
|
||||
bpy.data.collections.get(collection.name).hide_render = True
|
||||
bpy.context.view_layer.layer_collection.children['Views'].children[camera.users_collection[0].name].hide_viewport = False
|
||||
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class AssignContext(bpy.types.Operator):
|
||||
bl_idname = 'bim.assign_context'
|
||||
bl_label = 'Assign Context'
|
||||
|
||||
def execute(self, context):
|
||||
if not self.is_mesh_context_sensitive(bpy.context.active_object.data.name):
|
||||
bpy.context.active_object.data.name = '{}/{}/{}/{}'.format(
|
||||
bpy.context.scene.BIMProperties.available_contexts,
|
||||
bpy.context.scene.BIMProperties.available_subcontexts,
|
||||
bpy.context.scene.BIMProperties.available_target_views,
|
||||
bpy.context.active_object.data.name
|
||||
)
|
||||
else:
|
||||
bpy.context.active_object.data.name = '{}/{}/{}/{}'.format(
|
||||
bpy.context.scene.BIMProperties.available_contexts,
|
||||
bpy.context.scene.BIMProperties.available_subcontexts,
|
||||
bpy.context.scene.BIMProperties.available_target_views,
|
||||
bpy.context.active_object.data.name.split('/')[3]
|
||||
)
|
||||
return {'FINISHED'}
|
||||
|
||||
def is_mesh_context_sensitive(self, name):
|
||||
return '/' in name \
|
||||
and ( \
|
||||
name[0:6] == 'Model/' \
|
||||
or name[0:5] == 'Plan/' \
|
||||
)
|
||||
|
||||
|
||||
class SwitchContext(bpy.types.Operator):
|
||||
bl_idname = 'bim.switch_context'
|
||||
bl_label = 'Switch Context'
|
||||
@@ -2488,7 +2542,7 @@ class OpenUpstream(bpy.types.Operator):
|
||||
elif self.page == 'docs':
|
||||
webbrowser.open('https://blenderbim.org/docs/')
|
||||
elif self.page == 'wiki':
|
||||
webbrowser.open('https://wiki.osarch.org/')
|
||||
webbrowser.open('https://wiki.osarch.org/index.php?title=Category:BlenderBIM_Add-on')
|
||||
elif self.page == 'community':
|
||||
webbrowser.open('https://community.osarch.org/')
|
||||
return {'FINISHED'}
|
||||
@@ -2557,6 +2611,46 @@ class BIM_OT_CopyAttributesToSelection(bpy.types.Operator):
|
||||
except: pass
|
||||
|
||||
|
||||
class CopyPropertyToSelection(bpy.types.Operator):
|
||||
bl_idname = 'bim.copy_property_to_selection'
|
||||
bl_label = 'Copy Property To Selection'
|
||||
pset_name: bpy.props.StringProperty()
|
||||
prop_name: bpy.props.StringProperty()
|
||||
prop_value: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.applicable_psets_cache = {}
|
||||
self.empty = ifcopenshell.file()
|
||||
for obj in bpy.context.selected_objects:
|
||||
if '/' not in obj.name:
|
||||
continue
|
||||
pset = obj.BIMObjectProperties.psets.get(self.pset_name)
|
||||
if not pset:
|
||||
applicable_psets = self.get_applicable_psets(obj.name.split('/')[0])
|
||||
if self.pset_name not in applicable_psets:
|
||||
continue
|
||||
pset = obj.BIMObjectProperties.psets.add()
|
||||
pset.name = self.pset_name
|
||||
for template_prop_name in schema.ifc.psets[self.pset_name]['HasPropertyTemplates'].keys():
|
||||
prop = pset.properties.add()
|
||||
prop.name = template_prop_name
|
||||
prop = pset.properties.get(self.prop_name)
|
||||
if prop:
|
||||
prop.string_value = self.prop_value
|
||||
return {'FINISHED'}
|
||||
|
||||
# TODO: move into util module. See bug #971
|
||||
def get_applicable_psets(self, element_class):
|
||||
if element_class not in self.applicable_psets_cache:
|
||||
element = self.empty.create_entity(element_class)
|
||||
applicable_psets = []
|
||||
for ifc_class, pset_names in schema.ifc.applicable_psets.items():
|
||||
if element.is_a(ifc_class):
|
||||
applicable_psets.extend(pset_names)
|
||||
self.applicable_psets_cache[element_class] = applicable_psets
|
||||
return self.applicable_psets_cache[element_class]
|
||||
|
||||
|
||||
class BIM_OT_ChangeClassificationLevel(bpy.types.Operator):
|
||||
bl_idname = "bim.change_classification_level"
|
||||
bl_label = "Change Classification Level"
|
||||
@@ -2716,9 +2810,13 @@ class AddSectionPlane(bpy.types.Operator):
|
||||
section = bpy.data.objects.new('Section', None)
|
||||
section.empty_display_type = 'SINGLE_ARROW'
|
||||
section.empty_display_size = 5
|
||||
section.rotation_euler = Euler((radians(180.0), 0.0, 0.0), 'XYZ')
|
||||
section.location = bpy.context.scene.cursor.location
|
||||
section.show_in_front = True
|
||||
if bpy.context.active_object.select_get() \
|
||||
and isinstance(bpy.context.active_object.data, bpy.types.Camera):
|
||||
section.matrix_world = bpy.context.active_object.matrix_world @ Euler((radians(180.0), 0.0, 0.0), 'XYZ').to_matrix().to_4x4()
|
||||
else:
|
||||
section.rotation_euler = Euler((radians(180.0), 0.0, 0.0), 'XYZ')
|
||||
section.location = bpy.context.scene.cursor.location
|
||||
collection = bpy.data.collections.get('Sections')
|
||||
if not collection:
|
||||
collection = bpy.data.collections.new('Sections')
|
||||
@@ -3401,7 +3499,7 @@ class GuessQuantity(bpy.types.Operator):
|
||||
def get_prefix_name(self, value):
|
||||
if '/' in value:
|
||||
return value.split('/')
|
||||
return None, bpy.context.scene.BIMProperties.area_unit
|
||||
return None, value
|
||||
|
||||
def get_blender_prefix_name(self):
|
||||
if bpy.context.scene.unit_settings.system == 'IMPERIAL':
|
||||
@@ -3428,7 +3526,7 @@ class ExecuteBIMTester(bpy.types.Operator):
|
||||
os.chdir(bpy.context.scene.BIMProperties.features_dir)
|
||||
bimtester.run_tests({'feature': filename, 'advanced_arguments': None, 'console': False})
|
||||
bimtester.generate_report()
|
||||
webbrowser.open(os.path.join(
|
||||
webbrowser.open('file://' + os.path.join(
|
||||
bpy.context.scene.BIMProperties.features_dir,
|
||||
'report', bpy.context.scene.BIMProperties.features_file + '.feature.html'))
|
||||
os.chdir(cwd)
|
||||
@@ -3534,7 +3632,10 @@ class AddOpening(bpy.types.Operator):
|
||||
bl_label = 'Add Opening'
|
||||
|
||||
def execute(self, context):
|
||||
opening = context.active_object
|
||||
if context.active_object.children and 'IfcOpeningElement/' in context.active_object.children[0].name:
|
||||
opening = context.active_object.children[0]
|
||||
else:
|
||||
opening = context.active_object
|
||||
if context.selected_objects[0] != context.active_object:
|
||||
obj = context.selected_objects[0]
|
||||
else:
|
||||
@@ -3584,9 +3685,11 @@ class SaveDrawingStyle(bpy.types.Operator):
|
||||
index: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
space = self.get_view_3d()
|
||||
style = {
|
||||
'bpy.data.worlds[0].color': tuple(bpy.data.worlds[0].color),
|
||||
'bpy.context.scene.render.engine': bpy.context.scene.render.engine,
|
||||
'bpy.context.scene.render.film_transparent': bpy.context.scene.render.film_transparent,
|
||||
'bpy.context.scene.display.shading.show_object_outline': bpy.context.scene.display.shading.show_object_outline,
|
||||
'bpy.context.scene.display.shading.show_cavity': bpy.context.scene.display.shading.show_cavity,
|
||||
'bpy.context.scene.display.shading.cavity_type': bpy.context.scene.display.shading.cavity_type,
|
||||
@@ -3596,7 +3699,18 @@ class SaveDrawingStyle(bpy.types.Operator):
|
||||
'bpy.context.scene.display.shading.light': bpy.context.scene.display.shading.light,
|
||||
'bpy.context.scene.display.shading.color_type': bpy.context.scene.display.shading.color_type,
|
||||
'bpy.context.scene.display.shading.single_color': tuple(bpy.context.scene.display.shading.single_color),
|
||||
'bpy.context.scene.display.shading.show_shadows': bpy.context.scene.display.shading.show_shadows,
|
||||
'bpy.context.scene.display.shading.shadow_intensity': bpy.context.scene.display.shading.shadow_intensity,
|
||||
'bpy.context.scene.display.light_direction': tuple(bpy.context.scene.display.light_direction),
|
||||
'bpy.context.scene.view_settings.use_curve_mapping': bpy.context.scene.view_settings.use_curve_mapping,
|
||||
'space.overlay.show_wireframes': space.overlay.show_wireframes,
|
||||
'space.overlay.wireframe_threshold': space.overlay.wireframe_threshold,
|
||||
'space.overlay.show_floor': space.overlay.show_floor,
|
||||
'space.overlay.show_axis_x': space.overlay.show_axis_x,
|
||||
'space.overlay.show_axis_y': space.overlay.show_axis_y,
|
||||
'space.overlay.show_axis_z': space.overlay.show_axis_z,
|
||||
'space.overlay.show_object_origins': space.overlay.show_object_origins,
|
||||
'space.overlay.show_relationship_lines': space.overlay.show_relationship_lines,
|
||||
}
|
||||
if self.index:
|
||||
index = int(self.index)
|
||||
@@ -3605,21 +3719,32 @@ class SaveDrawingStyle(bpy.types.Operator):
|
||||
bpy.context.scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
|
||||
return {'FINISHED'}
|
||||
|
||||
def get_view_3d(self):
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type != 'VIEW_3D':
|
||||
continue
|
||||
for space in area.spaces:
|
||||
if space.type != 'VIEW_3D':
|
||||
continue
|
||||
return space
|
||||
|
||||
|
||||
class ActivateDrawingStyle(bpy.types.Operator):
|
||||
bl_idname = 'bim.activate_drawing_style'
|
||||
bl_label = 'Activate Drawing Style'
|
||||
|
||||
def execute(self, context):
|
||||
self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[bpy.context.active_object.data.BIMCameraProperties.active_drawing_style_index]
|
||||
self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[context.scene.camera.data.BIMCameraProperties.active_drawing_style_index]
|
||||
self.set_raster_style()
|
||||
self.set_query()
|
||||
return {'FINISHED'}
|
||||
|
||||
def set_raster_style(self):
|
||||
space = self.get_view_3d()
|
||||
style = json.loads(self.drawing_style.raster_style)
|
||||
bpy.data.worlds[0].color = style['bpy.data.worlds[0].color']
|
||||
bpy.context.scene.render.engine = style['bpy.context.scene.render.engine']
|
||||
bpy.context.scene.render.film_transparent = style['bpy.context.scene.render.film_transparent']
|
||||
bpy.context.scene.display.shading.show_object_outline = style['bpy.context.scene.display.shading.show_object_outline']
|
||||
bpy.context.scene.display.shading.show_cavity = style['bpy.context.scene.display.shading.show_cavity']
|
||||
bpy.context.scene.display.shading.cavity_type = style['bpy.context.scene.display.shading.cavity_type']
|
||||
@@ -3629,14 +3754,30 @@ class ActivateDrawingStyle(bpy.types.Operator):
|
||||
bpy.context.scene.display.shading.light = style['bpy.context.scene.display.shading.light']
|
||||
bpy.context.scene.display.shading.color_type = style['bpy.context.scene.display.shading.color_type']
|
||||
bpy.context.scene.display.shading.single_color = style['bpy.context.scene.display.shading.single_color']
|
||||
bpy.context.scene.display.shading.show_shadows = style['bpy.context.scene.display.shading.show_shadows']
|
||||
bpy.context.scene.display.shading.shadow_intensity = style['bpy.context.scene.display.shading.shadow_intensity']
|
||||
bpy.context.scene.display.light_direction = style['bpy.context.scene.display.light_direction']
|
||||
|
||||
bpy.context.scene.view_settings.use_curve_mapping = style['bpy.context.scene.view_settings.use_curve_mapping']
|
||||
space.overlay.show_wireframes = style['space.overlay.show_wireframes']
|
||||
space.overlay.wireframe_threshold = style['space.overlay.wireframe_threshold']
|
||||
space.overlay.show_floor = style['space.overlay.show_floor']
|
||||
space.overlay.show_axis_x = style['space.overlay.show_axis_x']
|
||||
space.overlay.show_axis_y = style['space.overlay.show_axis_y']
|
||||
space.overlay.show_axis_z = style['space.overlay.show_axis_z']
|
||||
space.overlay.show_object_origins = style['space.overlay.show_object_origins']
|
||||
space.overlay.show_relationship_lines = style['space.overlay.show_relationship_lines']
|
||||
space.shading.type = 'RENDERED'
|
||||
|
||||
def set_query(self):
|
||||
self.selector = ifcopenshell.util.selector.Selector()
|
||||
self.include_global_ids = []
|
||||
self.exclude_global_ids = []
|
||||
for ifc_file in bpy.context.scene.DocProperties.ifc_files:
|
||||
ifc = ifcopenshell.open(ifc_file.name)
|
||||
try:
|
||||
ifc = ifcopenshell.open(ifc_file.name)
|
||||
except:
|
||||
continue
|
||||
if self.drawing_style.include_query:
|
||||
results = self.selector.parse(ifc, self.drawing_style.include_query)
|
||||
self.include_global_ids.extend([e.GlobalId for e in results])
|
||||
@@ -3671,6 +3812,16 @@ class ActivateDrawingStyle(bpy.types.Operator):
|
||||
obj.hide_viewport = True # Note: this breaks alt-H
|
||||
|
||||
|
||||
def get_view_3d(self):
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type != 'VIEW_3D':
|
||||
continue
|
||||
for space in area.spaces:
|
||||
if space.type != 'VIEW_3D':
|
||||
continue
|
||||
return space
|
||||
|
||||
|
||||
class AddDrawing(bpy.types.Operator):
|
||||
bl_idname = 'bim.add_drawing'
|
||||
bl_label = 'Add Drawing'
|
||||
@@ -3697,6 +3848,7 @@ class AddDrawing(bpy.types.Operator):
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == 'VIEW_3D')
|
||||
area.spaces[0].region_3d.view_perspective = 'CAMERA'
|
||||
new.camera = camera
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -3727,22 +3879,6 @@ class EditVectorStyle(bpy.types.Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class PurgeProjectClassifications(bpy.types.Operator):
|
||||
bl_idname = 'bim.purge_project_classifications'
|
||||
bl_label = 'Purge Project Classifications'
|
||||
|
||||
def execute(self, context):
|
||||
self.schema_dir = bpy.context.scene.BIMProperties.schema_dir
|
||||
path = os.path.join(self.schema_dir, 'project_classifications')
|
||||
files = os.listdir(path)
|
||||
for f in files:
|
||||
os.remove(os.path.join(path, f))
|
||||
from . import prop
|
||||
prop.classification_enum.clear()
|
||||
prop.getClassifications(self, context)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RemoveSheet(bpy.types.Operator):
|
||||
bl_idname = 'bim.remove_sheet'
|
||||
bl_label = 'Remove Sheet'
|
||||
@@ -3804,7 +3940,9 @@ class BuildSchedule(bpy.types.Operator):
|
||||
bpy.context.scene.BIMProperties.data_dir, 'schedules',
|
||||
schedule.name + '.svg')
|
||||
schedule_creator.schedule(schedule.file, outfile)
|
||||
webbrowser.open('file://' + outfile)
|
||||
open_with_user_command(
|
||||
bpy.context.preferences.addons['blenderbim'].preferences.svg_command,
|
||||
outfile)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -3856,3 +3994,132 @@ class GetNorthOffset(bpy.types.Operator):
|
||||
bpy.context.scene.MapConversion.x_axis_abscissa = str(cos(x_angle))
|
||||
bpy.context.scene.MapConversion.x_axis_ordinate = str(sin(x_angle))
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class AddDrawingStyleAttribute(bpy.types.Operator):
|
||||
bl_idname = 'bim.add_drawing_style_attribute'
|
||||
bl_label = 'Add Drawing Style Attribute'
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RemoveDrawingStyleAttribute(bpy.types.Operator):
|
||||
bl_idname = 'bim.remove_drawing_style_attribute'
|
||||
bl_label = 'Remove Drawing Style Attribute'
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CreateShapeFromStepId(bpy.types.Operator):
|
||||
bl_idname = 'bim.create_shape_from_step_id'
|
||||
bl_label = 'Create Shape From STEP ID'
|
||||
|
||||
def execute(self, context):
|
||||
logger = logging.getLogger('ImportIFC')
|
||||
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
|
||||
self.file = ifc.IfcStore.get_file()
|
||||
element = self.file.by_id(int(bpy.context.scene.BIMDebugProperties.step_id))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
#settings.set(settings.INCLUDE_CURVES, True)
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
|
||||
ifc_importer.file = self.file
|
||||
mesh = ifc_importer.create_mesh(element, shape)
|
||||
obj = bpy.data.objects.new('Debug', mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class SelectHighPolygonMeshes(bpy.types.Operator):
|
||||
bl_idname = 'bim.select_high_polygon_meshes'
|
||||
bl_label = 'Select High Polygon Meshes'
|
||||
|
||||
def execute(self, context):
|
||||
results = {}
|
||||
for obj in bpy.data.objects:
|
||||
if not isinstance(obj.data, bpy.types.Mesh) \
|
||||
or len(obj.data.polygons) < int(bpy.context.scene.BIMDebugProperties.number_of_polygons):
|
||||
continue
|
||||
try:
|
||||
obj.select_set(True)
|
||||
except:
|
||||
# If it is not in the view layer
|
||||
pass
|
||||
relating_type = obj.BIMObjectProperties.relating_type
|
||||
if relating_type:
|
||||
relating_type.select_set(True)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RefreshDrawingList(bpy.types.Operator):
|
||||
bl_idname = 'bim.refresh_drawing_list'
|
||||
bl_label = 'Refresh Drawing List'
|
||||
|
||||
def execute(self, context):
|
||||
while len(bpy.context.scene.DocProperties.drawings) > 0:
|
||||
bpy.context.scene.DocProperties.drawings.remove(0)
|
||||
for obj in bpy.context.scene.objects:
|
||||
if not isinstance(obj.data, bpy.types.Camera):
|
||||
continue
|
||||
if 'IfcGroup/' in obj.name and obj.users_collection[0].name == obj.name:
|
||||
new = bpy.context.scene.DocProperties.drawings.add()
|
||||
new.name = obj.name.split('/')[1]
|
||||
new.camera = obj
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class GetRepresentationIfcParameters(bpy.types.Operator):
|
||||
bl_idname = 'bim.get_representation_ifc_parameters'
|
||||
bl_label = 'Get Representation IFC Parameters'
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.active_object.data.BIMMeshProperties
|
||||
dummy = ifcopenshell.file.from_string(props.ifc_definition)
|
||||
for element in dummy:
|
||||
if not element.is_a('IfcRepresentationItem'):
|
||||
continue
|
||||
for i in range(0, len(element)):
|
||||
if element.attribute_type(i) == 'DOUBLE':
|
||||
new = props.ifc_parameters.add()
|
||||
new.name = '{}/{}'.format(element.is_a(), element.attribute_name(i))
|
||||
new.step_id = element.id()
|
||||
new.type = element.attribute_type(i)
|
||||
new.index = i
|
||||
if element[i]:
|
||||
new.value = element[i]
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class UpdateIfcRepresentation(bpy.types.Operator):
|
||||
bl_idname = 'bim.update_ifc_representation'
|
||||
bl_label = 'Update IFC Representation'
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.active_object.data.BIMMeshProperties
|
||||
parameter = props.ifc_parameters[self.index]
|
||||
dummy = ifcopenshell.file.from_string(props.ifc_definition)
|
||||
element = dummy.by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||
props.ifc_definition = dummy.to_string()
|
||||
self.recreate_ifc_representation()
|
||||
return {'FINISHED'}
|
||||
|
||||
def recreate_ifc_representation(self):
|
||||
props = bpy.context.active_object.data.BIMMeshProperties
|
||||
dummy = ifcopenshell.file.from_string(props.ifc_definition)
|
||||
logger = logging.getLogger('ImportIFC')
|
||||
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
|
||||
element = dummy.by_id(props.ifc_definition_id)
|
||||
settings = ifcopenshell.geom.settings()
|
||||
shape = ifcopenshell.geom.create_shape(settings, element)
|
||||
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
|
||||
ifc_importer.file = dummy
|
||||
mesh = ifc_importer.create_mesh(element, shape)
|
||||
bpy.context.active_object.data.user_remap(mesh)
|
||||
|
||||
@@ -63,15 +63,48 @@ def setDefaultProperties(scene):
|
||||
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
|
||||
subcontext.name = 'Box'
|
||||
subcontext.target_view = 'MODEL_VIEW'
|
||||
if bpy.context.scene.BIMProperties.has_plan_context \
|
||||
and len(bpy.context.scene.BIMProperties.plan_subcontexts) == 0:
|
||||
subcontext = bpy.context.scene.BIMProperties.plan_subcontexts.add()
|
||||
subcontext.name = 'Annotation'
|
||||
subcontext.target_view = 'PLAN_VIEW'
|
||||
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
||||
drawing_style.name = 'Blender Default'
|
||||
bpy.ops.bim.save_drawing_style(index='0')
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
||||
drawing_style.name = 'Shaded'
|
||||
drawing_style.name = 'Technical'
|
||||
drawing_style.render_type = 'VIEWPORT'
|
||||
drawing_style.raster_style = json.dumps({
|
||||
'bpy.data.worlds[0].color': (1, 1, 1),
|
||||
'bpy.context.scene.render.engine': 'BLENDER_WORKBENCH',
|
||||
'bpy.context.scene.render.film_transparent': False,
|
||||
'bpy.context.scene.display.shading.show_object_outline': True,
|
||||
'bpy.context.scene.display.shading.show_cavity': False,
|
||||
'bpy.context.scene.display.shading.cavity_type': 'BOTH',
|
||||
'bpy.context.scene.display.shading.curvature_ridge_factor': 1,
|
||||
'bpy.context.scene.display.shading.curvature_valley_factor': 1,
|
||||
'bpy.context.scene.view_settings.view_transform': 'Standard',
|
||||
'bpy.context.scene.display.shading.light': 'FLAT',
|
||||
'bpy.context.scene.display.shading.color_type': 'SINGLE',
|
||||
'bpy.context.scene.display.shading.single_color': (1, 1, 1),
|
||||
'bpy.context.scene.display.shading.show_shadows': False,
|
||||
'bpy.context.scene.display.shading.shadow_intensity': 0.5,
|
||||
'bpy.context.scene.display.light_direction': (.5, .5, .5),
|
||||
'bpy.context.scene.view_settings.use_curve_mapping': False,
|
||||
'space.overlay.show_wireframes': True,
|
||||
'space.overlay.wireframe_threshold': 0,
|
||||
'space.overlay.show_floor': False,
|
||||
'space.overlay.show_axis_x': False,
|
||||
'space.overlay.show_axis_y': False,
|
||||
'space.overlay.show_axis_z': False,
|
||||
'space.overlay.show_object_origins': False,
|
||||
'space.overlay.show_relationship_lines': False,
|
||||
})
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
||||
drawing_style.name = 'Shaded'
|
||||
drawing_style.render_type = 'VIEWPORT'
|
||||
drawing_style.raster_style = json.dumps({
|
||||
'bpy.data.worlds[0].color': (1, 1, 1),
|
||||
'bpy.context.scene.render.engine': 'BLENDER_WORKBENCH',
|
||||
'bpy.context.scene.render.film_transparent': False,
|
||||
'bpy.context.scene.display.shading.show_object_outline': True,
|
||||
'bpy.context.scene.display.shading.show_cavity': True,
|
||||
'bpy.context.scene.display.shading.cavity_type': 'BOTH',
|
||||
@@ -81,26 +114,23 @@ def setDefaultProperties(scene):
|
||||
'bpy.context.scene.display.shading.light': 'STUDIO',
|
||||
'bpy.context.scene.display.shading.color_type': 'MATERIAL',
|
||||
'bpy.context.scene.display.shading.single_color': (1, 1, 1),
|
||||
'bpy.context.scene.display.shading.show_shadows': True,
|
||||
'bpy.context.scene.display.shading.shadow_intensity': 0.5,
|
||||
'bpy.context.scene.display.light_direction': (.5, .5, .5),
|
||||
'bpy.context.scene.view_settings.use_curve_mapping': False,
|
||||
'space.overlay.show_wireframes': True,
|
||||
'space.overlay.wireframe_threshold': 0,
|
||||
'space.overlay.show_floor': False,
|
||||
'space.overlay.show_axis_x': False,
|
||||
'space.overlay.show_axis_y': False,
|
||||
'space.overlay.show_axis_z': False,
|
||||
'space.overlay.show_object_origins': False,
|
||||
'space.overlay.show_relationship_lines': False,
|
||||
})
|
||||
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
|
||||
drawing_style.name = 'Technical'
|
||||
drawing_style.raster_style = json.dumps({
|
||||
'bpy.data.worlds[0].color': (1, 1, 1),
|
||||
'bpy.context.scene.render.engine': 'BLENDER_WORKBENCH',
|
||||
'bpy.context.scene.display.shading.show_object_outline': True,
|
||||
'bpy.context.scene.display.shading.show_cavity': True,
|
||||
'bpy.context.scene.display.shading.cavity_type': 'BOTH',
|
||||
'bpy.context.scene.display.shading.curvature_ridge_factor': 1,
|
||||
'bpy.context.scene.display.shading.curvature_valley_factor': 1,
|
||||
'bpy.context.scene.view_settings.view_transform': 'Standard',
|
||||
'bpy.context.scene.display.shading.light': 'FLAT',
|
||||
'bpy.context.scene.display.shading.color_type': 'SINGLE',
|
||||
'bpy.context.scene.display.shading.single_color': (1, 1, 1),
|
||||
'bpy.context.scene.view_settings.use_curve_mapping': True,
|
||||
})
|
||||
# TODO: This is used for technical styles, but probably should not be hardcoded
|
||||
bpy.context.scene.view_settings.curve_mapping.curves[3].points.new(.4, 0) # Increase black contrast
|
||||
drawing_style.name = 'Blender Default'
|
||||
drawing_style.render_type = 'DEFAULT'
|
||||
bpy.ops.bim.save_drawing_style(index='2')
|
||||
|
||||
|
||||
def getIfcPredefinedTypes(self, context):
|
||||
@@ -212,6 +242,10 @@ def refreshBoundaryConditionAttributes(self, context):
|
||||
new_attribute.name = attribute['name']
|
||||
|
||||
|
||||
def refreshActiveDrawingIndex(self, context):
|
||||
bpy.ops.bim.activate_view(drawing_index=context.scene.DocProperties.active_drawing_index)
|
||||
|
||||
|
||||
def getIfcProducts(self, context):
|
||||
global products_enum
|
||||
if len(products_enum) < 1:
|
||||
@@ -361,8 +395,6 @@ def getClassifications(self, context):
|
||||
classification_enum.clear()
|
||||
files = os.listdir(os.path.join(self.schema_dir, 'classifications'))
|
||||
classification_enum.extend([(f.replace('.ifc', ''), f.replace('.ifc', ''), '') for f in files])
|
||||
files = os.listdir(os.path.join(self.schema_dir, 'project_classifications'))
|
||||
classification_enum.extend([(f.replace('.ifc', ''), f.replace('.ifc', ''), '') for f in files])
|
||||
return classification_enum
|
||||
|
||||
|
||||
@@ -372,6 +404,7 @@ def refreshReferences(self, context):
|
||||
context.scene.BIMProperties.classification_references.root = ''
|
||||
|
||||
|
||||
# TODO: move into util module. See bug #971
|
||||
def getPsetNames(self, context):
|
||||
global psetnames_enum
|
||||
psetnames_enum.clear()
|
||||
@@ -385,6 +418,7 @@ def getPsetNames(self, context):
|
||||
return psetnames_enum
|
||||
|
||||
|
||||
# TODO: move into util module. See bug #971
|
||||
def getQtoNames(self, context):
|
||||
global qtonames_enum
|
||||
qtonames_enum.clear()
|
||||
@@ -518,22 +552,24 @@ class Sheet(PropertyGroup):
|
||||
class DrawingStyle(PropertyGroup):
|
||||
name: StringProperty(name='Name')
|
||||
raster_style: StringProperty(name='Raster Style')
|
||||
render_type: EnumProperty(items=[
|
||||
('NONE', 'None', ''),
|
||||
('DEFAULT', 'Default', ''),
|
||||
('VIEWPORT', 'Viewport', ''),
|
||||
], name='Render Type', default='VIEWPORT')
|
||||
vector_style: EnumProperty(items=getVectorStyles, name='Vector Style')
|
||||
include_query: StringProperty(name='Include Query')
|
||||
exclude_query: StringProperty(name='Exclude Query')
|
||||
attributes: CollectionProperty(name='Attributes', type=StrProperty)
|
||||
|
||||
|
||||
class DocProperties(PropertyGroup):
|
||||
should_recut: BoolProperty(name="Should Recut", default=True)
|
||||
should_recut_selected: BoolProperty(name="Should Recut Selected Only", default=False)
|
||||
should_render: EnumProperty(items=[
|
||||
('NONE', 'None', ''),
|
||||
('DEFAULT', 'Default', ''),
|
||||
('VIEWPORT', 'Viewport', ''),
|
||||
], name='Should Render', default='DEFAULT')
|
||||
should_extract: BoolProperty(name="Should Extract", default=True)
|
||||
drawings: CollectionProperty(name='Drawings', type=Drawing)
|
||||
active_drawing_index: IntProperty(name='Active Drawing Index')
|
||||
active_drawing_index: IntProperty(name='Active Drawing Index', update=refreshActiveDrawingIndex)
|
||||
current_drawing_index: IntProperty(name='Current Drawing Index')
|
||||
schedules: CollectionProperty(name='Schedules', type=Schedule)
|
||||
active_schedule_index: IntProperty(name='Active Schedule Index')
|
||||
titleblock: EnumProperty(items=getTitleblocks, name="Titleblock", update=refreshTitleblocks)
|
||||
@@ -558,7 +594,7 @@ class BIMCameraProperties(PropertyGroup):
|
||||
raster_y: IntProperty(name='Raster Y', default=1000)
|
||||
is_nts: BoolProperty(name='Is NTS')
|
||||
cut_objects: EnumProperty(items=[
|
||||
('.IfcWall|.IfcSlab|.IfcCurtainWall|.IfcStair|.IfcStairFlight|.IfcColumn|.IfcBeam|.IfcMember|.IfcCovering',
|
||||
('.IfcWall|.IfcSlab|.IfcCurtainWall|.IfcStair|.IfcStairFlight|.IfcColumn|.IfcBeam|.IfcMember|.IfcCovering|.IfcSpace',
|
||||
'Overall Plan / Section', ''),
|
||||
('.IfcElement', 'Detail Drawing', ''),
|
||||
('CUSTOM', 'Custom', '')
|
||||
@@ -987,7 +1023,7 @@ class PropertyTemplate(PropertyGroup):
|
||||
|
||||
|
||||
class Address(PropertyGroup):
|
||||
name: StringProperty(name="Name") # Stores IfcPostalAddress or IfcTelecomAddress
|
||||
name: StringProperty(name="Name", default='IfcPostalAddress') # Stores IfcPostalAddress or IfcTelecomAddress
|
||||
purpose: EnumProperty(items=[
|
||||
('OFFICE', 'OFFICE', 'An office address.'),
|
||||
('SITE', 'SITE', 'A site address.'),
|
||||
@@ -1074,7 +1110,7 @@ class Classification(PropertyGroup):
|
||||
description: StringProperty(name="Description")
|
||||
location: StringProperty(name="Location")
|
||||
reference_tokens: StringProperty(name="Reference Tokens")
|
||||
filename: StringProperty(name="Filename")
|
||||
data: StringProperty(name="Data")
|
||||
|
||||
|
||||
class ClassificationReference(PropertyGroup):
|
||||
@@ -1169,6 +1205,7 @@ class BIMProperties(PropertyGroup):
|
||||
import_should_treat_styled_item_as_material: BoolProperty(name="Import Treating Styled Item as Material", default=False)
|
||||
import_should_use_legacy: BoolProperty(name="Import with Legacy Importer", default=False)
|
||||
import_should_import_native: BoolProperty(name="Import Native Representations", default=False)
|
||||
import_export_should_roundtrip_native: BoolProperty(name="Roundtrip Native Representations", default=False)
|
||||
import_should_use_cpu_multiprocessing: BoolProperty(name="Import with CPU Multiprocessing", default=True)
|
||||
import_should_import_with_profiling: BoolProperty(name="Import with Profiling", default=True)
|
||||
import_should_import_aggregates: BoolProperty(name="Import Aggregates", default=True)
|
||||
@@ -1177,6 +1214,8 @@ class BIMProperties(PropertyGroup):
|
||||
import_should_merge_by_material: BoolProperty(name="Import and Merge by Material", default=False)
|
||||
import_should_merge_materials_by_colour: BoolProperty(name="Import and Merge Materials by Colour", default=False)
|
||||
import_should_clean_mesh: BoolProperty(name="Import and Clean Mesh", default=True)
|
||||
import_deflection_tolerance: FloatProperty(name="Import Deflection Tolerance", default=0.001)
|
||||
import_angular_tolerance: FloatProperty(name="Import Angular Tolerance", default=0.5)
|
||||
qa_reject_element_reason: StringProperty(name="Element Rejection Reason")
|
||||
person: EnumProperty(items=getPersons, name="Person")
|
||||
organisation: EnumProperty(items=getOrganisations, name="Organisation")
|
||||
@@ -1208,9 +1247,10 @@ class BIMProperties(PropertyGroup):
|
||||
aggregate_class: EnumProperty(items=getIfcClasses, name="Aggregate Class")
|
||||
aggregate_name: StringProperty(name="Aggregate Name")
|
||||
classification: EnumProperty(items=getClassifications, name="Classification", update=refreshReferences)
|
||||
active_classification_name: StringProperty(name="Active Classification Name")
|
||||
classifications: CollectionProperty(name="Classifications", type=Classification)
|
||||
has_model_context: BoolProperty(name="Has Model Context", default=True)
|
||||
has_plan_context: BoolProperty(name="Has Plan Context", default=False)
|
||||
has_plan_context: BoolProperty(name="Has Plan Context", default=True)
|
||||
model_subcontexts: CollectionProperty(name='Model Subcontexts', type=Subcontext)
|
||||
plan_subcontexts: CollectionProperty(name='Plan Subcontexts', type=Subcontext)
|
||||
available_contexts: EnumProperty(items=[('Model', 'Model', ''), ('Plan', 'Plan', '')], name="Available Contexts")
|
||||
@@ -1311,6 +1351,15 @@ class Attribute(PropertyGroup):
|
||||
int_value: IntProperty(name="Value")
|
||||
float_value: FloatProperty(name="Value")
|
||||
|
||||
|
||||
class IfcParameter(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
step_id: IntProperty(name="STEP ID")
|
||||
index: IntProperty(name="Index")
|
||||
value: FloatProperty(name="Value") # For now, only floats
|
||||
type: StringProperty(name="Type")
|
||||
|
||||
|
||||
class PsetQto(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
@@ -1345,6 +1394,13 @@ class BIMObjectProperties(PropertyGroup):
|
||||
boundary_condition: PointerProperty(name='Boundary Condition', type=BoundaryCondition)
|
||||
structural_member_connection: PointerProperty(name='Structural Member Connection', type=bpy.types.Object)
|
||||
representation_contexts: CollectionProperty(name="Representation Contexts", type=Subcontext)
|
||||
# Address applies to IfcSite's SiteAddress and IfcBuilding's BuildingAddress
|
||||
address: PointerProperty(name='Address', type=Address)
|
||||
|
||||
|
||||
class BIMDebugProperties(PropertyGroup):
|
||||
step_id: IntProperty(name="STEP ID")
|
||||
number_of_polygons: IntProperty(name="Number of Polygons")
|
||||
|
||||
|
||||
class BIMMaterialProperties(PropertyGroup):
|
||||
@@ -1379,5 +1435,7 @@ class BIMMeshProperties(PropertyGroup):
|
||||
is_parametric: BoolProperty(name='Is Parametric', default=False)
|
||||
presentation_layer: StringProperty(name="Presentation Layer")
|
||||
geometry_type: StringProperty(name="Geometry Type")
|
||||
representation_items: CollectionProperty(name="Representation Items", type=RepresentationItem)
|
||||
ifc_definition: StringProperty(name="IFC Definition")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
|
||||
active_representation_item_index: IntProperty(name='Active Representation Item Index')
|
||||
|
||||
@@ -62,14 +62,17 @@ class IfcSchema():
|
||||
entity = prop.ApplicableEntity if prop.ApplicableEntity else 'IfcRoot'
|
||||
self.applicable_psets.setdefault(entity, []).append(prop.Name)
|
||||
|
||||
def load_classification(self, filename):
|
||||
if filename not in self.classifications:
|
||||
classification_path = os.path.join(self.schema_dir, 'classifications', '{}.ifc'.format(filename))
|
||||
if not os.path.isfile(classification_path):
|
||||
classification_path = os.path.join(self.schema_dir, 'project_classifications', '{}.ifc'.format(filename))
|
||||
self.classification_files[filename] = ifcopenshell.open(classification_path)
|
||||
self.classifications[filename] = self.classification_files[filename].by_type('IfcClassification')[0]
|
||||
classification = self.classifications[filename]
|
||||
def load_classification(self, name, classification_index=None):
|
||||
if name not in self.classifications:
|
||||
if classification_index is not None:
|
||||
self.classification_files[name] = ifcopenshell.file.from_string(
|
||||
bpy.context.scene.BIMProperties.classifications[classification_index].data)
|
||||
else:
|
||||
classification_path = os.path.join(self.schema_dir, 'classifications', '{}.ifc'.format(name))
|
||||
self.classification_files[name] = ifcopenshell.open(classification_path)
|
||||
self.classifications[name] = self.classification_files[name].by_type('IfcClassification')[0]
|
||||
classification = self.classifications[name]
|
||||
bpy.context.scene.BIMProperties.active_classification_name = self.classifications[name].Name
|
||||
return {
|
||||
'name': '',
|
||||
'description': '',
|
||||
|
||||
@@ -43,6 +43,9 @@ class SheetBuilder:
|
||||
sheet_path = os.path.join(self.data_dir, 'sheets', sheet_name + '.svg')
|
||||
view_path = os.path.join(self.data_dir, 'diagrams', view_name + '.svg')
|
||||
|
||||
if not os.path.isfile(view_path):
|
||||
raise FileNotFoundError
|
||||
|
||||
ET.register_namespace('', 'http://www.w3.org/2000/svg')
|
||||
ET.register_namespace('xlink', 'http://www.w3.org/1999/xlink')
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import os
|
||||
import re
|
||||
import bpy
|
||||
import math
|
||||
import pystache
|
||||
import xml.etree.ElementTree as ET
|
||||
import svgwrite
|
||||
import ifcopenshell
|
||||
from . import annotation
|
||||
from . import helper
|
||||
from mathutils import Vector
|
||||
from mathutils import geometry
|
||||
|
||||
@@ -159,6 +162,9 @@ class SvgWriter():
|
||||
|
||||
self.draw_ifc_annotation()
|
||||
|
||||
for obj in self.ifc_cutter.misc_objs:
|
||||
self.draw_misc_annotation(obj, ['IfcAnnotation'])
|
||||
|
||||
for obj_data in self.ifc_cutter.hidden_objs:
|
||||
self.draw_line_annotation(obj_data, ['hidden'])
|
||||
|
||||
@@ -169,25 +175,33 @@ class SvgWriter():
|
||||
self.draw_line_annotation(self.ifc_cutter.leader_obj, ['leader'])
|
||||
|
||||
if self.ifc_cutter.plan_level_obj:
|
||||
matrix_world = self.ifc_cutter.plan_level_obj.matrix_world
|
||||
for spline in self.ifc_cutter.plan_level_obj.data.splines:
|
||||
classes = ['annotation', 'plan-level']
|
||||
points = self.get_spline_points(spline)
|
||||
d = ' '.join(['L {} {}'.format((x_offset + p.co.x) * self.scale, (y_offset - p.co.y) * self.scale) for p in points])
|
||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||
d = ' '.join(['L {} {}'.format(
|
||||
(x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
|
||||
for p in projected_points])
|
||||
d = 'M{}'.format(d[1:])
|
||||
path = self.svg.add(self.svg.path(d=d, class_=' '.join(classes)))
|
||||
path['marker-end'] = 'url(#plan-level-marker)'
|
||||
text_position = Vector((
|
||||
(x_offset + points[0].co.x) * self.scale,
|
||||
((y_offset - points[0].co.y) * self.scale) - 2.5
|
||||
(x_offset + projected_points[0].x) * self.scale,
|
||||
((y_offset - projected_points[0].y) * self.scale) - 2.5
|
||||
))
|
||||
# TODO: unhardcode m unit
|
||||
rl = ((self.ifc_cutter.plan_level_obj.matrix_world @
|
||||
# TODO: allow metric to be configurable
|
||||
rl = ((matrix_world @
|
||||
points[0].co).xyz + self.ifc_cutter.plan_level_obj.location).z
|
||||
if points[0].co.x > points[-1].co.x:
|
||||
if bpy.context.scene.unit_settings.system == 'IMPERIAL':
|
||||
rl = helper.format_distance(rl)
|
||||
else:
|
||||
rl = '{:.3f}m'.format(rl)
|
||||
if projected_points[0].x > projected_points[-1].x:
|
||||
text_anchor = 'end'
|
||||
else:
|
||||
text_anchor = 'start'
|
||||
self.svg.add(self.svg.text('RL +{:.3f}m'.format(rl), insert=tuple(text_position), **{
|
||||
self.svg.add(self.svg.text('RL +{}'.format(rl), insert=tuple(text_position), **{
|
||||
'font-size': annotation.Annotator.get_svg_text_size(2.5),
|
||||
'font-family': 'OpenGost Type B TT',
|
||||
'text-anchor': text_anchor,
|
||||
@@ -212,9 +226,13 @@ class SvgWriter():
|
||||
(x_offset + projected_points[0].x) * self.scale,
|
||||
((y_offset - projected_points[0].y) * self.scale) - 3.5
|
||||
))
|
||||
# TODO: unhardcode m unit
|
||||
# TODO: allow metric to be configurable
|
||||
rl = (matrix_world @ points[0].co.xyz).z
|
||||
self.svg.add(self.svg.text('RL +{:.3f}m'.format(rl), insert=tuple(text_position), **{
|
||||
if bpy.context.scene.unit_settings.system == 'IMPERIAL':
|
||||
rl = helper.format_distance(rl)
|
||||
else:
|
||||
rl = '{:.3f}m'.format(rl)
|
||||
self.svg.add(self.svg.text('RL +{}'.format(rl), insert=tuple(text_position), **{
|
||||
'font-size': annotation.Annotator.get_svg_text_size(2.5),
|
||||
'font-family': 'OpenGost Type B TT',
|
||||
'text-anchor': 'start',
|
||||
@@ -243,7 +261,6 @@ class SvgWriter():
|
||||
'alignment-baseline': 'middle',
|
||||
'dominant-baseline': 'middle'
|
||||
}))
|
||||
|
||||
self.draw_text_annotations()
|
||||
|
||||
def draw_ifc_annotation(self):
|
||||
@@ -261,6 +278,74 @@ class SvgWriter():
|
||||
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
||||
end=tuple(end * self.scale), class_=' '.join(annotation['classes'])))
|
||||
|
||||
def draw_misc_annotation(self, obj, classes):
|
||||
# We have to decide whether this should come from Blender or from IFC.
|
||||
# For the moment, for convenience of experimenting with ideas, it comes
|
||||
# from Blender. In the future, it should probably come from IFC.
|
||||
classes.extend(self.get_attribute_classes(obj))
|
||||
if len(obj.data.polygons) == 0:
|
||||
self.draw_edge_annotation(obj, classes)
|
||||
return
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
matrix_world = obj.matrix_world
|
||||
for polygon in obj.data.polygons:
|
||||
points = [obj.data.vertices[v] for v in polygon.vertices]
|
||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||
d = ' '.join(['L {} {}'.format(
|
||||
(x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
|
||||
for p in projected_points])
|
||||
d = 'M{}'.format(d[1:])
|
||||
path = self.svg.add(self.svg.path(d=d, class_=' '.join(classes)))
|
||||
|
||||
def get_attribute_classes(self, obj):
|
||||
classes = [obj.name.split('/')[0]]
|
||||
for slot in obj.material_slots:
|
||||
if slot.material:
|
||||
classes.append('material-{}'.format(
|
||||
re.sub('[^0-9a-zA-Z]+', '', slot.material.name)
|
||||
))
|
||||
result = obj.BIMObjectProperties.attributes.get('GlobalId')
|
||||
if not result:
|
||||
result = obj.BIMObjectProperties.attributes.add()
|
||||
result.name = 'GlobalId'
|
||||
result.string_value = ifcopenshell.guid.new()
|
||||
classes.append('globalid-{}'.format(result.string_value))
|
||||
for attribute in self.ifc_cutter.attributes:
|
||||
result = self.get_obj_value(obj, attribute)
|
||||
if result:
|
||||
classes.append('{}-{}'.format(
|
||||
re.sub('[^0-9a-zA-Z]+', '', attribute),
|
||||
re.sub('[^0-9a-zA-Z]+', '', result)
|
||||
))
|
||||
return classes
|
||||
|
||||
def get_obj_value(self, obj, key):
|
||||
# This is a duplicate implementation of the IFC selector key in Blender
|
||||
# In the future if all this becomes purely IFC based this can be deleted
|
||||
if '.' in key \
|
||||
and key.split('.')[0] == 'type':
|
||||
try:
|
||||
obj = obj.BIMObjectProperties.relating_type
|
||||
except:
|
||||
return
|
||||
key = '.'.join(key.split('.')[1:])
|
||||
result = obj.BIMObjectProperties.attributes.get(key)
|
||||
if result:
|
||||
return result.string_value
|
||||
elif key == 'Name':
|
||||
return obj.name.split('/')[1]
|
||||
elif '.' in key:
|
||||
pset_name, prop = key.split('.')
|
||||
pset = obj.BIMObjectProperties.psets.get(pset_name)
|
||||
if not pset:
|
||||
pset = obj.BIMObjectProperties.qtos.get(pset_name)
|
||||
if not pset:
|
||||
return
|
||||
result = pset.properties.get(prop)
|
||||
if result:
|
||||
return result.string_value
|
||||
|
||||
def draw_line_annotation(self, obj_data, classes):
|
||||
# TODO: properly scope these offsets
|
||||
x_offset = self.raw_width / 2
|
||||
@@ -281,25 +366,41 @@ class SvgWriter():
|
||||
d = 'M{}'.format(d[1:])
|
||||
path = self.svg.add(self.svg.path(d=d, class_=' '.join(classes)))
|
||||
elif isinstance(data, bpy.types.Mesh):
|
||||
for edge in data.edges:
|
||||
v0_global = matrix_world @ data.vertices[edge.vertices[0]].co.xyz
|
||||
v1_global = matrix_world @ data.vertices[edge.vertices[1]].co.xyz
|
||||
v0 = self.project_point_onto_camera(v0_global)
|
||||
v1 = self.project_point_onto_camera(v1_global)
|
||||
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||
vector = end - start
|
||||
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
||||
end=tuple(end * self.scale), class_=' '.join(classes)))
|
||||
self.draw_edge_annotation(obj, classes)
|
||||
|
||||
def draw_edge_annotation(self, obj, classes):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
matrix_world = obj.matrix_world
|
||||
for edge in obj.data.edges:
|
||||
v0_global = matrix_world @ obj.data.vertices[edge.vertices[0]].co.xyz
|
||||
v1_global = matrix_world @ obj.data.vertices[edge.vertices[1]].co.xyz
|
||||
v0 = self.project_point_onto_camera(v0_global)
|
||||
v1 = self.project_point_onto_camera(v1_global)
|
||||
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||
vector = end - start
|
||||
line = self.svg.add(self.svg.line(start=tuple(start * self.scale),
|
||||
end=tuple(end * self.scale), class_=' '.join(classes)))
|
||||
|
||||
def draw_text_annotations(self):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
|
||||
for text_obj in self.ifc_cutter.text_objs:
|
||||
loc, rot, scale = self.ifc_cutter.camera_obj.matrix_world.decompose()
|
||||
pos = (text_obj.location - self.ifc_cutter.camera_obj.location) @ rot.to_matrix()
|
||||
text_position = Vector(((x_offset + pos.x), (y_offset - pos.y)))
|
||||
text_position = self.project_point_onto_camera(text_obj.location)
|
||||
text_position = Vector(((x_offset + text_position.x), (y_offset - text_position.y)))
|
||||
|
||||
local_x_axis = text_obj.matrix_world.to_quaternion() @ Vector((1, 0, 0))
|
||||
projected_x_axis = self.project_point_onto_camera(text_obj.location + local_x_axis)
|
||||
angle = math.degrees((Vector((x_offset + projected_x_axis.x, y_offset -
|
||||
projected_x_axis.y)) - text_position).angle_signed(Vector((1, 0))))
|
||||
|
||||
transform = 'rotate({}, {}, {})'.format(
|
||||
angle,
|
||||
(text_position * self.scale)[0],
|
||||
(text_position * self.scale)[1],
|
||||
)
|
||||
|
||||
if text_obj.data.BIMTextProperties.symbol != 'None':
|
||||
self.svg.add(self.svg.use(
|
||||
@@ -329,12 +430,14 @@ class SvgWriter():
|
||||
self.svg.add(self.svg.text(
|
||||
text_line,
|
||||
insert=tuple((text_position * self.scale) + Vector((0, 3.5*line_number))),
|
||||
class_=' '.join(self.get_attribute_classes(text_obj)),
|
||||
**{
|
||||
'font-size': annotation.Annotator.get_svg_text_size(text_obj.data.BIMTextProperties.font_size),
|
||||
'font-family': 'OpenGost Type B TT',
|
||||
'text-anchor': text_anchor,
|
||||
'alignment-baseline': alignment_baseline,
|
||||
'dominant-baseline': alignment_baseline
|
||||
'dominant-baseline': alignment_baseline,
|
||||
'transform': transform
|
||||
}
|
||||
))
|
||||
|
||||
@@ -391,10 +494,10 @@ class SvgWriter():
|
||||
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||
mid = ((end - start) / 2) + start
|
||||
# TODO: hardcoded meters to mm conversion, until I properly do units
|
||||
vector = end - start
|
||||
perpendicular = Vector((vector.y, -vector.x)).normalized()
|
||||
dimension = (v1_global - v0_global).length * 1000
|
||||
dimension = (v1_global - v0_global).length
|
||||
dimension = helper.format_distance(dimension)
|
||||
sheet_dimension = ((end*self.scale) - (start*self.scale)).length
|
||||
if sheet_dimension < 5: # annotation can't fit
|
||||
# offset text to right of marker
|
||||
@@ -409,7 +512,7 @@ class SvgWriter():
|
||||
if text_override is not None:
|
||||
text = text_override
|
||||
else:
|
||||
text = str(round(dimension))
|
||||
text = str(dimension)
|
||||
self.svg.add(self.svg.text(text, insert=tuple(text_position), **{
|
||||
'transform': 'rotate({} {} {})'.format(
|
||||
rotation,
|
||||
|
||||
@@ -63,6 +63,9 @@ class BIM_PT_object(Panel):
|
||||
row = layout.row()
|
||||
row.prop(props, 'attributes')
|
||||
|
||||
if 'IfcSite/' in context.active_object.name or 'IfcBuilding/' in context.active_object.name:
|
||||
self.draw_addresses_ui()
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, 'relating_type')
|
||||
row.operator('bim.select_similar_type', icon='RESTRICT_SELECT_OFF', text='')
|
||||
@@ -72,6 +75,33 @@ class BIM_PT_object(Panel):
|
||||
row = layout.row()
|
||||
row.prop(props, 'material_type')
|
||||
|
||||
def draw_addresses_ui(self):
|
||||
layout = self.layout
|
||||
layout.label(text="Address:")
|
||||
address = bpy.context.active_object.BIMObjectProperties.address
|
||||
row = layout.row()
|
||||
row.prop(address, 'purpose')
|
||||
if address.purpose == 'USERDEFINED':
|
||||
row = layout.row()
|
||||
row.prop(address, 'user_defined_purpose')
|
||||
row = layout.row()
|
||||
row.prop(address, 'description')
|
||||
|
||||
row = layout.row()
|
||||
row.prop(address, 'internal_location')
|
||||
row = layout.row()
|
||||
row.prop(address, 'address_lines')
|
||||
row = layout.row()
|
||||
row.prop(address, 'postal_box')
|
||||
row = layout.row()
|
||||
row.prop(address, 'town')
|
||||
row = layout.row()
|
||||
row.prop(address, 'region')
|
||||
row = layout.row()
|
||||
row.prop(address, 'postal_code')
|
||||
row = layout.row()
|
||||
row.prop(address, 'country')
|
||||
|
||||
|
||||
class BIM_PT_object_psets(Panel):
|
||||
bl_label = 'IFC Object Property Sets'
|
||||
@@ -101,10 +131,10 @@ class BIM_PT_object_psets(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, 'name', text='')
|
||||
row.prop(prop, 'string_value', text='')
|
||||
op = row.operator('bim.copy_attributes_to_selection', icon='COPYDOWN', text='')
|
||||
op.prop_base = 'BIMObjectProperties.psets[\'{}\'].properties'.format(pset.name)
|
||||
op = row.operator('bim.copy_property_to_selection', icon='COPYDOWN', text='')
|
||||
op.pset_name = pset.name
|
||||
op.prop_name = prop.name
|
||||
op.collection_element = True
|
||||
op.prop_value = prop.string_value
|
||||
|
||||
class BIM_PT_object_qto(Panel):
|
||||
bl_label = 'IFC Object Quantity Sets'
|
||||
@@ -411,7 +441,8 @@ class BIM_PT_representations(Panel):
|
||||
row.prop(bpy.context.scene.BIMProperties, 'available_contexts', text='')
|
||||
row.prop(bpy.context.scene.BIMProperties, 'available_subcontexts', text='')
|
||||
row.prop(bpy.context.scene.BIMProperties, 'available_target_views', text='')
|
||||
row.operator('bim.switch_context', icon='ADD', text='')
|
||||
op = row.operator('bim.switch_context', icon='ADD', text='')
|
||||
op.has_target_context = False
|
||||
|
||||
for index, subcontext in enumerate(props.representation_contexts):
|
||||
row = layout.row(align=True)
|
||||
@@ -501,7 +532,7 @@ class BIM_PT_psets(Panel):
|
||||
|
||||
|
||||
class BIM_PT_classifications(Panel):
|
||||
bl_label = 'IFC Classifications References'
|
||||
bl_label = 'IFC Classifications'
|
||||
bl_idname = 'BIM_PT_classifications'
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_space_type = 'PROPERTIES'
|
||||
@@ -514,7 +545,6 @@ class BIM_PT_classifications(Panel):
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "classification", text='')
|
||||
row.operator("bim.purge_project_classifications", text='', icon='TRASH')
|
||||
row.operator("bim.add_classification", text='', icon='ADD')
|
||||
|
||||
if context.scene.BIMProperties.classification_references.raw_data:
|
||||
@@ -524,7 +554,7 @@ class BIM_PT_classifications(Panel):
|
||||
row.operator("bim.unassign_classification")
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.operator('bim.load_classification')
|
||||
row.operator('bim.load_classification').is_file = True
|
||||
|
||||
if not props.classifications:
|
||||
return
|
||||
@@ -534,6 +564,7 @@ class BIM_PT_classifications(Panel):
|
||||
for index, classification in enumerate(props.classifications):
|
||||
row = layout.row(align=True)
|
||||
row.prop(classification, 'name')
|
||||
row.operator('bim.load_classification', icon='IMPORT', text='').classification_index = index
|
||||
row.operator('bim.remove_classification', icon='X', text='').classification_index = index
|
||||
row = layout.row(align=True)
|
||||
row.prop(classification, 'source')
|
||||
@@ -569,21 +600,23 @@ class BIM_PT_mesh(Panel):
|
||||
layout = self.layout
|
||||
props = context.active_object.data.BIMMeshProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(bpy.context.scene.BIMProperties, 'available_contexts', text='')
|
||||
row.prop(bpy.context.scene.BIMProperties, 'available_subcontexts', text='')
|
||||
row.prop(bpy.context.scene.BIMProperties, 'available_target_views', text='')
|
||||
|
||||
row = layout.row()
|
||||
row.operator('bim.assign_context')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator('bim.push_representation')
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, 'geometry_type')
|
||||
|
||||
layout.template_list('BIM_UL_representation_items', '', props, 'representation_items', props, 'active_representation_item_index')
|
||||
row = layout.row()
|
||||
row.prop(props, 'ifc_definition')
|
||||
layout.label(text="IFC Parameters:")
|
||||
row = layout.row()
|
||||
row.operator('bim.get_representation_ifc_parameters')
|
||||
for index, ifc_parameter in enumerate(props.ifc_parameters):
|
||||
row = layout.row(align=True)
|
||||
row.prop(ifc_parameter, 'name', text='')
|
||||
row.prop(ifc_parameter, 'step_id')
|
||||
row.prop(ifc_parameter, 'index')
|
||||
row.prop(ifc_parameter, 'value', text='')
|
||||
row.operator('bim.update_ifc_representation', icon='FILE_REFRESH', text='').index = index
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, 'presentation_layer')
|
||||
@@ -667,6 +700,15 @@ class BIM_PT_material(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(pset, 'name', text='')
|
||||
row.operator('bim.remove_material_pset', icon='X', text='').pset_index = index
|
||||
op = row.operator('bim.copy_property_to_selection', icon='COPYDOWN', text='')
|
||||
for prop in pset.properties:
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, 'name', text='')
|
||||
row.prop(prop, 'string_value', text='')
|
||||
op = row.operator('bim.copy_property_to_selection', icon='COPYDOWN', text='')
|
||||
op.pset_name = pset.name
|
||||
op.prop_name = prop.name
|
||||
op.prop_value = prop.string_value
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, 'psets', text='')
|
||||
@@ -736,13 +778,13 @@ class BIM_PT_drawings(Panel):
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator('bim.add_drawing')
|
||||
row.operator('bim.refresh_drawing_list', icon='FILE_REFRESH', text='')
|
||||
|
||||
if props.drawings:
|
||||
op = row.operator('bim.open_view', icon='URL', text='')
|
||||
op.view = props.drawings[props.active_drawing_index].name
|
||||
row.operator('bim.activate_view', icon='SCENE', text='')
|
||||
row.operator('bim.remove_drawing', icon='X', text='').index = props.active_drawing_index
|
||||
|
||||
if props.active_drawing_index < len(props.drawings):
|
||||
op = row.operator('bim.open_view', icon='URL', text='')
|
||||
op.view = props.drawings[props.active_drawing_index].name
|
||||
row.operator('bim.remove_drawing', icon='X', text='').index = props.active_drawing_index
|
||||
layout.template_list('BIM_UL_generic', '', props, 'drawings', props, 'active_drawing_index')
|
||||
|
||||
row = layout.row()
|
||||
@@ -863,8 +905,6 @@ class BIM_PT_camera(Panel):
|
||||
row = layout.row()
|
||||
row.prop(dprops, 'should_recut_selected')
|
||||
row = layout.row()
|
||||
row.prop(dprops, 'should_render')
|
||||
row = layout.row()
|
||||
row.prop(dprops, 'should_extract')
|
||||
|
||||
row = layout.row()
|
||||
@@ -910,6 +950,8 @@ class BIM_PT_camera(Panel):
|
||||
row.prop(drawing_style, 'name')
|
||||
row.operator('bim.remove_drawing_style', icon='X', text='').index = props.active_drawing_style_index
|
||||
|
||||
row = layout.row()
|
||||
row.prop(drawing_style, 'render_type')
|
||||
row = layout.row(align=True)
|
||||
row.prop(drawing_style, 'vector_style')
|
||||
row.operator('bim.edit_vector_style', text='', icon='GREASEPENCIL')
|
||||
@@ -918,6 +960,14 @@ class BIM_PT_camera(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(drawing_style, 'exclude_query')
|
||||
|
||||
row = layout.row()
|
||||
row.operator('bim.add_drawing_style_attribute')
|
||||
|
||||
for index, attribute in enumerate(drawing_style.attributes):
|
||||
row = layout.row(align=True)
|
||||
row.prop(attribute, 'name', text='')
|
||||
row.operator('bim.remove_drawing_style_attribute', icon='X', text='').index = index
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator('bim.save_drawing_style')
|
||||
row.operator('bim.activate_drawing_style')
|
||||
@@ -1667,10 +1717,16 @@ class BIM_PT_mvd(Panel):
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_should_import_native')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_export_should_roundtrip_native')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_should_use_cpu_multiprocessing')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_should_import_with_profiling')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_deflection_tolerance')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'import_angular_tolerance')
|
||||
row = layout.row()
|
||||
row.prop(bim_properties, 'export_json_compact')
|
||||
|
||||
layout.label(text='Simplifications:')
|
||||
@@ -1792,19 +1848,12 @@ class BIM_UL_classifications(bpy.types.UIList):
|
||||
layout.label(text=itemdata['name'])
|
||||
|
||||
|
||||
class BIM_UL_representation_items(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
ob = data
|
||||
if item:
|
||||
layout.prop(item, 'name', text='', emboss=False)
|
||||
else:
|
||||
layout.label(text="", translate=False)
|
||||
|
||||
|
||||
class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
bl_idname = 'blenderbim'
|
||||
svg2pdf_command: StringProperty(name="SVG to PDF Command")
|
||||
svg2dxf_command: StringProperty(name="SVG to DXF Command")
|
||||
svg_command: StringProperty(name="SVG Command")
|
||||
pdf_command: StringProperty(name="PDF Command")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -1818,6 +1867,10 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row.prop(self, 'svg2pdf_command')
|
||||
row = layout.row()
|
||||
row.prop(self, 'svg2dxf_command')
|
||||
row = layout.row()
|
||||
row.prop(self, 'svg_command')
|
||||
row = layout.row()
|
||||
row.prop(self, 'pdf_command')
|
||||
|
||||
|
||||
class BIM_PT_ifcclash(Panel):
|
||||
@@ -1952,6 +2005,18 @@ class BIM_PT_annotation_utilities(Panel):
|
||||
op.obj_name = 'Section Level'
|
||||
op.data_type = 'curve'
|
||||
|
||||
props = bpy.context.scene.DocProperties
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator('bim.add_drawing')
|
||||
row.operator('bim.refresh_drawing_list', icon='FILE_REFRESH', text='')
|
||||
|
||||
if props.drawings:
|
||||
if props.active_drawing_index < len(props.drawings):
|
||||
op = row.operator('bim.open_view', icon='URL', text='')
|
||||
op.view = props.drawings[props.active_drawing_index].name
|
||||
row.operator('bim.remove_drawing', icon='X', text='').index = props.active_drawing_index
|
||||
layout.template_list('BIM_UL_generic', '', props, 'drawings', props, 'active_drawing_index')
|
||||
|
||||
|
||||
class BIM_PT_qto_utilities(Panel):
|
||||
@@ -1997,6 +2062,33 @@ class BIM_PT_misc_utilities(Panel):
|
||||
row.operator("bim.set_viewport_shadow_from_sun")
|
||||
|
||||
|
||||
class BIM_PT_debug(Panel):
|
||||
bl_label = "IFC Debug"
|
||||
bl_idname = "BIM_PT_debug"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
bim_props = scene.BIMProperties
|
||||
debug_props = scene.BIMDebugProperties
|
||||
|
||||
row = layout.row()
|
||||
row.prop(debug_props, 'step_id', text='')
|
||||
row = layout.row()
|
||||
row.operator('bim.create_shape_from_step_id')
|
||||
|
||||
row = layout.row()
|
||||
row.prop(debug_props, 'number_of_polygons', text='')
|
||||
row = layout.row()
|
||||
row.operator('bim.select_high_polygon_meshes')
|
||||
|
||||
|
||||
|
||||
def ifc_units(self, context):
|
||||
scene = context.scene
|
||||
props = context.scene.BIMProperties
|
||||
|
||||
@@ -25,7 +25,11 @@ import random
|
||||
import operator
|
||||
import warnings
|
||||
|
||||
from collections import namedtuple, Iterable
|
||||
from collections import namedtuple
|
||||
try: # python 3.3+
|
||||
from collections.abc import Iterable
|
||||
except ModuleNotFoundError: # python 2
|
||||
from collections import Iterable
|
||||
|
||||
try:
|
||||
from OCC.Core import TopoDS, gp, Quantity, BRepTools
|
||||
|
||||
Executable → Regular
+1
-1
@@ -350,7 +350,7 @@ class CollisionManager(object):
|
||||
cdata = fcl.CollisionData()
|
||||
if return_names or return_data:
|
||||
cdata = fcl.CollisionData(request=fcl.CollisionRequest(
|
||||
num_max_contacts=100000, enable_contact=True))
|
||||
num_max_contacts=1000000, enable_contact=True))
|
||||
|
||||
self._manager.collide(cdata, fcl.defaultCollisionCallback)
|
||||
|
||||
|
||||
Executable → Regular
+91
-29
@@ -22,7 +22,8 @@ class IfcClasher:
|
||||
self.settings = settings
|
||||
self.geom_settings = ifcopenshell.geom.settings()
|
||||
self.clash_sets = []
|
||||
#self.tolerance = 0.01
|
||||
self.clash_data = {'meshes': {}}
|
||||
self.global_data = {'meshes': {}, 'matrices': {}}
|
||||
|
||||
def clash(self):
|
||||
for clash_set in self.clash_sets:
|
||||
@@ -36,8 +37,6 @@ class IfcClasher:
|
||||
for data in clash_set[ab]:
|
||||
data['ifc'] = ifcopenshell.open(data['file'])
|
||||
self.patch_ifc(data['ifc'])
|
||||
self.settings.logger.info(f'Purging unnecessary elements {ab} ...')
|
||||
self.purge_elements(data)
|
||||
self.settings.logger.info(f'Creating collision data for {ab} ...')
|
||||
if len(data['ifc'].by_type('IfcElement')) > 0:
|
||||
self.add_collision_objects(data, clash_set[f'{ab}_cm'])
|
||||
@@ -62,10 +61,62 @@ class IfcClasher:
|
||||
b = self.get_element(clash_set['a'], b_global_id)
|
||||
if contact.raw.penetration_depth < tolerance:
|
||||
continue
|
||||
|
||||
# fcl returns contact data for faces that aren't actually
|
||||
# penetrating, but just touching. If our tolerance is zero, then we
|
||||
# consider these as clashes and we move on. If our tolerance is not
|
||||
# zero, fcl has a strange behaviour where the penetration depth can
|
||||
# be a large number even though objects are just touching
|
||||
# https://github.com/flexible-collision-library/fcl/issues/503 In
|
||||
# this case, I don't trust the penetration depth and I run my own
|
||||
# triangle-triangle intersection test. Optimistically, this skips
|
||||
# the false positives. Conservatively, we let the user manually deal
|
||||
# with the false positives and we mark it as a clash.
|
||||
is_optimistic = True # TODO: let user configure this
|
||||
|
||||
if is_optimistic and tolerance != 0:
|
||||
# We'll now check if the contact data's two faces are actually
|
||||
# intersecting, using this brute force check:
|
||||
# https://stackoverflow.com/questions/7113344/find-whether-two-triangles-intersect-or-not
|
||||
# I'm not very good at this kind of code. If you know this stuff
|
||||
# please help rewrite this.
|
||||
|
||||
# Get vertices of clashing tris
|
||||
p1 = self.global_data['meshes'][contact.names[0]].faces[contact.index(contact.names[0])]
|
||||
p2 = self.global_data['meshes'][contact.names[1]].faces[contact.index(contact.names[1])]
|
||||
m1 = self.global_data['matrices'][contact.names[0]]
|
||||
m2 = self.global_data['matrices'][contact.names[1]]
|
||||
v1 = []
|
||||
v2 = []
|
||||
|
||||
for v in p1:
|
||||
v1.append((m1 @ np.array([*self.global_data['meshes'][contact.names[0]].vertices[v], 1]))[0:3].round(2))
|
||||
for v in p2:
|
||||
v2.append((m2 @ np.array([*self.global_data['meshes'][contact.names[1]].vertices[v], 1]))[0:3].round(2))
|
||||
|
||||
tri1_x = 0
|
||||
tri2_x = 0
|
||||
tri1_x += 1 if self.intersect_line_triangle(v1[0], v1[1], v2[0], v2[1], v2[2]) is not None else 0
|
||||
tri1_x += 1 if self.intersect_line_triangle(v1[1], v1[2], v2[0], v2[1], v2[2]) is not None else 0
|
||||
tri1_x += 1 if self.intersect_line_triangle(v1[2], v1[0], v2[0], v2[1], v2[2]) is not None else 0
|
||||
|
||||
tri2_x += 1 if self.intersect_line_triangle(v2[0], v2[1], v1[0], v1[1], v1[2]) is not None else 0
|
||||
tri2_x += 1 if self.intersect_line_triangle(v2[1], v2[2], v1[0], v1[1], v1[2]) is not None else 0
|
||||
tri2_x += 1 if self.intersect_line_triangle(v2[2], v2[0], v1[0], v1[1], v1[2]) is not None else 0
|
||||
intersections = [tri1_x, tri2_x]
|
||||
if intersections == [0, 2] or intersections == [2, 0] or intersections == [1, 1]:
|
||||
# This is a penetrating collision
|
||||
pass
|
||||
else:
|
||||
# This is probably two triangles which just touch
|
||||
continue
|
||||
|
||||
key = f'{a_global_id}-{b_global_id}'
|
||||
|
||||
if key in clash_set['clashes'] \
|
||||
and clash_set['clashes'][key]['penetration_depth'] > contact.raw.penetration_depth:
|
||||
continue
|
||||
|
||||
clash_set['clashes'][key] = {
|
||||
'a_global_id': a_global_id,
|
||||
'b_global_id': b_global_id,
|
||||
@@ -78,6 +129,24 @@ class IfcClasher:
|
||||
'penetration_depth': contact.raw.penetration_depth
|
||||
}
|
||||
|
||||
# https://stackoverflow.com/questions/42740765/intersection-between-line-and-triangle-in-3d
|
||||
def intersect_line_triangle(self, q1, q2, p1, p2, p3):
|
||||
def signed_tetra_volume(a,b,c,d):
|
||||
return np.sign(np.dot(np.cross(b-a,c-a),d-a)/6.0)
|
||||
|
||||
s1 = signed_tetra_volume(q1,p1,p2,p3)
|
||||
s2 = signed_tetra_volume(q2,p1,p2,p3)
|
||||
|
||||
if s1 != s2:
|
||||
s3 = signed_tetra_volume(q1,q2,p1,p2)
|
||||
s4 = signed_tetra_volume(q1,q2,p2,p3)
|
||||
s5 = signed_tetra_volume(q1,q2,p3,p1)
|
||||
if s3 == s4 and s4 == s5:
|
||||
n = np.cross(p2-p1,p3-p1)
|
||||
t = -np.dot(q1,n-p1) / np.dot(q1,q2-q1)
|
||||
return q1 + t * (q2-q1)
|
||||
return None
|
||||
|
||||
def export(self):
|
||||
results = self.clash_sets.copy()
|
||||
for result in results:
|
||||
@@ -85,9 +154,8 @@ class IfcClasher:
|
||||
del result['b_cm']
|
||||
for ab in ['a', 'b']:
|
||||
for data in result[ab]:
|
||||
for key in ['ifc', 'meshes']:
|
||||
if key in data:
|
||||
del data[key]
|
||||
if 'ifc' in data:
|
||||
del data['ifc']
|
||||
with open(self.settings.output, 'w', encoding='utf-8') as clashes_file:
|
||||
json.dump(results, clashes_file, indent=4)
|
||||
|
||||
@@ -100,27 +168,18 @@ class IfcClasher:
|
||||
except:
|
||||
pass
|
||||
|
||||
def purge_elements(self, data):
|
||||
if 'selector' not in data:
|
||||
for element in data['ifc'].by_type('IfcSpace'):
|
||||
data['ifc'].remove(element)
|
||||
return
|
||||
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
elements = selector.parse(data['ifc'], data['selector'])
|
||||
|
||||
if data['mode'] == 'e':
|
||||
for element in data['ifc'].by_type('IfcElement'):
|
||||
if element in elements:
|
||||
data['ifc'].remove(element)
|
||||
elif data['mode'] == 'i':
|
||||
for element in data['ifc'].by_type('IfcElement'):
|
||||
if element not in elements:
|
||||
data['ifc'].remove(element)
|
||||
|
||||
def add_collision_objects(self, data, cm):
|
||||
data['meshes'] = {}
|
||||
iterator = ifcopenshell.geom.iterator(self.geom_settings, data['ifc'], multiprocessing.cpu_count())
|
||||
self.clash_data['meshes'] = {}
|
||||
selector = ifcopenshell.util.selector.Selector()
|
||||
if 'selector' not in data:
|
||||
iterator = ifcopenshell.geom.iterator(self.geom_settings, data['ifc'], multiprocessing.cpu_count(),
|
||||
exclude=(data['ifc'].by_type('IfcSpatialStructureElement')))
|
||||
elif data['mode'] == 'e':
|
||||
iterator = ifcopenshell.geom.iterator(self.geom_settings, data['ifc'], multiprocessing.cpu_count(),
|
||||
exclude=selector.parse(data['ifc'], data['selector']))
|
||||
elif data['mode'] == 'i':
|
||||
iterator = ifcopenshell.geom.iterator(self.geom_settings, data['ifc'], multiprocessing.cpu_count(),
|
||||
include=selector.parse(data['ifc'], data['selector']))
|
||||
valid_file = iterator.initialize()
|
||||
if not valid_file:
|
||||
return False
|
||||
@@ -140,11 +199,12 @@ class IfcClasher:
|
||||
element = data['ifc'].by_id(shape.guid)
|
||||
self.settings.logger.info('Creating object {}'.format(element))
|
||||
mesh_name = f'mesh-{shape.geometry.id}'
|
||||
if mesh_name in data['meshes']:
|
||||
mesh = data['meshes'][mesh_name]
|
||||
if mesh_name in self.clash_data['meshes']:
|
||||
mesh = self.clash_data['meshes'][mesh_name]
|
||||
else:
|
||||
mesh = self.create_mesh(shape)
|
||||
data['meshes'][mesh_name] = mesh
|
||||
self.clash_data['meshes'][mesh_name] = mesh
|
||||
self.global_data['meshes'][shape.guid] = mesh
|
||||
|
||||
m = shape.transformation.matrix.data
|
||||
mat = np.array(
|
||||
@@ -155,7 +215,9 @@ class IfcClasher:
|
||||
[0, 0, 0, 1]
|
||||
]
|
||||
)
|
||||
|
||||
mat.transpose()
|
||||
self.global_data['matrices'][shape.guid] = mat
|
||||
cm.add_object(shape.guid, mesh, mat)
|
||||
|
||||
def create_mesh(self, shape):
|
||||
|
||||
@@ -199,7 +199,7 @@ int main(int argc, char** argv) {
|
||||
typedef char char_t;
|
||||
#endif
|
||||
|
||||
double deflection_tolerance;
|
||||
double deflection_tolerance, angular_tolerance, force_space_transparency;
|
||||
inclusion_filter include_filter;
|
||||
inclusion_traverse_filter include_traverse_filter;
|
||||
exclusion_filter exclude_filter;
|
||||
@@ -328,6 +328,10 @@ int main(int argc, char** argv) {
|
||||
"model in other modelling application in any case.")
|
||||
("deflection-tolerance", po::value<double>(&deflection_tolerance)->default_value(1e-3),
|
||||
"Sets the deflection tolerance of the mesher, 1e-3 by default if not specified.")
|
||||
("force-space-transparency", po::value<double>(&force_space_transparency),
|
||||
"Overrides transparency of spaces in geometry output.")
|
||||
("angular-tolerance", po::value<double>(&angular_tolerance)->default_value(0.5),
|
||||
"Sets the angular tolerance of the mesher in radians 0.5 by default if not specified.")
|
||||
("generate-uvs",
|
||||
"Generates UVs (texture coordinates) by using simple box projection. Requires normals. "
|
||||
"Not guaranteed to work properly if used with --weld-vertices.")
|
||||
@@ -342,7 +346,8 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
short precision;
|
||||
double section_height;
|
||||
std::string svg_scale;
|
||||
std::string svg_scale, svg_center;
|
||||
std::string section_ref, elevation_ref;
|
||||
|
||||
po::options_description serializer_options("Serialization options");
|
||||
serializer_options.add_options()
|
||||
@@ -357,6 +362,13 @@ int main(int argc, char** argv) {
|
||||
("scale", po::value<std::string>(&svg_scale),
|
||||
"Interprets SVG bounds in mm, centers layout and draw elements to scale. "
|
||||
"Only used when converting to SVG. Example 1:100.")
|
||||
("center", po::value<std::string>(&svg_center),
|
||||
"When using --scale, specifies the location in the range [0 1]x[0 1] around which"
|
||||
"to center the drawings. Example 0.5x0.5 (default).")
|
||||
("section-ref", po::value<std::string>(§ion_ref),
|
||||
"Element at which vertical cross sections should be created")
|
||||
("elevation-ref", po::value<std::string>(&elevation_ref),
|
||||
"Element at which vertical elevations should be created")
|
||||
("door-arcs", "Draw door openings arcs for IfcDoor elements")
|
||||
("section-height", po::value<double>(§ion_height),
|
||||
"Specifies the cut section height for SVG 2D geometry.")
|
||||
@@ -519,8 +531,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<double> bounding_width;
|
||||
boost::optional<double> bounding_height;
|
||||
boost::optional<double> bounding_width, bounding_height, relative_center_x, relative_center_y;
|
||||
|
||||
if (vmap.count("bounds") == 1) {
|
||||
int w, h;
|
||||
if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2 && w > 0 && h > 0) {
|
||||
@@ -533,6 +545,18 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (vmap.count("center") == 1) {
|
||||
double cx, cy;
|
||||
if (sscanf(svg_center.c_str(), "%lfx%lf", &cx, &cy) == 2 && cx >= 0. && cy >= 0. && cx <= 1. && cy <= 1.) {
|
||||
relative_center_x = cx;
|
||||
relative_center_y = cy;
|
||||
} else {
|
||||
cerr_ << "[Error] Invalid use of --bounds" << std::endl;
|
||||
print_options(serializer_options);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
const path_t input_filename = vmap["input-file"].as<path_t>();
|
||||
if (!file_exists(IfcUtil::path::to_utf8(input_filename))) {
|
||||
cerr_ << "[Error] Input file '" << input_filename << "' does not exist" << std::endl;
|
||||
@@ -710,7 +734,13 @@ int main(int argc, char** argv) {
|
||||
settings.set(SerializerSettings::USE_ELEMENT_TYPES, use_element_types);
|
||||
settings.set(SerializerSettings::USE_ELEMENT_HIERARCHY, use_element_hierarchy);
|
||||
settings.set_deflection_tolerance(deflection_tolerance);
|
||||
settings.precision = precision;
|
||||
settings.set_angular_tolerance(angular_tolerance);
|
||||
settings.precision = precision;
|
||||
|
||||
if (vmap.count("force-space-transparency")) {
|
||||
settings.force_space_transparency(force_space_transparency);
|
||||
IfcGeom::update_default_style("IfcSpace").Transparency().reset(force_space_transparency);
|
||||
}
|
||||
|
||||
boost::shared_ptr<GeometrySerializer> serializer; /**< @todo use std::unique_ptr when possible */
|
||||
if (output_extension == OBJ) {
|
||||
@@ -907,6 +937,15 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (vmap.count("section-ref")) {
|
||||
static_cast<SvgSerializer*>(serializer.get())->setSectionRef(section_ref);
|
||||
}
|
||||
if (vmap.count("elevation-ref")) {
|
||||
static_cast<SvgSerializer*>(serializer.get())->setElevationRef(elevation_ref);
|
||||
}
|
||||
if (relative_center_x && relative_center_y) {
|
||||
static_cast<SvgSerializer*>(serializer.get())->setDrawingCenter(*relative_center_x, *relative_center_y);
|
||||
}
|
||||
}
|
||||
|
||||
if (convert_back_units) {
|
||||
|
||||
+30
-20
@@ -3,26 +3,50 @@
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
import ifcopenshell.util.element
|
||||
import csv
|
||||
import lark
|
||||
import argparse
|
||||
|
||||
class IfcAttributeExtractor():
|
||||
@staticmethod
|
||||
def set_element_key(element, key, value):
|
||||
def set_element_key(ifc_file, element, key, value):
|
||||
if key == 'type' and element.is_a() != value:
|
||||
return IfcAttributeExtractor.change_ifc_class(ifc_file, element, value)
|
||||
if hasattr(element, key):
|
||||
return setattr(element, key, value)
|
||||
setattr(element, key, value)
|
||||
return element
|
||||
if '.' not in key:
|
||||
return
|
||||
return element
|
||||
if key[0:3] == 'Qto':
|
||||
qto, prop = key.split('.', 1)
|
||||
qto = IfcAttributeExtractor.get_element_qto(element, qto_name)
|
||||
if qto:
|
||||
return IfcAttributeExtractor.set_qto_property(qto, prop, value)
|
||||
IfcAttributeExtractor.set_qto_property(qto, prop, value)
|
||||
return element
|
||||
pset_name, prop = key.split('.', 1)
|
||||
pset = IfcAttributeExtractor.get_element_pset(element, pset_name)
|
||||
if pset:
|
||||
return IfcAttributeExtractor.set_pset_property(pset, prop, value)
|
||||
IfcAttributeExtractor.set_pset_property(pset, prop, value)
|
||||
return element
|
||||
return element
|
||||
|
||||
@staticmethod
|
||||
def change_ifc_class(ifc_file, element, new_class):
|
||||
try:
|
||||
new_element = ifc_file.create_entity(new_class)
|
||||
except:
|
||||
return
|
||||
new_attributes = [new_element.attribute_name(i) for i, attribute in enumerate(new_element)]
|
||||
for i, attribute in enumerate(element):
|
||||
try:
|
||||
new_element[new_attributes.index(element.attribute_name(i))] = attribute
|
||||
except:
|
||||
continue
|
||||
for inverse in ifc_file.get_inverse(element):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, element, new_element)
|
||||
ifc_file.remove(element)
|
||||
return new_element
|
||||
|
||||
@staticmethod
|
||||
def get_element_qto(element, name):
|
||||
@@ -32,13 +56,6 @@ class IfcAttributeExtractor():
|
||||
and relationship.RelatingPropertyDefinition.Name == name:
|
||||
return relationship.RelatingPropertyDefinition
|
||||
|
||||
@staticmethod
|
||||
def get_qto_property(qto, name):
|
||||
for prop in qto.Quantities:
|
||||
if prop.Name != name:
|
||||
continue
|
||||
return getattr(prop, prop.is_a()[len('IfcQuantity'):] + 'Value')
|
||||
|
||||
@staticmethod
|
||||
def set_qto_property(qto, name, value):
|
||||
for prop in qto.Quantities:
|
||||
@@ -61,12 +78,6 @@ class IfcAttributeExtractor():
|
||||
and relationship.RelatingPropertyDefinition.Name == name:
|
||||
return relationship.RelatingPropertyDefinition
|
||||
|
||||
@staticmethod
|
||||
def get_pset_property(pset, name):
|
||||
for property in pset.HasProperties:
|
||||
if property.Name == name:
|
||||
return property.NominalValue.wrappedValue
|
||||
|
||||
@staticmethod
|
||||
def set_pset_property(pset, name, value):
|
||||
for property in pset.HasProperties:
|
||||
@@ -122,7 +133,6 @@ class IfcCsv():
|
||||
results = set()
|
||||
pset_qto_name = attribute.split('.', 1)[0]
|
||||
for element in self.ifc_file.by_type('IfcPropertySet') + self.ifc_file.by_type('IfcElementQuantity'):
|
||||
print(element)
|
||||
if element.Name != pset_qto_name:
|
||||
continue
|
||||
if element.is_a('IfcPropertySet'):
|
||||
@@ -146,7 +156,7 @@ class IfcCsv():
|
||||
for i, value in enumerate(row):
|
||||
if i == 0:
|
||||
continue # Skip GlobalId
|
||||
IfcAttributeExtractor.set_element_key(element, headers[i], value)
|
||||
element = IfcAttributeExtractor.set_element_key(ifc_file, element, headers[i], value)
|
||||
ifc_file.write(ifc)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
||||
This folder contains Python code to generate C++ type information based on an
|
||||
Express schema. In particular is has only been tested using recent version of
|
||||
the IFC schema and will most likely fail on any other Express schema.
|
||||
|
||||
The code can be invoked in the following way and results in several code outputs
|
||||
named according to the schema name in the Express file. A python 3 interpreter
|
||||
with the pyparsing [1] library is required.
|
||||
|
||||
$ python bootstrap.py express.bnf > express_parser.py
|
||||
$ python express_parser.py IFC2X3_TC1.exp header implementation schema_class definitions
|
||||
|
||||
[1] http://pyparsing.wikispaces.com/Download+and+Installation
|
||||
@@ -1,217 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
import string
|
||||
import operator
|
||||
import itertools
|
||||
|
||||
from pyparsing import *
|
||||
|
||||
try: from functools import reduce
|
||||
except: pass
|
||||
|
||||
class Expression:
|
||||
def __init__(self, contents):
|
||||
self.contents = contents[0]
|
||||
def __repr__(self):
|
||||
if self.op is None: return repr(self.contents)
|
||||
c = [isinstance(c,str) and c or str(c) for c in self.contents]
|
||||
if "%s" in self.op: return self.op % (" ".join(c))
|
||||
else: return "(%s)" % (" %s "%self.op).join(c)
|
||||
def __iter__(self):
|
||||
return self.contents.__iter__()
|
||||
|
||||
class Union(Expression):
|
||||
op = "|"
|
||||
|
||||
class Concat(Expression):
|
||||
op = "+"
|
||||
|
||||
class Optional(Expression):
|
||||
op = "Optional(%s)"
|
||||
|
||||
class Repeated(Expression):
|
||||
op = "ZeroOrMore(%s)"
|
||||
|
||||
class Term(Expression):
|
||||
op = None
|
||||
|
||||
class Keyword:
|
||||
def __init__(self, contents):
|
||||
self.contents = contents[0]
|
||||
def __repr__(self):
|
||||
return self.contents
|
||||
|
||||
class Terminal:
|
||||
def __init__(self, contents):
|
||||
self.contents = contents[0]
|
||||
s = self.contents
|
||||
self.is_keyword = len(s) >= 4 and s[0::len(s)-1] == '""' and \
|
||||
all(c in alphanums+"_" for c in s[1:-1])
|
||||
def __repr__(self):
|
||||
ty = "CaselessKeyword" if self.is_keyword else "CaselessLiteral"
|
||||
return "%s(%s)" % (ty, self.contents)
|
||||
|
||||
|
||||
LPAREN = Suppress("(")
|
||||
RPAREN = Suppress(")")
|
||||
LBRACK = Suppress("[")
|
||||
RBRACK = Suppress("]")
|
||||
LBRACE = Suppress("{")
|
||||
RBRACE = Suppress("}")
|
||||
EQUALS = Suppress("=")
|
||||
VBAR = Suppress("|")
|
||||
PERIOD = Suppress(".")
|
||||
HASH = Suppress("#")
|
||||
|
||||
identifier = Word(alphanums+"_")
|
||||
keyword = Word(alphanums+"_").setParseAction(Keyword)
|
||||
expression = Forward()
|
||||
optional = Group(LBRACK + expression + RBRACK).setParseAction(Optional)
|
||||
repeated = Group(LBRACE + expression + RBRACE).setParseAction(Repeated)
|
||||
terminal = quotedString.setParseAction(Terminal)
|
||||
term = (keyword | terminal | optional | repeated | (LPAREN + expression + RPAREN)).setParseAction(Term)
|
||||
concat = Group(term + OneOrMore(term)).setParseAction(Concat)
|
||||
factor = concat | term
|
||||
union = Group(factor + OneOrMore(VBAR + factor)).setParseAction(Union)
|
||||
rule = identifier + EQUALS + expression + PERIOD
|
||||
|
||||
expression << (union | factor)
|
||||
|
||||
grammar = OneOrMore(Group(rule))
|
||||
grammar.ignore(HASH + restOfLine)
|
||||
|
||||
express = grammar.parseFile(sys.argv[1])
|
||||
|
||||
def find_bytype(expr, ty, li = None):
|
||||
if li is None: li = []
|
||||
if isinstance(expr, Term):
|
||||
expr = expr.contents
|
||||
if isinstance(expr, ty):
|
||||
li.append(expr)
|
||||
return set(li)
|
||||
elif isinstance(expr, Expression):
|
||||
for term in expr:
|
||||
find_bytype(term, ty, li)
|
||||
return set(li)
|
||||
|
||||
actions = {
|
||||
'type_decl' : "TypeDeclaration",
|
||||
'entity_decl' : "EntityDeclaration",
|
||||
'enumeration_type' : "EnumerationType",
|
||||
'aggregation_types' : "AggregationType",
|
||||
'general_aggregation_types' : "AggregationType",
|
||||
'select_type' : "SelectType",
|
||||
'binary_type' : "BinaryType",
|
||||
'subtype_declaration' : "SubTypeExpression",
|
||||
'supertype_constraint' : "SuperTypeExpression",
|
||||
'derive_clause' : "AttributeList",
|
||||
'inverse_clause' : "AttributeList",
|
||||
'inverse_attr' : "InverseAttribute",
|
||||
'bound_spec' : "BoundSpecification",
|
||||
'explicit_attr' : "ExplicitAttribute",
|
||||
'width_spec' : "WidthSpec",
|
||||
'string_type' : "StringType",
|
||||
'named_types' : "NamedType",
|
||||
'simple_types' : "SimpleType",
|
||||
}
|
||||
|
||||
to_emit = set(id for id, expr in express)
|
||||
emitted = set()
|
||||
to_combine = set(["simple_id"])
|
||||
statements = []
|
||||
|
||||
terminals = reduce(lambda x,y: x | y, (find_bytype(e, Terminal) for id, e in express))
|
||||
keywords = list(filter(operator.attrgetter('is_keyword'), terminals))
|
||||
negated_keywords = map(lambda s: "~%s" % s, keywords)
|
||||
no_action = {"letter", "digit", "digits", "real_literal", "integer_literal"}
|
||||
|
||||
while True:
|
||||
emitted_in_loop = set()
|
||||
for id, expr in express:
|
||||
kws = map(repr, find_bytype(expr, Keyword))
|
||||
found = [k in emitted for k in kws]
|
||||
if id in to_emit and all(found):
|
||||
emitted_in_loop.add(id)
|
||||
emitted.add(id)
|
||||
stmt = "(%s)" % expr
|
||||
if id in to_combine:
|
||||
stmt = " + ".join(itertools.chain(negated_keywords, ("originalTextFor(Combine%s)" % stmt,)))
|
||||
if id not in no_action and not isinstance(expr.contents, Keyword) and not id in to_combine:
|
||||
node_type = "ListNode" if "ZeroOrMore" in stmt else "Node"
|
||||
action = actions.get(id, "lambda s, loc, t: %s(s, loc, t, rule=\"%s\")" % (node_type, id))
|
||||
stmt = "%s.setParseAction(%s)" % (stmt, action)
|
||||
statements.append("%s = %s(\"%s\")" % (id, stmt, id))
|
||||
to_emit -= emitted_in_loop
|
||||
if not emitted_in_loop: break
|
||||
|
||||
for id in to_emit:
|
||||
statements.append("%s = Forward()(\"%s\")" % (id, id))
|
||||
|
||||
for id in to_emit:
|
||||
expr = [e for k, e in express if k == id][0]
|
||||
stmt = "(%s)" % expr
|
||||
if id in to_combine:
|
||||
stmt = "Suppress%s" % stmt
|
||||
if id not in no_action and not isinstance(expr.contents, Keyword):
|
||||
node_type = "ListNode" if "ZeroOrMore" in stmt else "Node"
|
||||
action = ".setParseAction(%s)" % (actions[id] if id in actions else "lambda s, loc, t: %s(s, loc, t, rule=\"%s\")" % (node_type, id))
|
||||
stmt = "(%s)%s" % (stmt, action)
|
||||
statements.append("%s << %s" % (id, stmt))
|
||||
|
||||
print ("""
|
||||
# This file is generated by IfcOpenShell ifcexpressparser bootstrap.py
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pickle
|
||||
|
||||
import schema
|
||||
import mapping
|
||||
|
||||
from pyparsing import *
|
||||
from nodes import *
|
||||
|
||||
def parse(fn):
|
||||
cache_file = fn + ".cache.dat"
|
||||
if os.path.exists(cache_file):
|
||||
with open(cache_file, "rb") as f:
|
||||
m = pickle.load(f)
|
||||
else:
|
||||
%s
|
||||
|
||||
syntax.ignore("--" + restOfLine)
|
||||
syntax.ignore(Regex(r"\((?:\*(?:[^*]*\*+)+?\))"))
|
||||
ast = syntax.parseFile(fn)
|
||||
s = schema.Schema(ast)
|
||||
m = mapping.Mapping(s)
|
||||
|
||||
with open(cache_file, "wb") as f:
|
||||
pickle.dump(m, f, protocol=0)
|
||||
return m
|
||||
|
||||
if __name__ == "__main__":
|
||||
m = parse(sys.argv[1])
|
||||
import importlib
|
||||
for output in sys.argv[2:]:
|
||||
mdl = importlib.import_module(output)
|
||||
mdl.Generator(m).emit()
|
||||
sys.stdout.write(m.schema.name)
|
||||
"""%('\n '.join(statements)))
|
||||
@@ -1,35 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
class Base(object):
|
||||
"""
|
||||
A base class for all code generation classes. Currently only working around
|
||||
some python 2/3 incompatibilities in terms of unicode file handling.
|
||||
"""
|
||||
def emit(self):
|
||||
import platform
|
||||
if tuple(map(int, platform.python_version_tuple())) < (2, 8):
|
||||
from io import open as unicode_open
|
||||
unicode_type = unicode
|
||||
else:
|
||||
unicode_open = open
|
||||
unicode_type = lambda x, *args, **kwargs: x
|
||||
f = unicode_open(self.file_name, 'w', encoding='utf-8')
|
||||
f.write(unicode_type(repr(self), encoding='utf-8', errors='ignore'))
|
||||
f.close()
|
||||
@@ -1,68 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import operator
|
||||
|
||||
import nodes
|
||||
import codegen
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
class Definitions(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
|
||||
schema_name = mapping.schema.name
|
||||
self.schema_name = schema_name_title = schema_name.capitalize()
|
||||
|
||||
statements = ['']
|
||||
|
||||
def write_entity(schema_name, name, type):
|
||||
|
||||
attribute_names = list(map(lambda t: (t.name, t.optional), type.attributes))
|
||||
for attr, is_optional in attribute_names:
|
||||
statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals())
|
||||
if is_optional:
|
||||
statements.append("#define SCHEMA_%(name)s_%(attr)s_IS_OPTIONAL" % locals())
|
||||
inverse_attribute_names = list(map(operator.attrgetter('name'), type.inverse))
|
||||
for attr in inverse_attribute_names:
|
||||
statements.append("#define SCHEMA_%(name)s_HAS_%(attr)s" % locals())
|
||||
|
||||
def write(name):
|
||||
statements.append("#define SCHEMA_HAS_%(name)s" % locals())
|
||||
fn = None
|
||||
if mapping.schema.is_entity(name):
|
||||
fn = write_entity
|
||||
|
||||
if fn is not None:
|
||||
decl = mapping.schema[name]
|
||||
if isinstance(decl, nodes.TypeDeclaration):
|
||||
decl = decl.type.type
|
||||
fn(schema_name, name, decl) is not False
|
||||
|
||||
for name in mapping.schema:
|
||||
write(name)
|
||||
|
||||
self.str = "\n".join(statements) + "\n"
|
||||
|
||||
self.file_name = '%s-definitions.h' % self.schema_name
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
|
||||
Generator = Definitions
|
||||
@@ -1,77 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
# #
|
||||
# This files uses the documentation files from buildingSMART to generate #
|
||||
# descriptions from EXPRESS names that are suitable for comments in the C++ #
|
||||
# code. The .csv files used by this file are generated from the MS Office #
|
||||
# Access database, which in turn has been generated from the IFC baseline #
|
||||
# documentation by the IFCDOC utility provided by buildingSMART. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import re
|
||||
import os
|
||||
import csv
|
||||
|
||||
from schema import OrderedCaseInsensitiveDict
|
||||
|
||||
try: from html.entities import entitydefs
|
||||
except: from htmlentitydefs import entitydefs
|
||||
|
||||
make_absolute = lambda fn: os.path.join(os.path.dirname(os.path.realpath(__file__)), fn)
|
||||
|
||||
name_to_oid = OrderedCaseInsensitiveDict()
|
||||
oid_to_desc = {}
|
||||
oid_to_name = {}
|
||||
oid_to_pid = {}
|
||||
regices = list(zip([re.compile(s,re.M) for s in [r'<[\w\n=" \-/\.;_\t:%#,\?\(\)]+>',r'(\n[\t ]*){2,}',r'^[\t ]+']],['','\n\n',' ']))
|
||||
|
||||
definition_files = ['DocEntity.csv', 'DocEnumeration.csv', 'DocDefined.csv', 'DocSelect.csv']
|
||||
definition_files = map(make_absolute, definition_files)
|
||||
for fn in definition_files:
|
||||
with open(fn, encoding="utf8", errors='ignore') as f:
|
||||
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
name_to_oid[name] = oid
|
||||
oid_to_name[oid] = name
|
||||
oid_to_desc[oid] = desc
|
||||
|
||||
with open(make_absolute('DocEntityAttributes.csv')) as f:
|
||||
for pid, x, oid in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
oid_to_pid[oid] = pid
|
||||
|
||||
with open(make_absolute('DocAttribute.csv')) as f:
|
||||
for oid, name, desc in csv.reader(f, delimiter=';', quotechar='"'):
|
||||
pid = oid_to_pid[oid]
|
||||
pname = oid_to_name[pid]
|
||||
name_to_oid[".".join((pname, name))] = oid
|
||||
oid_to_desc[oid] = desc
|
||||
|
||||
def description(item):
|
||||
global name_to_oid, oid_to_desc, oid_to_name, oid_to_pid
|
||||
oid = name_to_oid.get(item,0)
|
||||
desc = oid_to_desc.get(oid, None)
|
||||
if desc:
|
||||
for a,b in entitydefs.items(): desc = desc.replace("&%s;"%a,b)
|
||||
desc = desc.replace("\r","")
|
||||
for r,s in regices: desc = r.sub(s,desc)
|
||||
desc = desc.strip()
|
||||
return desc.split("\n")
|
||||
else: return []
|
||||
@@ -1,342 +0,0 @@
|
||||
ABS = "abs" .
|
||||
ABSTRACT = "abstract" .
|
||||
ACOS = "acos" .
|
||||
AGGREGATE = "aggregate" .
|
||||
ALIAS = "alias" .
|
||||
AND = "and" .
|
||||
ANDOR = "andor" .
|
||||
ARRAY = "array" .
|
||||
AS = "as" .
|
||||
ASIN = "asin" .
|
||||
ATAN = "atan" .
|
||||
BAG = "bag" .
|
||||
BASED_ON = "based_on" .
|
||||
BEGIN = "begin" .
|
||||
BINARY = "binary" .
|
||||
BLENGTH = "blength" .
|
||||
BOOLEAN = "boolean" .
|
||||
BY = "by" .
|
||||
CASE = "case" .
|
||||
CONSTANT = "constant" .
|
||||
CONST_E = "const_e" .
|
||||
COS = "cos" .
|
||||
DERIVE = "derive" .
|
||||
DIV = "div" .
|
||||
ELSE = "else" .
|
||||
END = "end" .
|
||||
END_ALIAS = "end_alias" .
|
||||
END_CASE = "end_case" .
|
||||
END_CONSTANT = "end_constant" .
|
||||
END_ENTITY = "end_entity" .
|
||||
END_FUNCTION = "end_function" .
|
||||
END_IF = "end_if" .
|
||||
END_LOCAL = "end_local" .
|
||||
END_PROCEDURE = "end_procedure" .
|
||||
END_REPEAT = "end_repeat" .
|
||||
END_RULE = "end_rule" .
|
||||
END_SCHEMA = "end_schema" .
|
||||
END_SUBTYPE_CONSTRAINT = "end_subtype_constraint" .
|
||||
END_TYPE = "end_type" .
|
||||
ENTITY = "entity" .
|
||||
ENUMERATION = "enumeration" .
|
||||
ESCAPE = "escape" .
|
||||
EXISTS = "exists" .
|
||||
EXTENSIBLE = "extensible" .
|
||||
EXP = "exp" .
|
||||
FALSE = "false" .
|
||||
FIXED = "fixed" .
|
||||
FOR = "for" .
|
||||
FORMAT = "format" .
|
||||
FROM = "from" .
|
||||
FUNCTION = "function" .
|
||||
GENERIC = "generic" .
|
||||
GENERIC_ENTITY = "generic_entity" .
|
||||
HIBOUND = "hibound" .
|
||||
HIINDEX = "hiindex" .
|
||||
IF = "if" .
|
||||
IN = "in" .
|
||||
INSERT = "insert" .
|
||||
INTEGER = "integer" .
|
||||
INVERSE = "inverse" .
|
||||
LENGTH = "length" .
|
||||
LIKE = "like" .
|
||||
LIST = "list" .
|
||||
LOBOUND = "lobound" .
|
||||
LOCAL = "local" .
|
||||
LOG = "log" .
|
||||
LOG10 = "log10" .
|
||||
LOG2 = "log2" .
|
||||
LOGICAL = "logical" .
|
||||
LOINDEX = "loindex" .
|
||||
MOD = "mod" .
|
||||
NOT = "not" .
|
||||
NUMBER = "number" .
|
||||
NVL = "nvl" .
|
||||
ODD = "odd" .
|
||||
OF = "of" .
|
||||
ONEOF = "oneof" .
|
||||
OPTIONAL = "optional" .
|
||||
OR = "or" .
|
||||
OTHERWISE = "otherwise" .
|
||||
PI = "pi" .
|
||||
PROCEDURE = "procedure" .
|
||||
QUERY = "query" .
|
||||
REAL = "real" .
|
||||
REFERENCE = "reference" .
|
||||
REMOVE = "remove" .
|
||||
RENAMED = "renamed" .
|
||||
REPEAT = "repeat" .
|
||||
RETURN = "return" .
|
||||
ROLESOF = "rolesof" .
|
||||
RULE = "rule" .
|
||||
SCHEMA = "schema" .
|
||||
SELECT = "select" .
|
||||
SELF = "self" .
|
||||
SET = "set" .
|
||||
SIN = "sin" .
|
||||
SIZEOF = "sizeof" .
|
||||
SKIP = "skip" .
|
||||
SQRT = "sqrt" .
|
||||
STRING = "string" .
|
||||
SUBTYPE = "subtype" .
|
||||
SUBTYPE_CONSTRAINT = "subtype_constraint" .
|
||||
SUPERTYPE = "supertype" .
|
||||
TAN = "tan" .
|
||||
THEN = "then" .
|
||||
TO = "to" .
|
||||
TOTAL_OVER = "total_over" .
|
||||
TRUE = "true" .
|
||||
TYPE = "type" .
|
||||
TYPEOF = "typeof" .
|
||||
UNIQUE = "unique" .
|
||||
UNKNOWN = "unknown" .
|
||||
UNTIL = "until" .
|
||||
USE = "use" .
|
||||
USEDIN = "usedin" .
|
||||
VALUE = "value" .
|
||||
VALUE_IN = "value_in" .
|
||||
VALUE_UNIQUE = "value_unique" .
|
||||
VAR = "var" .
|
||||
WHERE = "where" .
|
||||
WHILE = "while" .
|
||||
WITH = "with" .
|
||||
XOR = "xor" .
|
||||
bit = "0" | "1" .
|
||||
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" .
|
||||
digits = digit { digit } .
|
||||
encoded_character = octet octet octet octet .
|
||||
hex_digit = digit | "a" | "b" | "c" | "d" | "e" | "f" .
|
||||
letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" .
|
||||
lparen_then_not_lparen_star = "(" { "(" } not_lparen_star { not_lparen_star } .
|
||||
not_lparen_star = not_paren_star | ")" .
|
||||
not_paren_star = letter | digit | not_paren_star_special .
|
||||
not_paren_star_quote_special = "!" | "#" | "$" | "%" | "&" | "+" | "," | "-" | "." | "/" | ":" | ";" | "<" | "=" | ">" | "?" | "@" | "[" | "\\" | "]" | "^" | "_" | "{" | "|" | "}" | "~" .
|
||||
not_paren_star_special = not_paren_star_quote_special | "\"\"" .
|
||||
not_quote = not_paren_star_quote_special | letter | digit | "(" | ")" | "*" .
|
||||
not_rparen_star = not_paren_star | "(" .
|
||||
octet = hex_digit hex_digit .
|
||||
special = not_paren_star_quote_special | "(" | ")" | "*" | "\"\"" .
|
||||
not_rparen_star_then_rparen = not_rparen_star { not_rparen_star } ")" { ")" } .
|
||||
binary_literal = "%" bit { bit } .
|
||||
encoded_string_literal = "\"" encoded_character { encoded_character } "\"" .
|
||||
integer_literal = digits .
|
||||
real_literal = ( digits "." [ digits ] [ "e" [ sign ] digits ] ) | integer_literal .
|
||||
simple_id = letter { letter | digit | "_" } .
|
||||
simple_string_literal = "'" { ( "'" "'" ) | not_quote } "'" .
|
||||
embedded_remark = "(*" [ remark_tag ] { ( not_paren_star { not_paren_star } ) | lparen_then_not_lparen_star | ( "*" { "*" } ) | not_rparen_star_then_rparen | embedded_remark } "*)" .
|
||||
remark = embedded_remark | tail_remark .
|
||||
remark_tag = "\"" remark_ref { "." remark_ref } "\"" .
|
||||
remark_ref = attribute_ref | constant_ref | entity_ref | enumeration_ref | function_ref | parameter_ref | procedure_ref | rule_label_ref | rule_ref | schema_ref | subtype_constraint_ref | type_label_ref | type_ref | variable_ref .
|
||||
tail_remark = "--" [ remark_tag ] .
|
||||
attribute_ref = attribute_id .
|
||||
constant_ref = constant_id .
|
||||
entity_ref = entity_id .
|
||||
enumeration_ref = enumeration_id .
|
||||
function_ref = function_id .
|
||||
parameter_ref = parameter_id .
|
||||
procedure_ref = procedure_id .
|
||||
rule_label_ref = rule_label_id .
|
||||
rule_ref = rule_id .
|
||||
schema_ref = schema_id .
|
||||
subtype_constraint_ref = subtype_constraint_id .
|
||||
type_label_ref = type_label_id .
|
||||
type_ref = type_id .
|
||||
variable_ref = variable_id .
|
||||
abstract_entity_declaration = ABSTRACT .
|
||||
abstract_supertype = ABSTRACT SUPERTYPE ";" .
|
||||
abstract_supertype_declaration = ABSTRACT SUPERTYPE [ subtype_constraint ] .
|
||||
actual_parameter_list = "(" [ parameter ] { "," parameter } ")" .
|
||||
add_like_op = "+" | "-" | OR | XOR .
|
||||
aggregate_initializer = "[" [ element { "," element } ] "]" .
|
||||
aggregate_source = simple_expression .
|
||||
aggregate_type = AGGREGATE [ ":" type_label ] OF parameter_type .
|
||||
aggregation_types = array_type | bag_type | list_type | set_type .
|
||||
algorithm_head = { declaration } [ constant_decl ] [ local_decl ] .
|
||||
alias_stmt = ALIAS variable_id FOR general_ref { qualifier } ";" stmt { stmt } END_ALIAS ";" .
|
||||
array_type = ARRAY bound_spec OF [ OPTIONAL ] [ UNIQUE ] instantiable_type .
|
||||
assignment_stmt = general_ref { qualifier } ":=" expression ";" .
|
||||
attribute_decl = redeclared_attribute | attribute_id .
|
||||
attribute_id = simple_id .
|
||||
attribute_qualifier = "." attribute_ref .
|
||||
bag_type = BAG [ bound_spec ] OF instantiable_type .
|
||||
binary_type = BINARY [ width_spec ] .
|
||||
boolean_type = BOOLEAN .
|
||||
bound_1 = numeric_expression .
|
||||
bound_2 = numeric_expression .
|
||||
bound_spec = "[" bound_1 ":" bound_2 "]" .
|
||||
built_in_constant = CONST_E | PI | SELF | "?" .
|
||||
built_in_function = ABS | ACOS | ASIN | ATAN | BLENGTH | COS | EXISTS | EXP | FORMAT | HIBOUND | HIINDEX | LENGTH | LOBOUND | LOINDEX | LOG | LOG2 | LOG10 | NVL | ODD | ROLESOF | SIN | SIZEOF | SQRT | TAN | TYPEOF | USEDIN | VALUE | VALUE_IN | VALUE_UNIQUE .
|
||||
built_in_procedure = INSERT | REMOVE .
|
||||
case_action = case_label { "," case_label } ":" stmt .
|
||||
case_label = expression .
|
||||
case_stmt = CASE selector OF { case_action } [ OTHERWISE ":" stmt ] END_CASE ";" .
|
||||
compound_stmt = BEGIN stmt { stmt } END ";" .
|
||||
concrete_types = aggregation_types | simple_types | type_ref .
|
||||
constant_body = constant_id ":" instantiable_type ":=" expression ";" .
|
||||
constant_decl = CONSTANT constant_body { constant_body } END_CONSTANT ";" .
|
||||
constant_factor = built_in_constant | constant_ref .
|
||||
constant_id = simple_id .
|
||||
constructed_types = enumeration_type | select_type .
|
||||
declaration = entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl .
|
||||
derived_attr = attribute_decl ":" parameter_type ":=" expression ";" .
|
||||
derive_clause = DERIVE derived_attr { derived_attr } .
|
||||
domain_rule = [ rule_label_id ":" ] expression .
|
||||
element = expression [ ":" repetition ] .
|
||||
entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] .
|
||||
entity_constructor = entity_ref "(" [ expression { "," expression } ] ")" .
|
||||
entity_decl = entity_head entity_body END_ENTITY ";" .
|
||||
entity_head = ENTITY entity_id subsuper ";" .
|
||||
entity_id = simple_id .
|
||||
enumeration_extension = BASED_ON type_ref [ WITH enumeration_items ] .
|
||||
enumeration_id = simple_id .
|
||||
enumeration_items = "(" enumeration_id { "," enumeration_id } ")" .
|
||||
enumeration_reference = [ type_ref "." ] enumeration_ref .
|
||||
enumeration_type = [ EXTENSIBLE ] ENUMERATION [ ( OF enumeration_items ) | enumeration_extension ] .
|
||||
escape_stmt = ESCAPE ";" .
|
||||
explicit_attr = attribute_decl { "," attribute_decl } ":" [ OPTIONAL ] parameter_type ";" .
|
||||
expression = simple_expression [ rel_op_extended simple_expression ] .
|
||||
factor = simple_factor [ "**" simple_factor ] .
|
||||
formal_parameter = parameter_id { "," parameter_id } ":" parameter_type .
|
||||
function_call = ( built_in_function | function_ref ) actual_parameter_list .
|
||||
function_decl = function_head algorithm_head stmt { stmt } END_FUNCTION ";" .
|
||||
function_head = FUNCTION function_id [ "(" formal_parameter { ";" formal_parameter } ")" ] ":" parameter_type ";" .
|
||||
function_id = simple_id .
|
||||
generalized_types = aggregate_type | general_aggregation_types | generic_entity_type | generic_type .
|
||||
general_aggregation_types = general_array_type | general_bag_type | general_list_type | general_set_type .
|
||||
general_array_type = ARRAY [ bound_spec ] OF [ OPTIONAL ] [ UNIQUE ] parameter_type .
|
||||
general_bag_type = BAG [ bound_spec ] OF parameter_type .
|
||||
general_list_type = LIST [ bound_spec ] OF [ UNIQUE ] parameter_type .
|
||||
general_ref = parameter_ref | variable_ref .
|
||||
general_set_type = SET [ bound_spec ] OF parameter_type .
|
||||
generic_entity_type = GENERIC_ENTITY [ ":" type_label ] .
|
||||
generic_type = GENERIC [ ":" type_label ] .
|
||||
group_qualifier = "\\" entity_ref .
|
||||
if_stmt = IF logical_expression THEN stmt { stmt } [ ELSE stmt { stmt } ] END_IF ";" .
|
||||
increment = numeric_expression .
|
||||
increment_control = variable_id ":=" bound_1 TO bound_2 [ BY increment ] .
|
||||
index = numeric_expression .
|
||||
index_1 = index .
|
||||
index_2 = index .
|
||||
index_qualifier = "[" index_1 [ ":" index_2 ] "]" .
|
||||
instantiable_type = concrete_types | entity_ref .
|
||||
integer_type = INTEGER .
|
||||
interface_specification = reference_clause | use_clause .
|
||||
interval = "{" interval_low interval_op interval_item interval_op interval_high "}" .
|
||||
interval_high = simple_expression .
|
||||
interval_item = simple_expression .
|
||||
interval_low = simple_expression .
|
||||
interval_op = "<=" | "<" .
|
||||
inverse_attr = attribute_decl ":" [ ( SET | BAG ) [ bound_spec ] OF ] entity_ref FOR [ entity_ref "." ] attribute_ref ";" .
|
||||
inverse_clause = INVERSE inverse_attr { inverse_attr } .
|
||||
list_type = LIST [ bound_spec ] OF [ UNIQUE ] instantiable_type .
|
||||
literal = binary_literal | logical_literal | real_literal | string_literal .
|
||||
local_decl = LOCAL local_variable { local_variable } END_LOCAL ";" .
|
||||
local_variable = variable_id { "," variable_id } ":" parameter_type [ ":=" expression ] ";" .
|
||||
logical_expression = expression .
|
||||
logical_literal = FALSE | TRUE | UNKNOWN .
|
||||
logical_type = LOGICAL .
|
||||
multiplication_like_op = "*" | "/" | DIV | MOD | AND | "||" .
|
||||
named_types = entity_ref | type_ref .
|
||||
named_type_or_rename = named_types [ AS ( entity_id | type_id ) ] .
|
||||
null_stmt = ";" .
|
||||
number_type = NUMBER .
|
||||
numeric_expression = simple_expression .
|
||||
one_of = ONEOF "(" supertype_expression { "," supertype_expression } ")" .
|
||||
parameter = expression .
|
||||
parameter_id = simple_id .
|
||||
parameter_type = generalized_types | simple_types | named_types .
|
||||
population = entity_ref .
|
||||
precision_spec = numeric_expression .
|
||||
primary = literal | ( qualifiable_factor { qualifier } ) .
|
||||
procedure_call_stmt = ( built_in_procedure | procedure_ref ) actual_parameter_list ";" .
|
||||
procedure_decl = procedure_head algorithm_head { stmt } END_PROCEDURE ";" .
|
||||
procedure_head = PROCEDURE procedure_id [ "(" [ VAR ] formal_parameter { ";" [ VAR ] formal_parameter } ")" ] ";" .
|
||||
procedure_id = simple_id .
|
||||
qualifiable_factor = function_call | attribute_ref | constant_factor | general_ref | population .
|
||||
qualified_attribute = SELF group_qualifier attribute_qualifier .
|
||||
qualifier = attribute_qualifier | group_qualifier | index_qualifier .
|
||||
query_expression = QUERY "(" variable_id "<*" aggregate_source "|" logical_expression ")" .
|
||||
real_type = REAL [ "(" precision_spec ")" ] .
|
||||
redeclared_attribute = qualified_attribute [ RENAMED attribute_id ] .
|
||||
referenced_attribute = attribute_ref | qualified_attribute .
|
||||
reference_clause = REFERENCE FROM schema_ref [ "(" resource_or_rename { "," resource_or_rename } ")" ] ";" .
|
||||
rel_op = "<=" | ">=" | "<>" | "=" | ":<>:" | ":=:" | "<" | ">" .
|
||||
rel_op_extended = rel_op | IN | LIKE .
|
||||
rename_id = constant_id | entity_id | function_id | procedure_id | type_id .
|
||||
repeat_control = [ increment_control ] [ while_control ] [ until_control ] .
|
||||
repeat_stmt = REPEAT repeat_control ";" stmt { stmt } END_REPEAT ";" .
|
||||
repetition = numeric_expression .
|
||||
resource_or_rename = resource_ref [ AS rename_id ] .
|
||||
resource_ref = constant_ref | entity_ref | function_ref | procedure_ref | type_ref .
|
||||
return_stmt = RETURN [ "(" expression ")" ] ";" .
|
||||
rule_decl = rule_head algorithm_head { stmt } where_clause END_RULE ";" .
|
||||
rule_head = RULE rule_id FOR "(" entity_ref { "," entity_ref } ")" ";" .
|
||||
rule_id = simple_id .
|
||||
rule_label_id = simple_id .
|
||||
schema_body = { interface_specification } [ constant_decl ] { declaration | rule_decl } .
|
||||
schema_decl = SCHEMA schema_id [ schema_version_id ] ";" schema_body END_SCHEMA ";" .
|
||||
schema_id = simple_id .
|
||||
schema_version_id = string_literal .
|
||||
selector = expression .
|
||||
select_extension = BASED_ON type_ref [ WITH select_list ] .
|
||||
select_list = "(" named_types { "," named_types } ")" .
|
||||
select_type = [ EXTENSIBLE [ GENERIC_ENTITY ] ] SELECT [ select_list | select_extension ] .
|
||||
set_type = SET [ bound_spec ] OF instantiable_type .
|
||||
sign = "+" | "-" .
|
||||
simple_expression = term { add_like_op term } .
|
||||
simple_factor = aggregate_initializer | interval | query_expression | ( [ unary_op ] ( "(" expression ")" | primary ) ) | entity_constructor | enumeration_reference .
|
||||
simple_types = binary_type | boolean_type | integer_type | logical_type | number_type | real_type | string_type .
|
||||
skip_stmt = SKIP ";" .
|
||||
stmt = alias_stmt | assignment_stmt | case_stmt | compound_stmt | escape_stmt | if_stmt | null_stmt | procedure_call_stmt | repeat_stmt | return_stmt | skip_stmt .
|
||||
string_literal = simple_string_literal | encoded_string_literal .
|
||||
string_type = STRING [ width_spec ] .
|
||||
subsuper = [ supertype_constraint ] [ subtype_declaration ] .
|
||||
subtype_constraint = OF "(" supertype_expression ")" .
|
||||
subtype_constraint_body = [ abstract_supertype ] [ total_over ] [ supertype_expression ";" ] .
|
||||
subtype_constraint_decl = subtype_constraint_head subtype_constraint_body END_SUBTYPE_CONSTRAINT ";" .
|
||||
subtype_constraint_head = SUBTYPE_CONSTRAINT subtype_constraint_id FOR entity_ref ";" .
|
||||
subtype_constraint_id = simple_id .
|
||||
subtype_declaration = SUBTYPE OF "(" entity_ref { "," entity_ref } ")" .
|
||||
supertype_constraint = abstract_supertype_declaration | abstract_entity_declaration | supertype_rule .
|
||||
supertype_expression = supertype_factor { ANDOR supertype_factor } .
|
||||
supertype_factor = supertype_term { AND supertype_term } .
|
||||
supertype_rule = SUPERTYPE subtype_constraint .
|
||||
supertype_term = one_of | "(" supertype_expression ")" | entity_ref .
|
||||
syntax = schema_decl { schema_decl } .
|
||||
term = factor { multiplication_like_op factor } .
|
||||
total_over = TOTAL_OVER "(" entity_ref { "," entity_ref } ")" ";" .
|
||||
type_decl = TYPE type_id "=" underlying_type ";" [ where_clause ] END_TYPE ";" .
|
||||
type_id = simple_id .
|
||||
type_label = type_label_id | type_label_ref .
|
||||
type_label_id = simple_id .
|
||||
unary_op = "+" | "-" | NOT .
|
||||
underlying_type = constructed_types | concrete_types .
|
||||
unique_clause = UNIQUE unique_rule ";" { unique_rule ";" } .
|
||||
unique_rule = [ rule_label_id ":" ] referenced_attribute { "," referenced_attribute } .
|
||||
until_control = UNTIL logical_expression .
|
||||
use_clause = USE FROM schema_ref [ "(" named_type_or_rename { "," named_type_or_rename } ")" ] ";" .
|
||||
variable_id = simple_id .
|
||||
where_clause = WHERE domain_rule ";" { domain_rule ";" } .
|
||||
while_control = WHILE logical_expression .
|
||||
width = numeric_expression .
|
||||
width_spec = "(" width ")" [ FIXED ] .
|
||||
@@ -1,149 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import operator
|
||||
|
||||
import codegen
|
||||
import templates
|
||||
import documentation
|
||||
|
||||
class Header(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
declarations = []
|
||||
|
||||
write = lambda str, **kwargs: declarations.append(str%dict({
|
||||
'documentation': templates.multi_line_comment(documentation.description(kwargs['name']))}, **kwargs))
|
||||
|
||||
forward_names = list(mapping.schema.entities.keys()) + list(mapping.schema.simpletypes.keys())
|
||||
forward_definitions = "".join(["class %s; "%n for n in forward_names])
|
||||
|
||||
for name, type in mapping.schema.selects.items():
|
||||
write(templates.select, name=name)
|
||||
|
||||
for name, type in mapping.schema.enumerations.items():
|
||||
short_name = name[:-4] if name.endswith("Enum") else name
|
||||
write(templates.enumeration, name=name, values=", ".join(["%s_%s"%(short_name, v) for v in type.values]))
|
||||
|
||||
emitted_simpletypes = set()
|
||||
while len(emitted_simpletypes) < len(mapping.schema.simpletypes):
|
||||
for name, type in mapping.schema.simpletypes.items():
|
||||
if name.lower() in emitted_simpletypes: continue
|
||||
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
|
||||
attr_type = mapping.make_argument_type(type)
|
||||
superclass = mapping.simple_type_parent(name)
|
||||
if superclass is None:
|
||||
superclass = "IfcUtil::IfcBaseType"
|
||||
elif superclass.lower() not in emitted_simpletypes:
|
||||
continue
|
||||
else:
|
||||
# Case normalize
|
||||
superclass = [k for k in mapping.schema.simpletypes.keys() if k.lower() == superclass.lower()][0]
|
||||
emitted_simpletypes.add(name.lower())
|
||||
write(templates.simpletype, name=name, type=type_str, attr_type=attr_type, superclass=superclass)
|
||||
|
||||
class_definitions = []
|
||||
|
||||
write = lambda str, **kwargs: class_definitions.append(str%dict({
|
||||
'documentation': templates.multi_line_comment(documentation.description(kwargs['name']))}, **kwargs))
|
||||
|
||||
emitted_entities = set()
|
||||
while len(emitted_entities) < len(mapping.schema.entities):
|
||||
for name, type in mapping.schema.entities.items():
|
||||
if name.lower() in emitted_entities: continue
|
||||
if len(type.supertypes) == 0 or set(map(str.lower, type.supertypes)) <= emitted_entities:
|
||||
attr_lines = []
|
||||
def write_method(attr):
|
||||
if attr.optional:
|
||||
attr_lines.append(templates.optional_attribute_description % (attr.name, name))
|
||||
attr_lines.append("bool has%s() const;"%(attr.name))
|
||||
attr_lines.extend(["/// %s"%d for d in documentation.description(".".join((name, attr.name)))])
|
||||
type_str = mapping.get_parameter_type(attr, allow_optional=False, allow_entities=False)
|
||||
if mapping.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN":
|
||||
attr_lines.append("%s %s() const;"%(type_str, attr.name))
|
||||
attr_lines.append("void set%s(%s v);"%(attr.name, type_str))
|
||||
|
||||
[write_method(attr) for attr in type.attributes]
|
||||
|
||||
inv_lines = []
|
||||
def write_inverse(attr):
|
||||
inv_lines.append(templates.inverse_attr%{'name':attr.name, 'entity':attr.entity, 'attribute':attr.attribute})
|
||||
|
||||
if type.inverse:
|
||||
[write_inverse(attr) for attr in type.inverse]
|
||||
|
||||
attributes = "\n".join(["%s%s"%(' '*4, a) for a in attr_lines])
|
||||
if len(attributes): attributes += '\n'
|
||||
|
||||
inverse = "\n".join(["%s%s"%(' '*4, a) for a in inv_lines])
|
||||
if len(inverse): inverse += '\n'
|
||||
|
||||
def case_norm(n):
|
||||
n = n.lower()
|
||||
return [k for k in mapping.schema.entities.keys() if k.lower() == n][0]
|
||||
|
||||
supertypes = map(case_norm, type.supertypes) if len(type.supertypes) else ['IfcUtil::IfcBaseEntity']
|
||||
superclass = ": %s "%(", ".join(["public %s"%c for c in supertypes]))
|
||||
|
||||
argument_count = mapping.argument_count(type)
|
||||
|
||||
argument_start = argument_count - len(type.attributes)
|
||||
|
||||
argument_name_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return "%s"; '%(i+argument_start, attr.name) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else ""
|
||||
argument_name_function_body_tail = (" return %s::getArgumentName(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
|
||||
|
||||
argument_name_function_body = argument_name_function_body_switch_stmt + argument_name_function_body_tail
|
||||
|
||||
derived = mapping.derived_in_supertype(type)
|
||||
attribute_names = list(map(operator.attrgetter('name'), mapping.arguments(type)))
|
||||
derived_in_supertype = set(derived) & set(attribute_names)
|
||||
derived_in_supertype_indices = sorted(attribute_names.index(nm) for nm in derived_in_supertype)
|
||||
attribute_type_cases = ['case %d: return IfcUtil::Argument_DERIVED; ' % idx for idx in derived_in_supertype_indices]
|
||||
attribute_type_cases += ['case %d: return %s; '%(i+argument_start, mapping.make_argument_type(attr)) for i, attr in enumerate(type.attributes)]
|
||||
argument_type_function_body_switch_stmt = " switch (i) {%s}"%("".join(attribute_type_cases)) if len(type.attributes) else ""
|
||||
argument_type_function_body_tail = (" return %s::getArgumentType(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
|
||||
|
||||
argument_type_function_body = argument_type_function_body_switch_stmt + argument_type_function_body_tail
|
||||
|
||||
argument_entity_function_body_switch_stmt = " switch (i) {%s}"%("".join(['case %d: return %s; '%(i+argument_start, mapping.make_argument_entity(attr)) for i, attr in enumerate(type.attributes)])) if len(type.attributes) else ""
|
||||
argument_entity_function_body_tail = (" return %s::getArgumentEntity(i); "%type.supertypes[0]) if len(type.supertypes) == 1 else ' (void)i; throw IfcParse::IfcAttributeOutOfRangeException("Argument index out of range"); '
|
||||
|
||||
argument_entity_function_body = argument_entity_function_body_switch_stmt + argument_entity_function_body_tail
|
||||
|
||||
constructor_arguments = ", ".join("%(full_type)s v%(index)d_%(name)s"%a for a in mapping.get_assignable_arguments(type))
|
||||
|
||||
write(templates.entity, **locals())
|
||||
emitted_entities.add(name)
|
||||
|
||||
self.str = templates.header % {
|
||||
'schema_name_upper' : mapping.schema.name.upper(),
|
||||
'schema_name' : mapping.schema.name.capitalize(),
|
||||
'declarations' : ''.join(declarations),
|
||||
'forward_definitions' : forward_definitions,
|
||||
'class_definitions' : ''.join(class_definitions)
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%s.h'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
|
||||
Generator = Header
|
||||
@@ -1,262 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import codegen
|
||||
import templates
|
||||
|
||||
from schema import OrderedCaseInsensitiveDict
|
||||
|
||||
class Implementation(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
enumeration_functions = []
|
||||
entity_implementations = []
|
||||
schema_entity_statements = []
|
||||
|
||||
schema_name = mapping.schema.name.capitalize()
|
||||
schema_name_upper = mapping.schema.name.upper()
|
||||
|
||||
stringify = lambda s: '"%s"'%s
|
||||
cat = lambda vs: "".join(vs)
|
||||
catc = lambda vs: ", ".join(vs)
|
||||
catnl = lambda vs: "\n".join(vs)
|
||||
cator = lambda vs: " || ".join(vs)
|
||||
nl = lambda s: "%s\n"%s if len(s) else s
|
||||
|
||||
write = lambda str, **kwargs: enumeration_functions.append(str%kwargs)
|
||||
|
||||
for name, enum in mapping.schema.enumerations.items():
|
||||
short_name = name[:-4] if name.endswith("Enum") else name
|
||||
context = locals()
|
||||
write(
|
||||
templates.enumeration_function,
|
||||
max_id = len(enum.values),
|
||||
name = name,
|
||||
schema_name = schema_name,
|
||||
schema_name_upper = schema_name_upper,
|
||||
values = catc(map(stringify, enum.values)),
|
||||
from_string_statements = catnl(templates.enum_from_string_stmt%dict(context,**locals()) for value in enum.values)
|
||||
)
|
||||
|
||||
write = lambda str, **kwargs: entity_implementations.append(str%kwargs)
|
||||
|
||||
for name, type in mapping.schema.entities.items():
|
||||
parent_type_test = "" if not type.supertypes or len(type.supertypes) != 1 \
|
||||
else templates.parent_type_test%(type.supertypes[0])
|
||||
|
||||
constructor_arguments = mapping.get_assignable_arguments(type, include_derived = True)
|
||||
constructor_arguments_str = catc("%(full_type)s v%(index)d_%(name)s"%a for a in constructor_arguments if not a['is_derived'])
|
||||
attributes = []
|
||||
constructor_implementations = []
|
||||
write_attr = lambda str, **kwargs: attributes.append(str%kwargs)
|
||||
for arg in constructor_arguments:
|
||||
if not arg['is_inherited'] and not arg['is_derived']:
|
||||
if arg['is_optional']:
|
||||
write_attr(
|
||||
templates.const_function,
|
||||
class_name = name,
|
||||
schema_name = schema_name,
|
||||
schema_name_upper = schema_name_upper,
|
||||
name = 'has%s'%arg['name'],
|
||||
arguments = '',
|
||||
return_type = 'bool',
|
||||
body = templates.optional_attr_stmt % {'index':arg['index']-1}
|
||||
)
|
||||
|
||||
def find_template(arg):
|
||||
simple = mapping.schema.is_simpletype(arg['list_instance_type'])
|
||||
select = arg['list_instance_type'] == "IfcUtil::IfcBaseClass"
|
||||
express = mapping.flatten_type_string(arg['list_instance_type']) in mapping.express_to_cpp_typemapping
|
||||
if arg['is_enum']: return templates.get_attr_stmt_enum
|
||||
elif arg['is_nested'] and arg['is_templated_list']: return templates.get_attr_stmt_nested_array
|
||||
elif arg['is_templated_list'] and not (select or simple or express): return templates.get_attr_stmt_array
|
||||
elif arg['non_optional_type'].endswith('*'): return templates.get_attr_stmt_entity
|
||||
else: return templates.get_attr_stmt
|
||||
|
||||
tmpl = find_template(arg)
|
||||
write_attr(
|
||||
templates.const_function,
|
||||
class_name = name,
|
||||
name = arg['name'],
|
||||
arguments = '',
|
||||
schema_name = schema_name,
|
||||
schema_name_upper = schema_name_upper,
|
||||
return_type = arg['non_optional_type'],
|
||||
body = tmpl % {'index': arg['index']-1,
|
||||
'type' : arg['non_optional_type'].replace('::Value', ''),
|
||||
'list_instance_type' : arg['list_instance_type']}
|
||||
)
|
||||
|
||||
def find_template(arg):
|
||||
simple = mapping.schema.is_simpletype(arg['list_instance_type'])
|
||||
select = arg['list_instance_type'] == "IfcUtil::IfcBaseClass"
|
||||
express = arg['list_instance_type'] in mapping.express_to_cpp_typemapping
|
||||
if arg['is_enum']: return templates.set_attr_stmt_enum
|
||||
elif arg['is_templated_list'] and not (select or simple or express): return templates.set_attr_stmt_array
|
||||
else: return templates.set_attr_stmt
|
||||
|
||||
tmpl = find_template(arg)
|
||||
write_attr(
|
||||
templates.function,
|
||||
class_name = name,
|
||||
name = 'set%s'%arg['name'],
|
||||
arguments = '%s v'%arg['non_optional_type'],
|
||||
return_type = 'void',
|
||||
schema_name = schema_name,
|
||||
schema_name_upper = schema_name_upper,
|
||||
body = tmpl % {'index': arg['index']-1,
|
||||
'type' : arg['non_optional_type'].replace('::Value', '')}
|
||||
)
|
||||
|
||||
if arg['is_derived']:
|
||||
constructor_implementations.append(templates.constructor_stmt_derived % {'index' : arg['index']-1})
|
||||
else:
|
||||
is_optional_non_naked_ptr = arg['is_optional'] and not arg['non_optional_type'].endswith('*')
|
||||
arg_name = "v%(index)d_%(name)s"%arg
|
||||
deref_name = ("*%s"%arg_name) if is_optional_non_naked_ptr else arg_name
|
||||
|
||||
tmpl = templates.constructor_stmt_array if arg['is_templated_list'] \
|
||||
else templates.constructor_stmt_enum if arg['is_enum'] \
|
||||
else templates.constructor_stmt
|
||||
impl = tmpl % {'name' : deref_name,
|
||||
'index' : arg['index']-1,
|
||||
'type' : arg['non_optional_type'].replace('::Value', '')}
|
||||
if is_optional_non_naked_ptr:
|
||||
impl = templates.constructor_stmt_optional%{'name' : arg_name,
|
||||
'index' : arg['index']-1,
|
||||
'stmt' : impl}
|
||||
constructor_implementations.append(impl)
|
||||
|
||||
def get_attribute_index(entity, attr_name):
|
||||
related_entity = mapping.schema.entities[entity]
|
||||
return [a['name'].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)].index(attr_name.lower())
|
||||
|
||||
inverse = [templates.const_function % {
|
||||
'class_name' : name,
|
||||
'schema_name' : schema_name,
|
||||
'schema_name_upper' : schema_name_upper,
|
||||
'name' : i.name,
|
||||
'arguments' : '',
|
||||
'return_type' : '::%s::%s::list::ptr' % (schema_name, i.entity),
|
||||
'body' : templates.get_inverse % {'type': i.entity, 'index':get_attribute_index(i.entity, i.attribute), 'schema_name' : schema_name, 'schema_name_upper': schema_name_upper}
|
||||
} for i in type.inverse]
|
||||
|
||||
superclass = "%s((IfcEntityInstanceData*)0)" % type.supertypes[0] if len(type.supertypes) == 1 else 'IfcUtil::IfcBaseEntity()'
|
||||
|
||||
write(
|
||||
templates.entity_implementation,
|
||||
name = name,
|
||||
parent_type_test = parent_type_test,
|
||||
constructor_arguments = constructor_arguments_str,
|
||||
constructor_implementation = cat(constructor_implementations),
|
||||
attributes = nl(catnl(attributes)),
|
||||
inverse = nl(catnl(inverse)),
|
||||
superclass = superclass,
|
||||
schema_name = schema_name,
|
||||
schema_name_upper = schema_name_upper
|
||||
)
|
||||
|
||||
selectable_simple_types = sorted(set(sum([b.values for a,b in mapping.schema.selects.items()], [])) & set(map(str, mapping.schema.types.keys())))
|
||||
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.simpletypes.items()]
|
||||
schema_entity_statements += [templates.schema_entity_stmt%locals() for name, type in mapping.schema.entities.items()]
|
||||
|
||||
enumerable_types = sorted(set([name for name, type in mapping.schema.types.items()] + [name for name, type in mapping.schema.entities.items()]))
|
||||
max_len = max(map(len, enumerable_types))
|
||||
type_name_strings = catc(map(stringify, enumerable_types))
|
||||
string_map_statements = [templates.string_map_statement % {
|
||||
'uppercase_name' : name.upper(),
|
||||
'name' : name,
|
||||
'padding' : ' ' * (max_len - len(name))
|
||||
} for name in enumerable_types]
|
||||
|
||||
enumeration_index_by_str = OrderedCaseInsensitiveDict((j,i) for i,j in enumerate(enumerable_types))
|
||||
def get_parent_id(s):
|
||||
e = mapping.schema.entities.get(s)
|
||||
if e and e.supertypes:
|
||||
return enumeration_index_by_str[e.supertypes[0]]
|
||||
else: return -1
|
||||
|
||||
parent_type_statements = ",".join(map(str, map(get_parent_id, enumerable_types)))
|
||||
|
||||
max_id = len(enumerable_types)
|
||||
|
||||
simple_type_statements = cator("v == Type::%s"%name for name in selectable_simple_types)
|
||||
|
||||
simple_type_impl = []
|
||||
for class_name, type in mapping.schema.simpletypes.items():
|
||||
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
|
||||
attr_type = mapping.make_argument_type(type)
|
||||
superclass = mapping.simple_type_parent(class_name)
|
||||
|
||||
simpletype_impl_is = templates.simpletype_impl_is_with_supertype if superclass \
|
||||
else templates.simpletype_impl_is_without_supertype
|
||||
|
||||
constructor = templates.constructor_single_initlist if superclass \
|
||||
else templates.constructor
|
||||
|
||||
simpletype_impl_cast = templates.simpletype_impl_cast_templated if mapping.is_templated_list(type) \
|
||||
else templates.simpletype_impl_cast
|
||||
|
||||
simpletype_impl_constructor = templates.simpletype_impl_constructor_templated if mapping.is_templated_list(type) \
|
||||
else templates.simpletype_impl_constructor
|
||||
|
||||
def compose(params, schema_name=schema_name, schema_name_upper=schema_name_upper):
|
||||
class_name, attr_type, superclass, superclass_init, name, tmpl, return_type, args, body = params
|
||||
underlying_type = mapping.list_instance_type(type)
|
||||
arguments = ",".join(args)
|
||||
body = body % locals()
|
||||
return tmpl % locals()
|
||||
|
||||
simple_type_impl.append(templates.simpletype_impl_comment % {'name': class_name})
|
||||
simple_type_impl.extend(map(compose, map(lambda x: (class_name, attr_type, superclass, "(IfcEntityInstanceData*)0")+x, (
|
||||
('Class', templates.function, 'const IfcParse::type_declaration&', (), templates.simpletype_impl_class ),
|
||||
('declaration', templates.const_function, 'const IfcParse::type_declaration&', (), templates.simpletype_impl_declaration ),
|
||||
('', constructor, '', ('IfcEntityInstanceData* e',), templates.simpletype_impl_explicit_constructor),
|
||||
('', constructor, '', ("%s v" % type_str,), simpletype_impl_constructor ),
|
||||
('', templates.cast_function, type_str, (), simpletype_impl_cast )
|
||||
))))
|
||||
simple_type_impl.append('')
|
||||
|
||||
external_definitions = [("extern entity* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.entities.keys() ] + \
|
||||
[("extern type_declaration* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.simpletypes.keys()]
|
||||
|
||||
self.str = templates.implementation % {
|
||||
'schema_name_upper' : schema_name_upper,
|
||||
'schema_name' : schema_name,
|
||||
'max_id' : max_id,
|
||||
'enumeration_functions' : cat(enumeration_functions),
|
||||
'schema_entity_statements' : catnl(schema_entity_statements),
|
||||
'type_name_strings' : type_name_strings,
|
||||
'string_map_statements' : catnl(string_map_statements),
|
||||
'simple_type_statement' : simple_type_statements,
|
||||
'parent_type_statements' : parent_type_statements,
|
||||
'entity_implementations' : catnl(entity_implementations),
|
||||
'simple_type_impl' : catnl(simple_type_impl),
|
||||
'external_definitions' : catnl(external_definitions)
|
||||
}
|
||||
|
||||
self.schema_name = mapping.schema.name.capitalize()
|
||||
|
||||
self.file_name = '%s.cpp'%self.schema_name
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
|
||||
|
||||
Generator = Implementation
|
||||
@@ -1,245 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import nodes
|
||||
import templates
|
||||
|
||||
class Mapping:
|
||||
|
||||
express_to_cpp_typemapping = {
|
||||
'boolean' : 'bool',
|
||||
'logical' : 'bool',
|
||||
'integer' : 'int',
|
||||
'real' : 'double',
|
||||
'number' : 'double',
|
||||
'string' : 'std::string',
|
||||
'binary' : 'boost::dynamic_bitset<>'
|
||||
}
|
||||
|
||||
supported_argument_types = set([
|
||||
'INT', 'BOOL', 'DOUBLE', 'STRING', 'BINARY', 'ENUMERATION', 'ENTITY_INSTANCE',
|
||||
'AGGREGATE_OF_INT', 'AGGREGATE_OF_DOUBLE', 'AGGREGATE_OF_STRING', 'AGGREGATE_OF_BINARY', 'AGGREGATE_OF_ENTITY_INSTANCE',
|
||||
'AGGREGATE_OF_AGGREGATE_OF_INT', 'AGGREGATE_OF_AGGREGATE_OF_DOUBLE', 'AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE',
|
||||
])
|
||||
|
||||
def __init__(self, schema):
|
||||
self.schema = schema
|
||||
|
||||
def flatten_type_string(self, type):
|
||||
return self.flatten_type_string(self.schema.types[type].type) if self.schema.is_simpletype(type) else type
|
||||
|
||||
def flatten_type(self, type):
|
||||
res = self.flatten_type(self.schema.types[type].type) if self.schema.is_simpletype(type) else type
|
||||
return res
|
||||
|
||||
def simple_type_parent(self, type):
|
||||
parent = self.schema.types[type].type
|
||||
if isinstance(parent, (nodes.AggregationType, nodes.StringType)) or (isinstance(parent, nodes.SimpleType) and isinstance(parent.type, nodes.StringType)):
|
||||
return None
|
||||
if str(parent) in self.express_to_cpp_typemapping:
|
||||
return None
|
||||
return str(parent)
|
||||
|
||||
def make_type_string(self, type):
|
||||
if isinstance(type, nodes.StringType) or (isinstance(type, nodes.SimpleType) and isinstance(type.type, nodes.StringType)):
|
||||
type = "string"
|
||||
if isinstance(type, (str, nodes.BinaryType, nodes.SimpleType, nodes.NamedType)):
|
||||
return self.express_to_cpp_typemapping.get(str(type), "::%s::%s" % (self.schema.name.capitalize(), type))
|
||||
else:
|
||||
if type.bounds is None:
|
||||
import pdb; pdb.set_trace()
|
||||
is_list = self.schema.is_entity(type.type)
|
||||
is_nested_list = isinstance(type.type, nodes.AggregationType)
|
||||
tmpl = templates.list_list_type if is_nested_list else templates.list_type if is_list else templates.array_type
|
||||
return tmpl % {
|
||||
'instance_type' : self.make_type_string(self.flatten_type_string(type.type)),
|
||||
'lower' : type.bounds.lower,
|
||||
'upper' : type.bounds.upper,
|
||||
}
|
||||
|
||||
def is_array(self, type):
|
||||
if isinstance(type, nodes.AggregationType):
|
||||
return True
|
||||
elif isinstance(type, str) and self.schema.is_type(type):
|
||||
return self.is_array(self.schema.types[type].type)
|
||||
else:
|
||||
return False
|
||||
|
||||
def make_argument_entity(self, attr):
|
||||
type = attr.type if hasattr(attr, 'type') else attr
|
||||
while isinstance(type, nodes.AggregationType): type = type.type
|
||||
if str(type) in self.express_to_cpp_typemapping: return "Type::UNDEFINED"
|
||||
else: return "Type::%s" % type
|
||||
|
||||
def make_argument_type(self, attr):
|
||||
def _make_argument_type(type):
|
||||
if isinstance(type, nodes.SimpleType):
|
||||
type = type.type
|
||||
if self.schema.is_entity(type) or isinstance(type, nodes.SelectType):
|
||||
return "ENTITY_INSTANCE"
|
||||
elif isinstance(type, nodes.BinaryType):
|
||||
return "BINARY"
|
||||
elif isinstance(type, nodes.StringType):
|
||||
return "STRING"
|
||||
elif isinstance(type, nodes.EnumerationType):
|
||||
return "ENUMERATION"
|
||||
elif isinstance(type, nodes.AggregationType):
|
||||
ty = _make_argument_type(type.type)
|
||||
if ty == "UNKNOWN": return "UNKNOWN"
|
||||
return "AGGREGATE_OF_" + ty
|
||||
elif str(type) in self.express_to_cpp_typemapping:
|
||||
return self.express_to_cpp_typemapping.get(str(type), type).split('::')[-1].upper()
|
||||
elif self.schema.is_type(type):
|
||||
return _make_argument_type(self.schema.types[type].type)
|
||||
else:
|
||||
raise ValueError("Unable to map type %r for attribute %r" % (type, attr))
|
||||
ty = _make_argument_type(attr.type if hasattr(attr, 'type') else attr)
|
||||
if ty not in self.supported_argument_types:
|
||||
print("Attribute %r mapped as 'unknown'" % (attr), file=sys.stderr)
|
||||
ty = 'UNKNOWN'
|
||||
return "IfcUtil::Argument_%s" % ty
|
||||
|
||||
def get_type_dep(self, type):
|
||||
if isinstance(type, str):
|
||||
return self.express_to_cpp_typemapping.get(str(type), type)
|
||||
else:
|
||||
return self.get_type_dep(type.type)
|
||||
|
||||
def get_parameter_type(self, attr, allow_optional, allow_entities, allow_pointer = True):
|
||||
attr_type = self.flatten_type(attr.type)
|
||||
|
||||
if (isinstance(attr_type, nodes.SimpleType) and isinstance(attr_type.type, nodes.StringType)) or isinstance(attr_type, nodes.StringType):
|
||||
type_str = self.express_to_cpp_typemapping["string"]
|
||||
else:
|
||||
type_str = self.express_to_cpp_typemapping.get(str(attr_type), attr_type)
|
||||
|
||||
is_ptr = False
|
||||
|
||||
if self.schema.is_enumeration(attr_type):
|
||||
type_str = '::%s::%s::Value' % (self.schema.name.capitalize(), attr_type)
|
||||
elif isinstance(type_str, nodes.AggregationType):
|
||||
is_nested_list = isinstance(attr_type.type, nodes.AggregationType)
|
||||
ty = self.get_parameter_type(attr_type.type if is_nested_list else attr_type, False, allow_entities, False)
|
||||
if self.schema.is_select(attr_type.type):
|
||||
type_str = templates.untyped_list
|
||||
elif self.schema.is_simpletype(ty) or str(ty) in self.express_to_cpp_typemapping.values():
|
||||
tmpl = templates.nested_array_type if is_nested_list else templates.array_type
|
||||
bounds = (attr_type.bounds.lower, attr_type.bounds.upper) if attr_type.bounds else (-1, -1)
|
||||
type_str = tmpl % {
|
||||
'instance_type' : ty,
|
||||
'lower' : bounds[0],
|
||||
'upper' : bounds[1]
|
||||
}
|
||||
else:
|
||||
tmpl = templates.list_list_type if is_nested_list else templates.list_type
|
||||
type_str = tmpl % {
|
||||
'instance_type': ty
|
||||
}
|
||||
elif (self.schema.is_entity(type_str) or self.schema.is_select(type_str)):
|
||||
type_str = '::%s::%s' % (self.schema.name.capitalize(), attr_type)
|
||||
if allow_pointer:
|
||||
type_str += "*"
|
||||
is_ptr = True
|
||||
elif not allow_pointer and self.schema.is_select(type_str):
|
||||
type_str = "IfcUtil::IfcBaseClass*"
|
||||
is_ptr = True
|
||||
if allow_optional and attr.optional and not is_ptr:
|
||||
type_str = "boost::optional< %s >"%type_str
|
||||
return type_str
|
||||
|
||||
def argument_count(self, t):
|
||||
c = sum([self.argument_count(self.schema.entities[s]) for s in t.supertypes])
|
||||
return c + len(t.attributes)
|
||||
|
||||
def arguments(self, t):
|
||||
c = sum([self.arguments(self.schema.entities[s]) for s in t.supertypes], [])
|
||||
return c + t.attributes
|
||||
|
||||
def derived_in_supertype(self, t):
|
||||
c = sum([self.derived_in_supertype(self.schema.entities[s]) for s in t.supertypes], [])
|
||||
derived = c + t.derive
|
||||
return [d[0][1] for d in derived if isinstance(d[0], tuple)]
|
||||
|
||||
def list_instance_type(self, attr):
|
||||
attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr
|
||||
if isinstance(attr_type, str): return None
|
||||
def f(v):
|
||||
v = self.flatten_type(v)
|
||||
if isinstance(v, (nodes.AggregationType, nodes.StringType)) or (isinstance(v, nodes.SimpleType) and isinstance(v.type, nodes.StringType)):
|
||||
return "string"
|
||||
if self.schema.is_select(v):
|
||||
return 'IfcUtil::IfcBaseClass'
|
||||
elif str(v) in self.schema.types or str(v) in self.schema.entities:
|
||||
return "::%s::%s" % (self.schema.name.capitalize(), v)
|
||||
else: return str(v)
|
||||
if self.is_array(attr_type):
|
||||
if not isinstance(attr_type, str) and self.is_array(attr_type.type):
|
||||
if isinstance(attr_type.type, str):
|
||||
return f(attr_type.type)
|
||||
else: return f(attr_type.type.type)
|
||||
else:
|
||||
if isinstance(attr_type, str):
|
||||
return f(attr_type)
|
||||
else: return f(attr_type.type)
|
||||
return None
|
||||
|
||||
def is_templated_list(self, attr):
|
||||
attr_type = attr.type if isinstance(attr, nodes.ExplicitAttribute) else attr
|
||||
if isinstance(attr, str): return False
|
||||
ty = self.list_instance_type(attr)
|
||||
if ty is None: return False
|
||||
arr = self.is_array(attr_type)
|
||||
simple = self.schema.is_simpletype(ty)
|
||||
express = self.flatten_type_string(ty) in self.express_to_cpp_typemapping
|
||||
select = ty == 'IfcUtil::IfcBaseClass'
|
||||
return arr and not simple and not express and not select
|
||||
|
||||
def get_assignable_arguments(self, t, include_derived = False):
|
||||
count = self.argument_count(t)
|
||||
num_inherited = count - len(t.attributes)
|
||||
derived = set(self.derived_in_supertype(t))
|
||||
attrs = enumerate(self.arguments(t))
|
||||
|
||||
def include(attr):
|
||||
not_derived = include_derived or (attr.name not in derived)
|
||||
supported = self.make_argument_type(attr) != "IfcUtil::Argument_UNKNOWN"
|
||||
return not_derived and supported
|
||||
|
||||
return [{
|
||||
'index' : i+1,
|
||||
'name' : attr.name,
|
||||
'full_type' : self.get_parameter_type(attr, allow_optional=True, allow_entities=True),
|
||||
'specialized_type' : self.get_parameter_type(attr, allow_optional=True, allow_entities=False),
|
||||
'non_optional_type' : self.get_parameter_type(attr, allow_optional=False, allow_entities=False),
|
||||
'list_instance_type' : self.list_instance_type(attr),
|
||||
'is_optional' : attr.optional,
|
||||
'is_inherited' : i < num_inherited,
|
||||
'is_enum' : attr.type in self.schema.enumerations,
|
||||
'is_array' : self.is_array(attr.type),
|
||||
'is_nested' : self.is_array(attr.type) and not isinstance(attr.type, str) and self.is_array(attr.type.type),
|
||||
'is_derived' : attr.name in derived,
|
||||
'is_templated_list' : self.is_templated_list(attr),
|
||||
'argument_type_enum' : self.make_argument_type(attr),
|
||||
'argument_entity' : self.make_argument_entity(attr),
|
||||
'argument_type' : attr.type
|
||||
} for i, attr in attrs if include(attr)]
|
||||
|
||||
@@ -1,358 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import io
|
||||
import string
|
||||
import collections
|
||||
|
||||
class Node:
|
||||
def __init__(self, s, loc, tokens, rule=None):
|
||||
self.rule = rule or (type(self).__name__)
|
||||
self.tokens = tokens.asDict()
|
||||
self.flat = sum([getattr(t, 'flat', [t]) for t in tokens.asList()], [])
|
||||
if rule is None:
|
||||
self.init()
|
||||
def __repr__(self):
|
||||
return "%s(%s)" % (self.rule, ",".join("%s:%s" % i for i in self.tokens.items()))
|
||||
def __getattr__(self, k):
|
||||
return self.tokens.get(k)
|
||||
def __getstate__(self): return self.__dict__
|
||||
def __setstate__(self, d): self.__dict__.update(d)
|
||||
def init(self): pass
|
||||
def any(self):
|
||||
return next(iter(self.tokens.values()))
|
||||
|
||||
class ListNode:
|
||||
def __init__(self, s, loc, tokens, rule=None):
|
||||
self.rule = rule or (type(self).__name__)
|
||||
self.tokens = tokens.asList()
|
||||
self.flat = sum([getattr(t, 'flat', [t]) for t in self.tokens], [])
|
||||
def __repr__(self):
|
||||
return "%s[%s]" % (self.rule, ",".join("%s" % i for i in self.tokens))
|
||||
def __iter__(self):
|
||||
return iter(self.tokens)
|
||||
def __getitem__(self, i):
|
||||
return self.tokens[i]
|
||||
def init(self): pass
|
||||
|
||||
|
||||
class SimpleType(Node):
|
||||
|
||||
def get_type(self):
|
||||
t = self.any()
|
||||
if (type(t) == Node):
|
||||
return t.any()
|
||||
else:
|
||||
t = t[0]
|
||||
if (type(t) == Node):
|
||||
return t.any().any()
|
||||
else:
|
||||
return t
|
||||
|
||||
type = property(get_type)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.type)
|
||||
|
||||
|
||||
def format_clause(exp):
|
||||
def whitespace(t):
|
||||
if t in {'=', '|', '<*', 'or', 'in', '<>', 'and'}:
|
||||
return ' %s ' % t
|
||||
return t
|
||||
return "".join(whitespace(term) for term in exp.flat)
|
||||
|
||||
|
||||
class TypeDeclaration(Node):
|
||||
name = property(lambda self: self.type_id[0])
|
||||
type = property(lambda self: self.underlying_type.any().any())
|
||||
def init(self):
|
||||
|
||||
assert hasattr(self, "TYPE")
|
||||
|
||||
self.where = []
|
||||
clause = self.where_clause
|
||||
if clause:
|
||||
clause = clause[0]
|
||||
|
||||
self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]]
|
||||
|
||||
def __repr__(self):
|
||||
s = "TYPE %s = %s;\n" % (self.name, self.type)
|
||||
if self.where:
|
||||
s += " WHERE\n"
|
||||
for nm_exp in self.where:
|
||||
s += " %s : %s;\n" % nm_exp
|
||||
s += "END_TYPE;"
|
||||
return s
|
||||
|
||||
|
||||
class EntityDeclaration(Node):
|
||||
name = property(lambda self: self.entity_head[0].entity_id[0])
|
||||
supertype = property(lambda self: self.entity_head[0].subsuper[0].supertype_constraint)
|
||||
subtype = property(lambda self: self.entity_head[0].subsuper[0].subtype_declaration)
|
||||
supertypes = property(lambda self: [self.subtype.super_type] if self.subtype else [])
|
||||
|
||||
def get_abstract(self):
|
||||
if self.entity_head[0].subsuper[0].supertype_constraint:
|
||||
return self.entity_head[0].subsuper[0].supertype_constraint.abstract
|
||||
else:
|
||||
return False
|
||||
|
||||
abstract = property(get_abstract)
|
||||
|
||||
def init(self):
|
||||
|
||||
def redeclared_attribute(a):
|
||||
try:
|
||||
return (
|
||||
a.attribute_decl.redeclared_attribute.qualified_attribute.group_qualifier.simple_id,
|
||||
a.attribute_decl.redeclared_attribute.qualified_attribute.attribute_qualifier.simple_id
|
||||
)
|
||||
except:
|
||||
return a.attribute_decl.simple_id
|
||||
|
||||
assert self.flat[0] == 'entity'
|
||||
|
||||
self.attributes = [a for a in self.entity_body[0] if isinstance(a, ExplicitAttribute)]
|
||||
self.inverse = []
|
||||
alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == 'inverse']
|
||||
if alist:
|
||||
self.inverse = alist[0]
|
||||
|
||||
self.derive = []
|
||||
alist = [x for x in self.entity_body[0] if isinstance(x, AttributeList) and x.type == 'derive']
|
||||
if alist:
|
||||
alist = alist[0]
|
||||
self.derive = [(redeclared_attribute(a), format_clause(a.expression[0])) for a in alist]
|
||||
|
||||
self.where = []
|
||||
clause = [r for r in self.entity_body[0] if r.rule == "where_clause"]
|
||||
if clause:
|
||||
clause = clause[0]
|
||||
|
||||
self.where = [(r.simple_id, format_clause(r.expression[0])) for r in clause[1::2]]
|
||||
|
||||
self.unique = []
|
||||
clause = [r for r in self.entity_body[0] if r.rule == "unique_clause"]
|
||||
if clause:
|
||||
clause = clause[0]
|
||||
self.unique = [(r[0], r[2].simple_id) for r in clause[1::2]]
|
||||
|
||||
def __repr__(self):
|
||||
strm = io.StringIO()
|
||||
|
||||
print("ENTITY %s" % self.name, file=strm)
|
||||
|
||||
if self.supertype:
|
||||
print("", self.supertype, file=strm)
|
||||
if self.subtype:
|
||||
print("", self.subtype, file=strm)
|
||||
|
||||
strm.seek(strm.tell() - 1)
|
||||
print(";", file=strm)
|
||||
|
||||
for a in self.attributes:
|
||||
print(" ", a, ";", file=strm, sep='')
|
||||
|
||||
if self.derive:
|
||||
print(" DERIVE", file=strm)
|
||||
for nm, exp in self.derive:
|
||||
if isinstance(nm, tuple):
|
||||
nm = "SELF\\%s.%s" % nm
|
||||
print(" %s : %s;" % (nm, exp), file=strm)
|
||||
|
||||
if self.inverse:
|
||||
print(" INVERSE", file=strm)
|
||||
print(self.inverse, file=strm)
|
||||
|
||||
if self.where:
|
||||
print(" WHERE", file=strm)
|
||||
for nm_exp in self.where:
|
||||
print(" %s : %s;" % nm_exp, file=strm)
|
||||
|
||||
if self.unique:
|
||||
print(" UNIQUE", file=strm)
|
||||
for nm_exp in self.unique:
|
||||
print(" %s : %s;" % nm_exp, file=strm)
|
||||
|
||||
print("END_ENTITY;", file=strm)
|
||||
return strm.getvalue()
|
||||
|
||||
|
||||
class EnumerationType(Node):
|
||||
values = property(lambda self: self.enumeration_type[2][1::2])
|
||||
def __repr__(self):
|
||||
return "ENUMERATION OF (" + ",".join(self.values) + ")"
|
||||
|
||||
|
||||
class NamedType(Node):
|
||||
type = property(lambda self: self.simple_id)
|
||||
def __repr__(self):
|
||||
return self.type
|
||||
|
||||
|
||||
class AggregationType(Node):
|
||||
aggregate_type = property(lambda self: self.flat[0])
|
||||
bounds = property(lambda self: (list(self.tokens.values())[0][0].bound_spec or [None])[0])
|
||||
unique = property(lambda self: list(self.tokens.values())[0][0].UNIQUE is not None)
|
||||
|
||||
def get_type(self):
|
||||
v = list(self.tokens.values())[0][0]
|
||||
if v.instantiable_type:
|
||||
try:
|
||||
return v.instantiable_type.concrete_types.simple_id or v.instantiable_type.concrete_types.simple_types
|
||||
except:
|
||||
return v.instantiable_type
|
||||
elif v.parameter_type.simple_types:
|
||||
return v.parameter_type.simple_types
|
||||
elif v.parameter_type.named_types:
|
||||
return v.parameter_type.named_types
|
||||
elif v.parameter_type.generalized_types.general_aggregation_types:
|
||||
return v.parameter_type.generalized_types.general_aggregation_types
|
||||
else:
|
||||
import pdb; pdb.set_trace()
|
||||
raise ValueError()
|
||||
|
||||
type = property(get_type)
|
||||
|
||||
def init(self):
|
||||
assert self.bounds is None or isinstance(self.bounds, BoundSpecification)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s%s of %s%s"%(self.aggregate_type, self.bounds, "unique " if self.unique else "", self.type)
|
||||
|
||||
|
||||
class SelectType(Node):
|
||||
values = property(lambda self: self.select_type[1][1::2])
|
||||
def __repr__(self):
|
||||
return "SELECT (" + ",".join(map(str, self.values)) + ")"
|
||||
|
||||
|
||||
class SuperTypeExpression(Node):
|
||||
abstract = property(lambda self: self.abstract_supertype_declaration is not None)
|
||||
def get_sub_types(self):
|
||||
if self.abstract:
|
||||
constraint = self.abstract_supertype_declaration[0]
|
||||
else:
|
||||
constraint = self.supertype_rule[0]
|
||||
return [s[0][0].simple_id for s in constraint.subtype_constraint[0].supertype_expression[0][0][0].one_of[0][2::2]]
|
||||
|
||||
sub_types = property(get_sub_types)
|
||||
|
||||
def __repr__(self):
|
||||
return "%sSUPERTYPE OF(ONEOF(%s))" % ("ABSTRACT " if self.abstract else "",",".join(self.sub_types))
|
||||
|
||||
|
||||
class SubTypeExpression(Node):
|
||||
super_type = property(lambda self: self.entity_ref[0])
|
||||
|
||||
def __repr__(self):
|
||||
return "SUBTYPE OF(%s)" % self.super_type
|
||||
|
||||
class AttributeList(ListNode):
|
||||
type = property(lambda self: self.flat[0] if self.flat[0] in {'inverse', 'derive'} else 'explicit')
|
||||
def __repr__(self):
|
||||
return "\n".join([" %s;"%s for s in self.tokens[1:]])
|
||||
def __iter__(self):
|
||||
return iter(self.tokens[1:])
|
||||
def __len__(self):
|
||||
return len(self.tokens[1:])
|
||||
|
||||
|
||||
class InverseAttribute(Node):
|
||||
name = property(lambda self: self.attribute_decl.simple_id)
|
||||
type = property(lambda self: self.flat[2] if self.flat[2] != self.flat[-4] else None)
|
||||
bounds = property(lambda self: self.bound_spec[0] if self.bound_spec else None)
|
||||
entity = property(lambda self: self.entity_ref[0])
|
||||
attribute = property(lambda self: self.attribute_ref[0])
|
||||
def __repr__(self):
|
||||
def _():
|
||||
yield self.name
|
||||
yield ":"
|
||||
if self.type:
|
||||
yield self.type.upper()
|
||||
yield "OF"
|
||||
if self.bounds:
|
||||
yield self.bounds
|
||||
yield self.entity
|
||||
yield "FOR"
|
||||
yield self.attribute
|
||||
return " ".join(map(str, _()))
|
||||
|
||||
"""
|
||||
class DerivedAttribute(Node):
|
||||
def init(self):
|
||||
return
|
||||
name_index = list(self.tokens).index(':') - 1
|
||||
self.name = self.tokens[name_index]
|
||||
def __repr__(self):
|
||||
return str(self.name)
|
||||
"""
|
||||
|
||||
class BinaryType(Node):
|
||||
def __repr__(self):
|
||||
return "binary"
|
||||
|
||||
|
||||
class BoundSpecification(Node):
|
||||
lower = property(lambda self: self.flat[1])
|
||||
upper = property(lambda self: self.flat[3])
|
||||
|
||||
def __repr__(self):
|
||||
return "[%s:%s]"%(self.lower, self.upper)
|
||||
|
||||
|
||||
class ExplicitAttribute(Node):
|
||||
name = property(lambda self: self.attribute_decl.simple_id)
|
||||
optional = property(lambda self: self.OPTIONAL is not None)
|
||||
|
||||
def get_type(self):
|
||||
v = next(iter(self.parameter_type.tokens.values()))
|
||||
if v.general_aggregation_types:
|
||||
return v.general_aggregation_types
|
||||
else:
|
||||
return v
|
||||
|
||||
type = property(get_type)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s : %s%s" % (self.name, "optional " if self.optional else "", self.type)
|
||||
|
||||
|
||||
class WidthSpec(Node):
|
||||
fixed = property(lambda self: self.FIXED is not None)
|
||||
|
||||
def init(self):
|
||||
self.width = int(''.join(self.width[0].flat))
|
||||
|
||||
def __repr__(self):
|
||||
return "(%d)%s" % (self.width, " fixed" if self.fixed else "")
|
||||
|
||||
|
||||
class StringType(Node):
|
||||
width = property(lambda self: self.width_spec[0] if self.width_spec else None)
|
||||
|
||||
def __repr__(self):
|
||||
s = "string"
|
||||
if self.width:
|
||||
s += " " + repr(self.width)
|
||||
return s
|
||||
@@ -1,91 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import nodes
|
||||
import platform
|
||||
import collections
|
||||
|
||||
if tuple(map(int, platform.python_version_tuple())) < (2, 7):
|
||||
import ordereddict
|
||||
collections.OrderedDict = ordereddict.OrderedDict
|
||||
|
||||
# According to ISO 10303-11 7.1.2: Letters: "... The case of
|
||||
# letters is significant only within explicit string literals."
|
||||
class OrderedCaseInsensitiveDict_KeyObject(str):
|
||||
def __eq__(self, other):
|
||||
return self.lower() == other.lower()
|
||||
def __hash__(self):
|
||||
return hash(self.lower())
|
||||
|
||||
|
||||
class OrderedCaseInsensitiveDict(collections.OrderedDict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
collections.OrderedDict.__init__(self)
|
||||
for key, value in collections.OrderedDict(*args, **kwargs).items():
|
||||
self[OrderedCaseInsensitiveDict_KeyObject(key)] = value
|
||||
def __setitem__(self, key, value):
|
||||
return collections.OrderedDict.__setitem__(self, OrderedCaseInsensitiveDict_KeyObject(key), value)
|
||||
def __getitem__(self, key):
|
||||
return collections.OrderedDict.__getitem__(self, OrderedCaseInsensitiveDict_KeyObject(key))
|
||||
def get(self, key, *args, **kwargs):
|
||||
return collections.OrderedDict.get(self, OrderedCaseInsensitiveDict_KeyObject(key), *args, **kwargs)
|
||||
def __contains__(self, key):
|
||||
return collections.OrderedDict.__contains__(self, OrderedCaseInsensitiveDict_KeyObject(key))
|
||||
def __delitem__(self, key):
|
||||
return collections.OrderedDict.__delitem__(self, OrderedCaseInsensitiveDict_KeyObject(key))
|
||||
|
||||
|
||||
|
||||
class Schema:
|
||||
def is_enumeration(self, v):
|
||||
return str(v) in self.enumerations
|
||||
def is_select(self, v):
|
||||
return str(v) in self.selects
|
||||
def is_simpletype(self, v):
|
||||
return str(v) in self.simpletypes
|
||||
def is_type(self, v):
|
||||
return str(v) in self.types
|
||||
def is_entity(self, v):
|
||||
return str(v) in self.entities
|
||||
def __len__(self):
|
||||
return len(self.types) + len(self.entities)
|
||||
def __iter__(self):
|
||||
return iter(self.keys)
|
||||
def __getitem__(self, key):
|
||||
return self.types_entities[key]
|
||||
def __init__(self, parsetree):
|
||||
self.name = parsetree.syntax[0][0].simple_id
|
||||
|
||||
sort = lambda d: OrderedCaseInsensitiveDict(sorted(d))
|
||||
|
||||
declarations = [d.any()[0] for d in parsetree.syntax[0][0].schema_body[0] if d.rule == 'declaration' and d.any()[0].rule != 'function_decl']
|
||||
|
||||
self.types = sort([(t.name,t) for t in declarations if isinstance(t, nodes.TypeDeclaration)])
|
||||
self.entities = sort([(t.name,t) for t in declarations if isinstance(t, nodes.EntityDeclaration)])
|
||||
|
||||
self.keys = list(self.types.keys()) + list(self.entities.keys())
|
||||
self.types_entities = {k: v for d in (self.types, self.entities) for k, v in d.items()}
|
||||
|
||||
of_type = lambda *types: sort([(a, b.type) for a,b in self.types.items() if any(isinstance(b.type, ty) for ty in types)])
|
||||
|
||||
self.enumerations = of_type(nodes.EnumerationType)
|
||||
self.selects = of_type(nodes.SelectType)
|
||||
self.simpletypes = of_type(str, nodes.AggregationType, nodes.BinaryType, nodes.StringType, nodes.SimpleType, nodes.NamedType)
|
||||
|
||||
assert len(self.enumerations) + len(self.selects) + len(self.simpletypes) == len(self.types)
|
||||
@@ -1,277 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
import operator
|
||||
|
||||
import nodes
|
||||
import codegen
|
||||
import templates
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
class SchemaClass(codegen.Base):
|
||||
def __init__(self, mapping):
|
||||
|
||||
class UnmetDependenciesException(Exception): pass
|
||||
|
||||
schema_name = mapping.schema.name
|
||||
self.schema_name = schema_name_title = schema_name.capitalize()
|
||||
|
||||
declared_types = []
|
||||
|
||||
def get_declared_type(type, emitted_names=None):
|
||||
if isinstance(type, nodes.SimpleType):
|
||||
type = type.type
|
||||
if isinstance(type, nodes.NamedType):
|
||||
type = str(type)
|
||||
|
||||
if isinstance(type, nodes.AggregationType):
|
||||
aggr_type = type.aggregate_type
|
||||
make_bound = lambda b: -1 if b == '?' else int(b)
|
||||
bound1, bound2 = map(make_bound, (type.bounds.lower, type.bounds.upper))
|
||||
decl_type = get_declared_type(type.type, emitted_names)
|
||||
return "new aggregation_type(aggregation_type::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(decl_type)s)" % locals()
|
||||
elif isinstance(type, nodes.BinaryType):
|
||||
return "new simple_type(simple_type::binary_type)"
|
||||
elif isinstance(type, nodes.StringType):
|
||||
return "new simple_type(simple_type::string_type)"
|
||||
elif isinstance(type, str):
|
||||
if mapping.schema.is_type(type) or mapping.schema.is_entity(type):
|
||||
if emitted_names is None or type.lower() in emitted_names:
|
||||
return "new named_type(%s_%s_type)" % (schema_name, type)
|
||||
else:
|
||||
raise UnmetDependenciesException(type)
|
||||
else:
|
||||
return "new simple_type(simple_type::%s_type)" % type
|
||||
else:
|
||||
raise ValueError("No mapping for '%s'" % type)
|
||||
|
||||
def find_inverse_name_and_index(entity_name, attribute_name):
|
||||
attributes_per_subtype = []
|
||||
while True:
|
||||
entity = mapping.schema.entities[entity_name]
|
||||
attr_names = list(map(operator.attrgetter('name'), entity.attributes))
|
||||
if len(attr_names):
|
||||
attributes_per_subtype.append((entity_name, attr_names))
|
||||
if len(entity.supertypes) != 1: break
|
||||
entity_name = entity.supertypes[0]
|
||||
index = 0
|
||||
for et, attrs in attributes_per_subtype[::-1]:
|
||||
try: return et, attrs.index(attribute_name)
|
||||
except: pass
|
||||
|
||||
else:
|
||||
raise Exception("No declared type for <%r>" % type)
|
||||
|
||||
statements = ['',
|
||||
'#include "../ifcparse/IfcSchema.h"',
|
||||
'#include "../ifcparse/%(schema_name_title)s.h"' % locals(),
|
||||
'',
|
||||
'using namespace IfcParse;',
|
||||
'']
|
||||
|
||||
collections_by_type = (('entity', mapping.schema.entities ),
|
||||
('type_declaration', mapping.schema.simpletypes ),
|
||||
('select_type', mapping.schema.selects ),
|
||||
('enumeration_type', mapping.schema.enumerations))
|
||||
|
||||
for cpp_type, collection in collections_by_type:
|
||||
for name in collection.keys():
|
||||
statements.append('%(cpp_type)s* %(schema_name)s_%(name)s_type = 0;' % locals())
|
||||
|
||||
declarations_by_index = []
|
||||
|
||||
statements.append("{factory_placeholder}")
|
||||
|
||||
statements.append("""
|
||||
#if defined(__clang__)
|
||||
__attribute__((optnone))
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma optimize("", off)
|
||||
#endif
|
||||
""")
|
||||
statements.append('IfcParse::schema_definition* %(schema_name)s_populate_schema() {' % locals())
|
||||
|
||||
emitted = set()
|
||||
len_to_emit = len(mapping.schema)
|
||||
|
||||
def write_simpletype(schema_name, name, type):
|
||||
try:
|
||||
declared_type = get_declared_type(type, emitted)
|
||||
except UnmetDependenciesException:
|
||||
# @todo?
|
||||
# print("Unmet", repr(name))
|
||||
return False
|
||||
|
||||
statements.append(' %(schema_name)s_%(name)s_type = new type_declaration("%(name)s", %%(index_in_schema_%(name)s)d, %(declared_type)s);' % locals())
|
||||
|
||||
def write_enumeration(schema_name, name, enum):
|
||||
statements.append(' {')
|
||||
statements.append(' std::vector<std::string> items; items.reserve(%d);' % len(enum.values))
|
||||
statements.extend(map(lambda v: ' items.push_back("%s");' % v, sorted(enum.values)))
|
||||
statements.append(' %(schema_name)s_%(name)s_type = new enumeration_type("%(name)s", %%(index_in_schema_%(name)s)d, items);' % locals())
|
||||
statements.append(' }')
|
||||
|
||||
def write_entity(schema_name, name, type):
|
||||
if len(type.supertypes) == 0 or set(map(lambda s: s.lower(), type.supertypes)) < emitted:
|
||||
supertype = '0' if len(type.supertypes) == 0 else '%s_%s_type' % (schema_name, type.supertypes[0])
|
||||
is_abstract = "true" if type.abstract else "false"
|
||||
statements.append(' %(schema_name)s_%(name)s_type = new entity("%(name)s", %(is_abstract)s, %%(index_in_schema_%(name)s)d, %(supertype)s);' % locals())
|
||||
else: return False
|
||||
|
||||
def write_select(schema_name, name, type):
|
||||
if set(map(lambda s: str(s).lower(), type.values)) < emitted:
|
||||
statements.append(' {')
|
||||
statements.append(' std::vector<const declaration*> items; items.reserve(%d);' % len(type.values))
|
||||
statements.extend(map(lambda v: ' items.push_back(%s_%s_type);' % (schema_name, v), sorted(map(str, type.values))))
|
||||
statements.append(' %(schema_name)s_%(name)s_type = new select_type("%(name)s", %%(index_in_schema_%(name)s)d, items);' % locals())
|
||||
statements.append(' }')
|
||||
else: return False
|
||||
|
||||
def write(name):
|
||||
if mapping.schema.is_simpletype(name):
|
||||
fn = write_simpletype
|
||||
elif mapping.schema.is_enumeration(name):
|
||||
fn = write_enumeration
|
||||
elif mapping.schema.is_entity(name):
|
||||
fn = write_entity
|
||||
elif mapping.schema.is_select(name):
|
||||
fn = write_select
|
||||
|
||||
decl = mapping.schema[name]
|
||||
if isinstance(decl, nodes.TypeDeclaration):
|
||||
decl = decl.type
|
||||
return fn(schema_name, name, decl) is not False
|
||||
|
||||
while len(emitted) < len_to_emit:
|
||||
for name in mapping.schema:
|
||||
if name.lower() in emitted: continue
|
||||
if write(name):
|
||||
emitted.add(name.lower())
|
||||
declarations_by_index.append(name)
|
||||
declared_types.append('%(schema_name)s_%(name)s_type' % locals())
|
||||
|
||||
num_declarations = len(declared_types)
|
||||
|
||||
for name, type in mapping.schema.entities.items():
|
||||
derived = set(mapping.derived_in_supertype(type))
|
||||
attribute_names = list(map(operator.attrgetter('name'), mapping.arguments(type)))
|
||||
|
||||
statements.append(' {')
|
||||
statements.append(' std::vector<const attribute*> attributes; attributes.reserve(%d);' % len(type.attributes))
|
||||
for attr in type.attributes:
|
||||
attr_name, optional = attr.name, str(attr.optional).lower()
|
||||
decl_type = get_declared_type(attr.type)
|
||||
statements.append(' attributes.push_back(new attribute("%(attr_name)s", %(decl_type)s, %(optional)s));' % locals())
|
||||
statements.append(' std::vector<bool> derived; derived.reserve(%d);' % len(attribute_names))
|
||||
statements.append(' ' + " ".join(map(lambda b: 'derived.push_back(%s);' % str(b in derived).lower(), attribute_names)))
|
||||
statements.append(' %(schema_name)s_%(name)s_type->set_attributes(attributes, derived);' % locals())
|
||||
statements.append(' }')
|
||||
|
||||
for name, type in mapping.schema.entities.items():
|
||||
if type.inverse:
|
||||
statements.append(' {')
|
||||
statements.append(' std::vector<const inverse_attribute*> attributes; attributes.reserve(%d);' % len(type.inverse))
|
||||
for attr in type.inverse:
|
||||
if attr.bounds:
|
||||
make_bound = lambda b: -1 if b == '?' else int(b)
|
||||
bound1, bound2 = map(make_bound, (attr.bounds.lower, attr.bounds.upper))
|
||||
else:
|
||||
bound1, bound2 = -1, -1
|
||||
attr_name, aggr_type, entity_ref = attr.name, attr.type, attr.entity
|
||||
if aggr_type is None: aggr_type = 'unspecified'
|
||||
attribute_entity, attribute_entity_index = find_inverse_name_and_index(entity_ref, attr.attribute)
|
||||
statements.append(' attributes.push_back(new inverse_attribute("%(attr_name)s", inverse_attribute::%(aggr_type)s_type, %(bound1)d, %(bound2)d, %(schema_name)s_%(entity_ref)s_type, %(schema_name)s_%(attribute_entity)s_type->attributes()[%(attribute_entity_index)d]));' % locals())
|
||||
statements.append(' %(schema_name)s_%(name)s_type->set_inverse_attributes(attributes);' % locals())
|
||||
statements.append(' }')
|
||||
|
||||
subtypes = defaultdict(list)
|
||||
|
||||
for name, type in mapping.schema.entities.items():
|
||||
for ty in type.supertypes:
|
||||
subtypes[ty].append(name)
|
||||
|
||||
for name, tys in subtypes.items():
|
||||
statements.append(' {')
|
||||
statements.append(' std::vector<const entity*> defs; defs.reserve(%d);' % len(tys))
|
||||
statements.append((' ' + "".join(map(lambda t: ("defs.push_back(%%(schema_name)s_%s_type);" % t), tys))) % locals())
|
||||
statements.append(' %(schema_name)s_%(name)s_type->set_subtypes(defs);' % locals())
|
||||
statements.append(' }')
|
||||
|
||||
statements.append('')
|
||||
statements.append(' std::vector<const declaration*> declarations; declarations.reserve(%(num_declarations)d);' % locals())
|
||||
for type_name in declared_types:
|
||||
statements.append(' declarations.push_back(%(type_name)s);' % locals())
|
||||
|
||||
statements.append(' return new schema_definition("%(schema_name)s", declarations, new %(schema_name)s_instance_factory());' % locals())
|
||||
|
||||
statements.extend(('}',''))
|
||||
|
||||
statements.append("""
|
||||
#if defined(__clang__)
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
#pragma GCC pop_options
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma optimize("", on)
|
||||
#endif
|
||||
""")
|
||||
|
||||
statements.extend(('const schema_definition& %s::get_schema() {' % schema_name_title,
|
||||
'',
|
||||
' static const schema_definition* s = %(schema_name)s_populate_schema();' % locals(),
|
||||
' return *s;',
|
||||
'}','',''))
|
||||
|
||||
declarations_by_index.sort(key=str.lower)
|
||||
declarations_by_index_map = dict(("index_in_schema_%s" % j,i) for i,j in enumerate(declarations_by_index))
|
||||
|
||||
def bind(s):
|
||||
if "%" in s: return s % declarations_by_index_map
|
||||
else: return s
|
||||
|
||||
can_be_instantiated_set = set(list(mapping.schema.entities.keys()) + list(mapping.schema.simpletypes.keys()))
|
||||
def can_be_instantiated(idx_name):
|
||||
name = idx_name[1]
|
||||
return name in can_be_instantiated_set
|
||||
|
||||
instance_mapping = """switch(data->type()->index_in_schema()) {
|
||||
%s
|
||||
default: throw IfcParse::IfcException(data->type()->name() + " cannot be instantiated");
|
||||
}
|
||||
""" % "\n ".join(map(lambda tup: ("case %%d: return new ::%s::%%s(data);" % schema_name_title) % tup, filter(can_be_instantiated, enumerate(declarations_by_index))))
|
||||
|
||||
statements[statements.index("{factory_placeholder}")] = """
|
||||
class %(schema_name)s_instance_factory : public IfcParse::instance_factory {
|
||||
virtual IfcUtil::IfcBaseClass* operator()(IfcEntityInstanceData* data) const {
|
||||
%(instance_mapping)s
|
||||
}
|
||||
};
|
||||
""" % locals()
|
||||
|
||||
self.str = "\n".join(map(bind, statements))
|
||||
|
||||
self.file_name = '%s-schema.cpp'%self.schema_name
|
||||
|
||||
def __repr__(self):
|
||||
return self.str
|
||||
|
||||
Generator = SchemaClass
|
||||
@@ -1,227 +0,0 @@
|
||||
###############################################################################
|
||||
# #
|
||||
# This file is part of IfcOpenShell. #
|
||||
# #
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the Lesser GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# IfcOpenShell is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# Lesser GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the Lesser GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
###############################################################################
|
||||
|
||||
header = """
|
||||
#ifndef %(schema_name_upper)s_H
|
||||
#define %(schema_name_upper)s_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
|
||||
#include "../ifcparse/IfcEntityList.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
#include "../ifcparse/IfcSchema.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/Argument.h"
|
||||
|
||||
struct %(schema_name)s {
|
||||
|
||||
static const IfcParse::schema_definition& get_schema();
|
||||
|
||||
static const char* const Identifier;
|
||||
|
||||
// Forward definitions
|
||||
%(forward_definitions)s
|
||||
|
||||
%(declarations)s
|
||||
|
||||
%(class_definitions)s
|
||||
};
|
||||
|
||||
#endif
|
||||
"""
|
||||
|
||||
enum_header = """
|
||||
#ifndef %(schema_name_upper)sENUM_H
|
||||
#define %(schema_name_upper)sENUM_H
|
||||
|
||||
#include "../ifcparse/ifc_parse_api.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#endif
|
||||
"""
|
||||
|
||||
lb_header = """"""
|
||||
|
||||
implementation= """
|
||||
#include "../ifcparse/%(schema_name)s.h"
|
||||
#include "../ifcparse/IfcSchema.h"
|
||||
#include "../ifcparse/IfcException.h"
|
||||
#include "../ifcparse/IfcWrite.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
const char* const %(schema_name)s::Identifier = "%(schema_name_upper)s";
|
||||
|
||||
using namespace IfcParse;
|
||||
using namespace IfcWrite;
|
||||
|
||||
// External definitions
|
||||
%(external_definitions)s
|
||||
|
||||
%(enumeration_functions)s
|
||||
|
||||
%(simple_type_impl)s
|
||||
|
||||
%(entity_implementations)s
|
||||
"""
|
||||
|
||||
lb_implementation = """"""
|
||||
|
||||
entity_descriptor = """ current = entity_descriptor_map[Type::%(type)s] = new IfcEntityDescriptor(Type::%(type)s,%(parent_statement)s);
|
||||
%(entity_descriptor_attributes)s"""
|
||||
|
||||
entity_descriptor_parent = "entity_descriptor_map.find(Type::%(type)s)->second"
|
||||
entity_descriptor_attribute_without_entity = ' current->add("%(name)s",%(optional)s,%(type)s);'
|
||||
entity_descriptor_attribute_with_entity = ' current->add("%(name)s",%(optional)s,%(type)s,Type::%(entity_name)s);'
|
||||
|
||||
enumeration_descriptor = """ values.clear(); values.reserve(128);
|
||||
%(enumeration_descriptor_values)s
|
||||
enumeration_descriptor_map[Type::%(type)s] = new IfcEnumerationDescriptor(Type::%(type)s, values);"""
|
||||
|
||||
enumeration_descriptor_value = ' values.push_back("%(name)s");'
|
||||
|
||||
derived_field_statement = ' {std::set<int> idxs; %(statements)sderived_map[Type::%(type)s] = idxs;}';
|
||||
derived_field_statement_attrs = 'idxs.insert(%d); '
|
||||
|
||||
simpletype = """%(documentation)s
|
||||
class IFC_PARSE_API %(name)s : public %(superclass)s {
|
||||
public:
|
||||
virtual const IfcParse::type_declaration& declaration() const;
|
||||
static const IfcParse::type_declaration& Class();
|
||||
explicit %(name)s (IfcEntityInstanceData* e);
|
||||
%(name)s (%(type)s v);
|
||||
operator %(type)s() const;
|
||||
};
|
||||
"""
|
||||
|
||||
simpletype_impl_comment = "// Function implementations for %(name)s"
|
||||
simpletype_impl_argument_type = "if (i == 0) { return %(attr_type)s; } else { throw IfcParse::IfcAttributeOutOfRangeException(\"Argument index out of range\"); }"
|
||||
simpletype_impl_argument = "return data_->getArgument(i);"
|
||||
simpletype_impl_is_with_supertype = "return v == %(class_name)s_type || %(superclass)s::is(v);"
|
||||
simpletype_impl_is_without_supertype = "return v == %(class_name)s_type;"
|
||||
simpletype_impl_type = "return *%(schema_name_upper)s_%(class_name)s_type;"
|
||||
simpletype_impl_class = "return *%(schema_name_upper)s_%(class_name)s_type;"
|
||||
simpletype_impl_explicit_constructor = "data_ = e;"
|
||||
simpletype_impl_constructor = "data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(class_name)s_type); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v" +"); data_->setArgument(0, attr);}"
|
||||
simpletype_impl_constructor_templated = "data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(class_name)s_type); {IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(v->generalize()); data_->setArgument(0, attr);}"
|
||||
simpletype_impl_cast = "return *data_->getArgument(0);"
|
||||
simpletype_impl_cast_templated = "IfcEntityList::ptr es = *data_->getArgument(0); return es->as< %(underlying_type)s >();"
|
||||
simpletype_impl_declaration = "return *%(schema_name_upper)s_%(class_name)s_type;"
|
||||
|
||||
select = """%(documentation)s
|
||||
typedef IfcUtil::IfcBaseClass %(name)s;
|
||||
"""
|
||||
|
||||
enumeration = """struct %(name)s {
|
||||
%(documentation)s
|
||||
typedef enum {%(values)s} Value;
|
||||
IFC_PARSE_API static const char* ToString(Value v);
|
||||
IFC_PARSE_API static Value FromString(const std::string& s);
|
||||
};
|
||||
"""
|
||||
|
||||
entity = """%(documentation)s
|
||||
class IFC_PARSE_API %(name)s %(superclass)s{
|
||||
public:
|
||||
%(attributes)s %(inverse)s virtual const IfcParse::entity& declaration() const;
|
||||
static const IfcParse::entity& Class();
|
||||
%(name)s (IfcEntityInstanceData* e);
|
||||
%(name)s (%(constructor_arguments)s);
|
||||
typedef IfcTemplatedEntityList< %(name)s > list;
|
||||
};
|
||||
"""
|
||||
|
||||
enumeration_function="""
|
||||
const char* %(schema_name)s::%(name)s::ToString(Value v) {
|
||||
if ( v < 0 || v >= %(max_id)d ) throw IfcException("Unable to find find keyword in schema");
|
||||
const char* names[] = { %(values)s };
|
||||
return names[v];
|
||||
}
|
||||
|
||||
%(schema_name)s::%(name)s::Value %(schema_name)s::%(name)s::FromString(const std::string& s) {
|
||||
%(from_string_statements)s
|
||||
throw IfcException("Unable to find find keyword in schema");
|
||||
}
|
||||
"""
|
||||
|
||||
entity_implementation = """// Function implementations for %(name)s
|
||||
%(attributes)s
|
||||
%(inverse)s
|
||||
const IfcParse::entity& %(schema_name)s::%(name)s::declaration() const { return *%(schema_name_upper)s_%(name)s_type; }
|
||||
const IfcParse::entity& %(schema_name)s::%(name)s::Class() { return *%(schema_name_upper)s_%(name)s_type; }
|
||||
%(schema_name)s::%(name)s::%(name)s(IfcEntityInstanceData* e) : %(superclass)s { if (!e) return; if (e->type() != %(schema_name_upper)s_%(name)s_type) throw IfcException("Unable to find find keyword in schema"); data_ = e; }
|
||||
%(schema_name)s::%(name)s::%(name)s(%(constructor_arguments)s) : %(superclass)s {data_ = new IfcEntityInstanceData(%(schema_name_upper)s_%(name)s_type); %(constructor_implementation)s }
|
||||
"""
|
||||
|
||||
optional_attribute_description = "/// Whether the optional attribute %s is defined for this %s"
|
||||
|
||||
function = "%(return_type)s %(schema_name)s::%(class_name)s::%(name)s(%(arguments)s) { %(body)s }"
|
||||
const_function = "%(return_type)s %(schema_name)s::%(class_name)s::%(name)s(%(arguments)s) const { %(body)s }"
|
||||
constructor = "%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) { %(body)s }"
|
||||
constructor_single_initlist = "%(schema_name)s::%(class_name)s::%(class_name)s(%(arguments)s) : %(superclass)s(%(superclass_init)s) { %(body)s }"
|
||||
cast_function = "%(schema_name)s::%(class_name)s::operator %(return_type)s() const { %(body)s }"
|
||||
|
||||
array_type = "std::vector< %(instance_type)s > /*[%(lower)s:%(upper)s]*/"
|
||||
nested_array_type = "std::vector< std::vector< %(instance_type)s > >"
|
||||
list_type = "IfcTemplatedEntityList< %(instance_type)s >::ptr"
|
||||
list_list_type = "IfcTemplatedEntityListList< %(instance_type)s >::ptr"
|
||||
untyped_list = "IfcEntityList::ptr"
|
||||
inverse_attr = "IfcTemplatedEntityList< %(entity)s >::ptr %(name)s() const; // INVERSE %(entity)s::%(attribute)s"
|
||||
|
||||
enum_from_string_stmt = ' if (s == "%(value)s") return ::%(schema_name)s::%(name)s::%(short_name)s_%(value)s;'
|
||||
|
||||
schema_entity_stmt = ' case Type::%(name)s: return new %(name)s(e); break;'
|
||||
string_map_statement = ' string_map["%(uppercase_name)s"%(padding)s] = Type::%(name)s;'
|
||||
parent_type_stmt = ' if(v==%(name)s%(padding)s) { return %(parent)s; }'
|
||||
|
||||
parent_type_test = " || %s::is(v)"
|
||||
|
||||
optional_attr_stmt = "return !data_->getArgument(%(index)d)->isNull();"
|
||||
|
||||
get_attr_stmt = "return *data_->getArgument(%(index)d);"
|
||||
get_attr_stmt_enum = "return %(type)s::FromString(*data_->getArgument(%(index)d));"
|
||||
get_attr_stmt_entity = "return (%(type)s)((IfcUtil::IfcBaseClass*)(*data_->getArgument(%(index)d)));"
|
||||
get_attr_stmt_array = "IfcEntityList::ptr es = *data_->getArgument(%(index)d); return es->as< %(list_instance_type)s >();"
|
||||
get_attr_stmt_nested_array = "IfcEntityListList::ptr es = *data_->getArgument(%(index)d); return es->as< %(list_instance_type)s >();"
|
||||
|
||||
get_inverse = "return data_->getInverse(%(schema_name_upper)s_%(type)s_type, %(index)d)->as<%(type)s>();"
|
||||
|
||||
set_attr_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v" +");data_->setArgument(%(index)d,attr);}"
|
||||
set_attr_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::EnumerationReference(v,%(type)s::ToString(v)));data_->setArgument(%(index)d,attr);}"
|
||||
set_attr_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(v->generalize()" +");data_->setArgument(%(index)d,attr);}"
|
||||
|
||||
constructor_stmt = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)" +");data_->setArgument(%(index)d,attr);}"
|
||||
constructor_stmt_enum = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((IfcWrite::IfcWriteArgument::EnumerationReference(%(name)s,%(type)s::ToString(%(name)s)))" +");data_->setArgument(%(index)d,attr);}"
|
||||
constructor_stmt_array = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set((%(name)s)->generalize()" +");data_->setArgument(%(index)d,attr);}"
|
||||
constructor_stmt_derived = "{IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument();attr->set(IfcWrite::IfcWriteArgument::Derived()" +");data_->setArgument(%(index)d,attr);}"
|
||||
|
||||
constructor_stmt_optional = " if (%(name)s) {%(stmt)s } else { IfcWrite::IfcWriteArgument* attr = new IfcWrite::IfcWriteArgument(); attr->set(boost::blank()); data_->setArgument(%(index)d, attr); }"
|
||||
|
||||
inverse_implementation = " inverse_map[Type::%(type)s].insert(std::make_pair(\"%(name)s\", std::make_pair(Type::%(related_type)s, %(index)d)));"
|
||||
|
||||
def multi_line_comment(li):
|
||||
return ("/// %s"%("\n/// ".join(li))) if len(li) else ""
|
||||
|
||||
@@ -584,10 +584,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l,
|
||||
}
|
||||
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS_Shape& face) {
|
||||
const double x1 = l->BottomXDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double x1 = l->BottomXDim() / 2. * getValue(GV_LENGTH_UNIT);
|
||||
const double w = l->TopXDim() * getValue(GV_LENGTH_UNIT);
|
||||
const double dx = l->TopXOffset() * getValue(GV_LENGTH_UNIT);
|
||||
const double y = l->YDim() / 2.0f * getValue(GV_LENGTH_UNIT);
|
||||
const double y = l->YDim() / 2. * getValue(GV_LENGTH_UNIT);
|
||||
|
||||
// See: https://forums.buildingsmart.org/t/how-are-the-sides-of-ifctrapeziumprofiledefs-bounding-box-calculated-in-most-implementations/2945/8
|
||||
// The trapezium x center should not be midway of BottomXDim but rather at the center of the overall bounding box.
|
||||
const double x_offset = ((std::min(dx, 0.) + std::max(w + dx, x1 * 2.)) / 2.) - x1;
|
||||
|
||||
if ( x1 < ALMOST_ZERO || w < ALMOST_ZERO || y < ALMOST_ZERO ) {
|
||||
Logger::Message(Logger::LOG_NOTICE,"Skipping zero sized profile:",l);
|
||||
@@ -603,7 +607,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf2d);
|
||||
}
|
||||
|
||||
double coords[8] = {-x1,-y, x1,-y, dx+w-x1,y, dx-x1,y};
|
||||
double coords[8] = {
|
||||
-x1 - x_offset, -y,
|
||||
+x1 - x_offset, -y,
|
||||
-x1 + dx + w - x_offset, y,
|
||||
-x1 + dx - x_offset,y
|
||||
};
|
||||
return profile_helper(4,coords,0,0,0,trsf2d,face);
|
||||
}
|
||||
|
||||
|
||||
@@ -1854,6 +1854,18 @@ IfcGeom::BRepElement<P, PP>* IfcGeom::Kernel::create_brep_for_representation_and
|
||||
representation_id_builder << "-material-" << single_material->data().id();
|
||||
}
|
||||
|
||||
if (settings.force_space_transparency() >= 0. && product->declaration().is("IfcSpace")) {
|
||||
for (auto& s : shapes) {
|
||||
if (s.hasStyle()) {
|
||||
for (auto& p : style_cache) {
|
||||
if (&p.second == &s.Style()) {
|
||||
p.second.Transparency() = settings.force_space_transparency();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int parent_id = -1;
|
||||
try {
|
||||
IfcUtil::IfcBaseEntity* parent_object = get_decomposing_entity(product);
|
||||
@@ -2712,7 +2724,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre
|
||||
|
||||
double layer_offset = 0;
|
||||
|
||||
const double total_thickness = std::accumulate(thicknesses.begin(), thicknesses.end(), 0);
|
||||
const double total_thickness = std::accumulate(thicknesses.begin(), thicknesses.end(), 0.);
|
||||
|
||||
std::vector<double>::const_iterator thickness = thicknesses.begin();
|
||||
result_t::iterator result_vector = result.begin() + 1;
|
||||
@@ -2849,6 +2861,13 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre
|
||||
|
||||
namespace {
|
||||
|
||||
void subshapes(const TopoDS_Shape& in, std::list<TopoDS_Shape>& out) {
|
||||
TopoDS_Iterator sit(in);
|
||||
for (; sit.More(); sit.Next()) {
|
||||
out.push_back(sit.Value());
|
||||
}
|
||||
}
|
||||
|
||||
#if OCC_VERSION_HEX >= 0x70200
|
||||
bool split(IfcGeom::Kernel&, const TopoDS_Shape& input, const TopTools_ListOfShape& operands, double eps, std::vector<TopoDS_Shape>& slices) {
|
||||
if (operands.Extent() < 2) {
|
||||
@@ -2880,18 +2899,19 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// Count subshapes
|
||||
size_t n = 0;
|
||||
TopoDS_Iterator sit(split.Shape());
|
||||
for (; sit.More(); sit.Next()) {
|
||||
++n;
|
||||
auto result_shape = split.Shape();
|
||||
std::list<TopoDS_Shape> subs;
|
||||
subshapes(result_shape, subs);
|
||||
if (subs.size() == 1 && operands.Size() - 2 > subs.size() && (subs.front().ShapeType() == TopAbs_COMPSOLID || subs.front().ShapeType() == TopAbs_COMPOUND)) {
|
||||
auto s = subs.front();
|
||||
subs.clear();
|
||||
subshapes(s, subs);
|
||||
}
|
||||
|
||||
// Initialize storage
|
||||
slices.resize(n);
|
||||
slices.resize(subs.size());
|
||||
|
||||
sit.Initialize(split.Shape());
|
||||
for (; sit.More(); sit.Next()) {
|
||||
for (auto& s : subs) {
|
||||
|
||||
// Iterate over the faces of solid to find correspondence to original
|
||||
// splitting surfaces. For the outmost slices, there will be a single
|
||||
@@ -2900,7 +2920,7 @@ namespace {
|
||||
// slices, two surface indices should be find that should be next to
|
||||
// each other in the array of input surfaces.
|
||||
|
||||
TopExp_Explorer exp(sit.Value(), TopAbs_FACE);
|
||||
TopExp_Explorer exp(s, TopAbs_FACE);
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = std::numeric_limits<int>::min();
|
||||
for (; exp.More(); exp.Next()) {
|
||||
@@ -2928,7 +2948,7 @@ namespace {
|
||||
|
||||
if (idx < (int) slices.size()) {
|
||||
if (slices[idx].IsNull()) {
|
||||
slices[idx] = sit.Value();
|
||||
slices[idx] = s;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,11 +101,14 @@ namespace IfcGeom
|
||||
IteratorSettings()
|
||||
: settings_(WELD_VERTICES) // OR options that default to true here
|
||||
, deflection_tolerance_(1.e-3)
|
||||
, angular_tolerance_(0.5)
|
||||
{
|
||||
}
|
||||
|
||||
/// Note that this is independent of the IFC length unit, one millimeter by default.
|
||||
double deflection_tolerance() const { return deflection_tolerance_; }
|
||||
double angular_tolerance() const { return angular_tolerance_; }
|
||||
double force_space_transparency() const { return force_space_transparency_; }
|
||||
|
||||
void set_deflection_tolerance(double value)
|
||||
{
|
||||
@@ -118,6 +121,14 @@ namespace IfcGeom
|
||||
}
|
||||
}
|
||||
|
||||
void set_angular_tolerance(double value) {
|
||||
angular_tolerance_ = value;
|
||||
}
|
||||
|
||||
void force_space_transparency(double value) {
|
||||
force_space_transparency_ = value;
|
||||
}
|
||||
|
||||
/// Get boolean value for a single settings or for a combination of settings.
|
||||
bool get(SettingField setting) const
|
||||
{
|
||||
@@ -143,7 +154,7 @@ namespace IfcGeom
|
||||
|
||||
protected:
|
||||
SettingField settings_;
|
||||
double deflection_tolerance_;
|
||||
double deflection_tolerance_, angular_tolerance_, force_space_transparency_;
|
||||
};
|
||||
|
||||
class IFC_GEOM_API ElementSettings : public IteratorSettings
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace IfcGeom {
|
||||
|
||||
// Triangulate the shape
|
||||
try {
|
||||
BRepMesh_IncrementalMesh(s, settings().deflection_tolerance());
|
||||
BRepMesh_IncrementalMesh(s, settings().deflection_tolerance(), false, settings().angular_tolerance());
|
||||
} catch(...) {
|
||||
Logger::Message(Logger::LOG_ERROR, "Failed to triangulate shape");
|
||||
continue;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <TColStd_Array1OfReal.hxx>
|
||||
#include <TColStd_Array2OfReal.hxx>
|
||||
#include <TColStd_Array1OfInteger.hxx>
|
||||
#include <Geom_BezierCurve.hxx>
|
||||
|
||||
#include "IfcGeom.h"
|
||||
|
||||
@@ -129,6 +130,52 @@ int convert_to_ifc(const Handle_Geom_Curve& c, IfcSchema::IfcCurve*& curve, bool
|
||||
return 1;
|
||||
}
|
||||
#ifdef SCHEMA_HAS_IfcRationalBSplineSurfaceWithKnots
|
||||
else if (c->DynamicType() == STANDARD_TYPE(Geom_BezierCurve)) {
|
||||
Handle_Geom_BezierCurve bezier = Handle_Geom_BezierCurve::DownCast(c);
|
||||
|
||||
std::vector<int> mults;
|
||||
std::vector<double> knots;
|
||||
std::vector<double> weights;
|
||||
|
||||
IfcSchema::IfcKnotType::Value knot_spec = IfcSchema::IfcKnotType::IfcKnotType_QUASI_UNIFORM_KNOTS;
|
||||
|
||||
IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
|
||||
TColgp_Array1OfPnt poles(1, bezier->NbPoles());
|
||||
bezier->Poles(poles);
|
||||
for (int i = 1; i <= bezier->NbPoles(); ++i) {
|
||||
IfcSchema::IfcCartesianPoint* p;
|
||||
if (!convert_to_ifc(poles.Value(i), p, advanced)) {
|
||||
return 0;
|
||||
}
|
||||
points->push(p);
|
||||
|
||||
if (i == 1 || i == bezier->NbPoles()) {
|
||||
mults.push_back(bezier->Degree() + 1);
|
||||
} else {
|
||||
mults.push_back(bezier->Degree());
|
||||
}
|
||||
|
||||
knots.push_back((double) i - 1);
|
||||
}
|
||||
|
||||
TColStd_Array1OfReal bspline_weights(1, bezier->NbPoles());
|
||||
bezier->Weights(bspline_weights);
|
||||
opencascade_array_to_vector(bspline_weights, weights);
|
||||
|
||||
curve = new IfcSchema::IfcRationalBSplineCurveWithKnots(
|
||||
bezier->Degree(),
|
||||
points,
|
||||
IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
|
||||
bezier->IsClosed() != 0,
|
||||
false,
|
||||
mults,
|
||||
knots,
|
||||
knot_spec,
|
||||
weights
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if (c->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve)) {
|
||||
Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c);
|
||||
|
||||
@@ -460,7 +507,7 @@ int convert_to_ifc(const TopoDS_Face& f, IfcSchema::IfcFace*& face, bool advance
|
||||
face = new IfcSchema::IfcFace(bounds);
|
||||
return 1;
|
||||
} else {
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcAdvancedFace
|
||||
IfcSchema::IfcSurface* surface;
|
||||
if (!convert_to_ifc(surf, surface, advanced)) {
|
||||
return 0;
|
||||
@@ -499,7 +546,8 @@ int convert_to_ifc(const TopoDS_Shape& s, U*& item, bool advanced) {
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* IfcGeom::MAKE_TYPE_NAME(serialise_)(const TopoDS_Shape& shape, bool advanced) {
|
||||
#ifndef USE_IFC4
|
||||
|
||||
#ifndef SCHEMA_HAS_IfcAdvancedBrep
|
||||
advanced = false;
|
||||
#endif
|
||||
|
||||
@@ -528,7 +576,7 @@ IfcUtil::IfcBaseClass* IfcGeom::MAKE_TYPE_NAME(serialise_)(const TopoDS_Shape& s
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_IFC4
|
||||
#ifdef SCHEMA_HAS_IfcAdvancedBrep
|
||||
if (advanced) {
|
||||
if (inner->size()) {
|
||||
items->push(new IfcSchema::IfcAdvancedBrepWithVoids(outer, inner));
|
||||
|
||||
@@ -355,7 +355,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceOfRevolution* l, TopoDS
|
||||
bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_Shape& shape) {
|
||||
const double ang = l->Angle() * getValue(GV_PLANEANGLE_UNIT);
|
||||
|
||||
TopoDS_Face face;
|
||||
TopoDS_Shape face;
|
||||
if ( ! convert_face(l->SweptArea(),face) ) return false;
|
||||
|
||||
gp_Ax1 ax1;
|
||||
@@ -370,6 +370,45 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRevolvedAreaSolid* l, TopoDS_S
|
||||
IfcGeom::Kernel::convert(l->Position(), trsf);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// https://github.com/IfcOpenShell/IfcOpenShell/issues/1030
|
||||
// Check whether Axis does not intersect SweptArea
|
||||
|
||||
double min_dot = +std::numeric_limits<double>::infinity();
|
||||
double max_dot = -std::numeric_limits<double>::infinity();
|
||||
|
||||
gp_Ax2 ax(ax1.Location(), gp::DZ(), ax1.Direction());
|
||||
|
||||
TopExp_Explorer exp(face, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
BRepAdaptor_Curve crv(TopoDS::Edge(exp.Current()));
|
||||
GCPnts_QuasiUniformDeflection tessellater(crv, getValue(GV_PRECISION));
|
||||
|
||||
int n = tessellater.NbPoints();
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
double d = ax.YDirection().XYZ().Dot(tessellater.Value(i).XYZ());
|
||||
if (d < min_dot) {
|
||||
min_dot = d;
|
||||
}
|
||||
if (d > max_dot) {
|
||||
max_dot = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool intersecting;
|
||||
if (std::abs(min_dot) > std::abs(max_dot)) {
|
||||
intersecting = max_dot > + getValue(GV_PRECISION);
|
||||
} else {
|
||||
intersecting = min_dot < - getValue(GV_PRECISION);
|
||||
}
|
||||
|
||||
if (intersecting) {
|
||||
Logger::Warning("Warning Axis and SweptArea intersecting", l);
|
||||
}
|
||||
}
|
||||
|
||||
if (ang >= M_PI * 2. - ALMOST_ZERO) {
|
||||
shape = BRepPrimAPI_MakeRevol(face, ax1);
|
||||
} else {
|
||||
@@ -1188,7 +1227,7 @@ namespace {
|
||||
// @todo we could be extruding the wire only when we know this is an intermediate edge.
|
||||
const double depth = std::abs(u - v);
|
||||
TopoDS_Face face = BRepBuilderAPI_MakeFace(section).Face();
|
||||
result = BRepPrimAPI_MakeRevol(section, circ->Axis(), depth).Shape();
|
||||
result = BRepPrimAPI_MakeRevol(face, circ->Axis(), depth).Shape();
|
||||
}
|
||||
|
||||
void process_sweep_as_pipe(const TopoDS_Wire& wire, const TopoDS_Wire& section, TopoDS_Shape& result, bool force_transformed=false) {
|
||||
@@ -1408,6 +1447,29 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count(wire, TopAbs_EDGE) == 1) {
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(wire, v0, v1);
|
||||
if (v0.IsSame(v1)) {
|
||||
TopExp_Explorer exp(wire, TopAbs_EDGE);
|
||||
auto& e = TopoDS::Edge(exp.Current());
|
||||
double a, b;
|
||||
auto crv = BRep_Tool::Curve(e, a, b);
|
||||
if ((crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) ||
|
||||
(crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)))
|
||||
{
|
||||
BRepBuilderAPI_MakeEdge me(crv, l->StartParam(), l->EndParam());
|
||||
if (me.IsDone()) {
|
||||
auto e2 = me.Edge();
|
||||
BRep_Builder B;
|
||||
wire.Nullify();
|
||||
B.MakeWire(wire);
|
||||
B.Add(wire, e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Note that StartParam and EndParam param are ignored and the assumption is
|
||||
// made that the parametric range over which to be swept matches the IfcCurve in
|
||||
// its entirety.
|
||||
|
||||
@@ -581,17 +581,34 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrimmedCurve* l, TopoDS_Wire&
|
||||
// Fix from @sanderboer to compare using model tolerance, see #744
|
||||
// Made dependent on radius, see #928
|
||||
|
||||
// @todo another good critereon for determining whether to take full curve
|
||||
// A good critereon for determining whether to take full curve
|
||||
// or trimmed segment would be whether there are other curve segments or this
|
||||
// is the only one. But it does not really match the bottom-up construction
|
||||
// mechanism of IfcOpenShell.
|
||||
// is the only one.
|
||||
boost::optional<size_t> num_segments;
|
||||
auto segment = l->data().getInverse(&IfcSchema::IfcCompositeCurveSegment::Class(), -1);
|
||||
if (segment->size() == 1) {
|
||||
auto comp = (*segment->begin())->data().getInverse(&IfcSchema::IfcCompositeCurve::Class(), -1);
|
||||
if (comp->size() == 1) {
|
||||
num_segments = (*comp->begin())->as<IfcSchema::IfcCompositeCurve>()->Segments()->size();
|
||||
}
|
||||
}
|
||||
|
||||
if (isConic && ALMOST_THE_SAME(fmod(flts[1]-flts[0],M_PI*2.), 0., 100 * getValue(GV_PRECISION) / (2 * M_PI * radius))) {
|
||||
e = BRepBuilderAPI_MakeEdge(curve).Edge();
|
||||
} else {
|
||||
BRepBuilderAPI_MakeEdge me (curve,flts[0],flts[1]);
|
||||
e = me.Edge();
|
||||
}
|
||||
}
|
||||
|
||||
if (num_segments && *num_segments > 1) {
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(e, v0, v1);
|
||||
if (v0.IsSame(v1)) {
|
||||
Logger::Warning("Skipping degenerate segment", l);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} else if ( trim_cartesian_failed && (has_pnts[0] && has_pnts[1]) ) {
|
||||
e = BRepBuilderAPI_MakeEdge(pnts[0], pnts[1]).Edge();
|
||||
}
|
||||
|
||||
@@ -96,7 +96,10 @@ namespace IfcGeom {
|
||||
boost::optional<double>& Specularity() { return specularity; }
|
||||
};
|
||||
|
||||
IFC_GEOM_API const SurfaceStyle* get_default_style(const std::string& ifc_type);
|
||||
IFC_GEOM_API const SurfaceStyle* get_default_style(const std::string& ifc_type);
|
||||
|
||||
IFC_GEOM_API SurfaceStyle& update_default_style(const std::string& ifc_type);
|
||||
|
||||
IFC_GEOM_API void set_default_style_file(const std::string& json_file);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ void InitDefaultMaterials() {
|
||||
default_materials.insert(std::make_pair("IfcPlate", IfcGeom::SurfaceStyle("IfcPlate")));
|
||||
default_materials["IfcPlate"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.8, 0.8, 0.8));
|
||||
|
||||
default_materials.insert(std::make_pair("IfcSpace", IfcGeom::SurfaceStyle("IfcSpace")));
|
||||
default_materials["IfcWindow"].Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.65, 0.75, 0.8));
|
||||
default_materials["IfcWindow"].Transparency().reset(0.8);
|
||||
|
||||
default_material = IfcGeom::SurfaceStyle("DefaultMaterial");
|
||||
default_material.Diffuse().reset(IfcGeom::SurfaceStyle::ColorComponent(0.7, 0.7, 0.7));
|
||||
|
||||
@@ -121,3 +125,12 @@ const IfcGeom::SurfaceStyle* IfcGeom::get_default_style(const std::string& s) {
|
||||
const IfcGeom::SurfaceStyle& surface_style = it->second;
|
||||
return &surface_style;
|
||||
}
|
||||
|
||||
IfcGeom::SurfaceStyle& IfcGeom::update_default_style(const std::string& s) {
|
||||
if (!default_materials_initialized) InitDefaultMaterials();
|
||||
std::map<std::string, IfcGeom::SurfaceStyle>::iterator it = default_materials.find(s);
|
||||
if (it == default_materials.end()) {
|
||||
throw std::runtime_error("No style registered for " + s);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ def create_entity(type, *args, **kwargs):
|
||||
for idx, arg in attrs:
|
||||
e[idx] = arg
|
||||
return e
|
||||
|
||||
|
||||
gcroot = []
|
||||
def register_schema(schema):
|
||||
gcroot.append(schema)
|
||||
ifcopenshell_wrapper.register_schema(schema.schema)
|
||||
|
||||
from .main import *
|
||||
|
||||
@@ -11,7 +11,11 @@ import multiprocessing
|
||||
|
||||
import OCC.AIS
|
||||
|
||||
from collections import defaultdict, Iterable, OrderedDict
|
||||
from collections import defaultdict, OrderedDict
|
||||
try: # python 3.3+
|
||||
from collections.abc import Iterable
|
||||
except ModuleNotFoundError: # python 2
|
||||
from collections import Iterable
|
||||
|
||||
try:
|
||||
QString = unicode
|
||||
|
||||
@@ -25,7 +25,11 @@ import random
|
||||
import operator
|
||||
import warnings
|
||||
|
||||
from collections import namedtuple, Iterable
|
||||
from collections import namedtuple
|
||||
try: # python 3.3+
|
||||
from collections.abc import Iterable
|
||||
except ModuleNotFoundError: # python 2
|
||||
from collections import Iterable
|
||||
|
||||
try:
|
||||
from OCC.Core import V3d, TopoDS, gp, AIS, Quantity, BRepTools, Graphic3d
|
||||
|
||||
@@ -44,7 +44,7 @@ def get_properties(properties):
|
||||
results = {}
|
||||
for prop in properties:
|
||||
if prop.is_a('IfcPropertySingleValue'):
|
||||
results[prop.Name] = prop.NominalValue
|
||||
results[prop.Name] = prop.NominalValue.wrappedValue
|
||||
elif prop.is_a('IfcComplexProperty'):
|
||||
data = prop.get_info()
|
||||
data['properties'] = get_properties(prop.HasProperties)
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import math
|
||||
|
||||
def dms2dd(degrees, minutes, seconds, milliseconds=0):
|
||||
dd = float(degrees) + float(minutes)/60.0 + float(seconds)/(3600.0) + float(milliseconds/3600000.0)
|
||||
def dms2dd(degrees, minutes, seconds, ms=0):
|
||||
dd = float(degrees) + float(minutes)/60.0 + float(seconds)/(3600.0) + float(ms/3600000000.0)
|
||||
return dd
|
||||
|
||||
def dd2dms(dd):
|
||||
def dd2dms(dd, use_ms=False):
|
||||
dd = float(dd)
|
||||
sign = 1 if dd >= 0 else -1
|
||||
dd = abs(dd)
|
||||
minutes, seconds = divmod(dd*3600, 60)
|
||||
if use_ms:
|
||||
seconds, ms = divmod(dd*60*60*1000000, 1000000)
|
||||
minutes, seconds = divmod(dd*60*60, 60)
|
||||
degrees, minutes = divmod(minutes, 60)
|
||||
if dd < 0:
|
||||
degrees = -degrees
|
||||
if use_ms:
|
||||
return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign, int(ms) * sign)
|
||||
return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign)
|
||||
|
||||
def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_axis_ordinate, scale=None):
|
||||
@@ -27,4 +31,4 @@ def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_
|
||||
|
||||
# Used for converting the X and Y vectors of the X Axis in IFC geolocation
|
||||
def xy2angle(x, y):
|
||||
return math.degrees(math.atan2(y, x)) - 90
|
||||
return math.degrees(math.atan2(y, x))
|
||||
|
||||
@@ -215,6 +215,8 @@ class Selector():
|
||||
and key.split('.')[0] == 'type':
|
||||
try:
|
||||
element = ifcopenshell.util.element.get_type(element)
|
||||
if not element:
|
||||
return None
|
||||
except:
|
||||
return
|
||||
key = '.'.join(key.split('.')[1:])
|
||||
|
||||
@@ -17,12 +17,12 @@ si_conversions = {
|
||||
'yard': 0.914,
|
||||
'mile': 1609,
|
||||
'square inch': 0.0006452,
|
||||
'square foot': 0.09290,
|
||||
'square foot': 0.09290304,
|
||||
'square yard': 0.83612736,
|
||||
'acre': 4046.86,
|
||||
'square mile': 2588881,
|
||||
'cubic inch': 0.00001639,
|
||||
'cubic foot': 0.02832,
|
||||
'cubic foot': 0.02831684671168849,
|
||||
'cubic yard': 0.7636,
|
||||
'litre': 0.001,
|
||||
'fluid ounce UK': 0.0000284130625,
|
||||
|
||||
@@ -74,6 +74,18 @@ namespace IfcUtil {
|
||||
}
|
||||
};
|
||||
|
||||
class IFC_PARSE_API IfcLateBoundEntity : public IfcBaseClass {
|
||||
private:
|
||||
const IfcParse::declaration* decl_;
|
||||
|
||||
public:
|
||||
IfcLateBoundEntity(const IfcParse::declaration* decl, IfcEntityInstanceData* data) : IfcBaseClass(data), decl_(decl) {}
|
||||
|
||||
virtual const IfcParse::declaration& declaration() const {
|
||||
return *decl_;
|
||||
}
|
||||
};
|
||||
|
||||
class IFC_PARSE_API IfcBaseEntity : public IfcBaseClass {
|
||||
public:
|
||||
IfcBaseEntity() : IfcBaseClass() {}
|
||||
|
||||
@@ -195,6 +195,15 @@ public:
|
||||
IfcUtil::IfcBaseClass* addEntity(IfcUtil::IfcBaseClass* entity);
|
||||
void addEntities(IfcEntityList::ptr es);
|
||||
|
||||
/// Removes entity instance from file and unsets references.
|
||||
///
|
||||
/// Attention when running removeEntity inside a loop over a list of entities to be removed.
|
||||
/// This invalidates the iterator. A workaround is to reverse the loop:
|
||||
/// boost::shared_ptr<IfcEntityList> entities = ...;
|
||||
/// for (auto it = entities->end() - 1; it >= entities->begin(); --it) {
|
||||
/// IfcUtil::IfcBaseClass *const inst = *it;
|
||||
/// model->removeEntity(inst);
|
||||
/// }
|
||||
void removeEntity(IfcUtil::IfcBaseClass* entity);
|
||||
|
||||
const IfcSpfHeader& header() const { return _header; }
|
||||
|
||||
@@ -1763,6 +1763,14 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity) {
|
||||
void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
|
||||
const unsigned id = entity->data().id();
|
||||
IfcUtil::IfcBaseClass* file_entity = instance_by_id(id);
|
||||
|
||||
// Attention when running removeEntity inside a loop over a list of entities to be removed.
|
||||
// This invalidates the iterator. A workaround is to reverse the loop:
|
||||
// boost::shared_ptr<IfcEntityList> entities = ...;
|
||||
// for (auto it = entities->end() - 1; it >= entities->begin(); --it) {
|
||||
// IfcUtil::IfcBaseClass *const inst = *it;
|
||||
// model->removeEntity(inst);
|
||||
// }
|
||||
|
||||
// TODO: Create a set of weak relations. Inverse relations that do not dictate an
|
||||
// instance to be retained. For example: when deleting an IfcRepresentation, the
|
||||
@@ -2173,4 +2181,4 @@ void IfcParse::IfcFile::build_inverses() {
|
||||
for (auto& pair : *this) {
|
||||
build_inverses_(pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "IfcSchema.h"
|
||||
#include "../ifcparse/IfcBaseClass.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -62,6 +63,19 @@ IfcParse::schema_definition::~schema_definition() {
|
||||
}
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseClass* IfcParse::schema_definition::instantiate(IfcEntityInstanceData * data) const {
|
||||
if (factory_) {
|
||||
return (*factory_)(data);
|
||||
} else {
|
||||
return new IfcUtil::IfcLateBoundEntity(data->type(), data);
|
||||
}
|
||||
}
|
||||
|
||||
void IfcParse::register_schema(schema_definition* s) {
|
||||
schemas.insert({ s->name(), s });
|
||||
}
|
||||
|
||||
|
||||
#include "../ifcparse/Ifc2x3.h"
|
||||
#include "../ifcparse/Ifc4.h"
|
||||
#include "../ifcparse/Ifc4x1.h"
|
||||
|
||||
@@ -443,10 +443,12 @@ namespace IfcParse {
|
||||
|
||||
const std::string& name() const { return name_; }
|
||||
|
||||
IfcUtil::IfcBaseClass* instantiate(IfcEntityInstanceData* data) const { return (*factory_)(data); }
|
||||
IfcUtil::IfcBaseClass* instantiate(IfcEntityInstanceData* data) const;
|
||||
};
|
||||
|
||||
const schema_definition* schema_by_name(const std::string&);
|
||||
|
||||
void register_schema(schema_definition*);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -83,7 +83,10 @@ IF(PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "")
|
||||
IF("${python_package_dir}" STREQUAL "")
|
||||
MESSAGE(WARNING "Unable to locate Python site-package directory, unable to install the Python wrapper")
|
||||
ELSE()
|
||||
FILE(GLOB_RECURSE sourcefiles "${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.py")
|
||||
FILE(GLOB_RECURSE sourcefiles
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/*.bnf"
|
||||
)
|
||||
FOREACH(file ${sourcefiles})
|
||||
FILE(RELATIVE_PATH relative "${CMAKE_CURRENT_SOURCE_DIR}/../ifcopenshell-python/ifcopenshell/" "${file}")
|
||||
GET_FILENAME_COMPONENT(dir "${relative}" DIRECTORY)
|
||||
|
||||
@@ -154,9 +154,28 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
|
||||
|
||||
%extend IfcGeom::IteratorSettings {
|
||||
%pythoncode %{
|
||||
attrs = ("convert_back_units", "deflection_tolerance", "disable_opening_subtractions", "disable_triangulation", "faster_booleans", "sew_shells", "use_brep_data", "use_world_coords", "weld_vertices")
|
||||
def __repr__(self):
|
||||
return "%s(%s)"%(self.__class__.__name__, ",".join(tuple("%s=%r"%(a, getattr(self, a)()) for a in self.attrs)))
|
||||
|
||||
old_init = __init__
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.old_init()
|
||||
for k, v in kwargs.items():
|
||||
self.set(getattr(self, k), v)
|
||||
|
||||
def __repr__(self):
|
||||
def d():
|
||||
import numbers
|
||||
for x in dir(self):
|
||||
if x.isupper() and x not in {"NUM_SETTINGS", "USE_PYTHON_OPENCASCADE"}:
|
||||
v = getattr(self, x)
|
||||
if isinstance(v, numbers.Integral):
|
||||
yield x
|
||||
|
||||
return "%s(%s)" % (
|
||||
type(self).__name__,
|
||||
(", ".join(map(lambda x: "%s = %r" % (x, self.get(getattr(self, x))), d())))
|
||||
)
|
||||
|
||||
%}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,8 @@
|
||||
return SWIGTYPE_p_IfcParse__select_type;
|
||||
} else if (t->as_enumeration_type()) {
|
||||
return SWIGTYPE_p_IfcParse__enumeration_type;
|
||||
} else {
|
||||
throw std::runtime_error("Unexpected declaration type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,96 @@ CREATE_VECTOR_TYPEMAP_IN(int, INTEGER, int)
|
||||
CREATE_VECTOR_TYPEMAP_IN(double, REAL, float)
|
||||
CREATE_VECTOR_TYPEMAP_IN(std::string, STRING, str)
|
||||
|
||||
// @todo use macros.
|
||||
|
||||
%typemap(in) const std::vector<const IfcParse::declaration*>& {
|
||||
if (PySequence_Check($input)) {
|
||||
$1 = new std::vector<const IfcParse::declaration*>;
|
||||
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
|
||||
PyObject* element = PySequence_GetItem($input, i);
|
||||
void *arg = 0;
|
||||
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcParse__declaration, 0);
|
||||
auto decl = static_cast<const IfcParse::declaration*>(SWIG_IsOK(res) ? arg : 0);
|
||||
if (decl) {
|
||||
$1->push_back(decl);
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected a schema declaration");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected an sequence type");
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) const std::vector<const IfcParse::entity*>& {
|
||||
if (PySequence_Check($input)) {
|
||||
$1 = new std::vector<const IfcParse::entity*>;
|
||||
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
|
||||
PyObject* element = PySequence_GetItem($input, i);
|
||||
void *arg = 0;
|
||||
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcParse__entity, 0);
|
||||
auto decl = static_cast<const IfcParse::entity*>(SWIG_IsOK(res) ? arg : 0);
|
||||
if (decl) {
|
||||
$1->push_back(decl);
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected a schema entity");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected an sequence type");
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) const std::vector<const IfcParse::attribute*>& {
|
||||
if (PySequence_Check($input)) {
|
||||
$1 = new std::vector<const IfcParse::attribute*>;
|
||||
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
|
||||
PyObject* element = PySequence_GetItem($input, i);
|
||||
void *arg = 0;
|
||||
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcParse__attribute, 0);
|
||||
auto decl = static_cast<const IfcParse::attribute*>(SWIG_IsOK(res) ? arg : 0);
|
||||
if (decl) {
|
||||
$1->push_back(decl);
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected a schema attribute");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected an sequence type");
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) const std::vector<const IfcParse::inverse_attribute*>& {
|
||||
if (PySequence_Check($input)) {
|
||||
$1 = new std::vector<const IfcParse::inverse_attribute*>;
|
||||
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
|
||||
PyObject* element = PySequence_GetItem($input, i);
|
||||
void *arg = 0;
|
||||
int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_IfcParse__inverse_attribute, 0);
|
||||
auto decl = static_cast<const IfcParse::inverse_attribute*>(SWIG_IsOK(res) ? arg : 0);
|
||||
if (decl) {
|
||||
$1->push_back(decl);
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected a schema inverse attribute");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected an sequence type");
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) const std::vector<bool>& {
|
||||
if (PySequence_Check($input)) {
|
||||
$1 = new std::vector<bool>;
|
||||
for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
|
||||
PyObject* element = PySequence_GetItem($input, i);
|
||||
$1->push_back(PyObject_IsTrue(element));
|
||||
}
|
||||
} else {
|
||||
SWIG_exception(SWIG_TypeError, "Expected an sequence type");
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in) IfcEntityList::ptr {
|
||||
if (PySequence_Check($input)) {
|
||||
$1 = IfcEntityList::ptr(new IfcEntityList());
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
$result = SWIG_NewPointerObj(SWIG_as_voidptr($1->as_simple_type()), SWIGTYPE_p_IfcParse__simple_type, 0);
|
||||
} else if ($1->as_aggregation_type()) {
|
||||
$result = SWIG_NewPointerObj(SWIG_as_voidptr($1->as_aggregation_type()), SWIGTYPE_p_IfcParse__aggregation_type, 0);
|
||||
} else {
|
||||
throw std::runtime_error("unexpected parameter type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
|
||||
#include <ShapeFix_Edge.hxx>
|
||||
|
||||
#include "../ifcparse/IfcGlobalId.h"
|
||||
|
||||
#include "SvgSerializer.h"
|
||||
@@ -128,7 +130,7 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
|
||||
double r = circle->Radius();
|
||||
gp_Circ c = circle->Circ();
|
||||
gp_Pnt center = c.Location();
|
||||
path.add(" <circle style=\"stroke:black; fill:none;\" r=\"");
|
||||
path.add(" <circle r=\"");
|
||||
radii.push_back(path.add(r));
|
||||
path.add("\" cx=\"");
|
||||
xcoords.push_back(path.add(center.X()));
|
||||
@@ -146,7 +148,7 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
|
||||
gp_Pnt center = e.Location();
|
||||
|
||||
// Write the ellipse with major radius along X axis:
|
||||
path.add(" <ellipse style=\"stroke:black; fill:none;\" rx=\"");
|
||||
path.add(" <ellipse rx=\"");
|
||||
radii.push_back(path.add(e.MajorRadius()));
|
||||
path.add("\" ry=\"");
|
||||
radii.push_back(path.add(e.MinorRadius()));
|
||||
@@ -193,7 +195,7 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
|
||||
}
|
||||
|
||||
if (first) {
|
||||
path.add(" <path style=\"stroke:black; fill:none;\" d=\"");
|
||||
path.add(" <path d=\"");
|
||||
path.add("M");
|
||||
addXCoordinate(path.add(p1.X()));
|
||||
path.add(",");
|
||||
@@ -291,68 +293,159 @@ void SvgSerializer::write(path_object& p, const TopoDS_Wire& wire) {
|
||||
}
|
||||
|
||||
SvgSerializer::path_object& SvgSerializer::start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id) {
|
||||
SvgSerializer::path_object& p = paths.insert(std::make_pair(storey, path_object()))->second;
|
||||
auto key = std::make_pair(std::make_pair(storey, ""), path_object());
|
||||
SvgSerializer::path_object& p = paths.insert(key)->second;
|
||||
p.first = id;
|
||||
return p;
|
||||
}
|
||||
|
||||
void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
{
|
||||
std::vector<std::pair<std::pair<double, double>, IfcUtil::IfcBaseEntity*>> section_heights_storage;
|
||||
const std::vector<std::pair<std::pair<double, double>, IfcUtil::IfcBaseEntity*>>* section_heights_used = §ion_heights_storage;
|
||||
SvgSerializer::path_object& SvgSerializer::start_path(const std::string& drawing_name, const std::string& id) {
|
||||
auto key = std::make_pair(std::make_pair(nullptr, drawing_name), path_object());
|
||||
SvgSerializer::path_object& p = paths.insert(key)->second;
|
||||
p.first = id;
|
||||
return p;
|
||||
}
|
||||
|
||||
if (section_heights) {
|
||||
section_heights_used = section_heights.get_ptr();
|
||||
} else {
|
||||
namespace {
|
||||
boost::optional<std::pair<IfcUtil::IfcBaseEntity*, double>> storey_elevation_from_element(const IfcGeom::BRepElement<real_t>* o) {
|
||||
for (const auto& p : o->parents()) {
|
||||
if (p->type() == "IfcBuildingStorey") {
|
||||
try {
|
||||
const IfcGeom::ElementSettings& settings = o->geometry().settings();
|
||||
double e = *p->product()->get("Elevation");
|
||||
double storey_elevation = e * settings.unit_magnitude();
|
||||
section_heights_storage.push_back({ {storey_elevation, +1.} , p->product() });
|
||||
return std::make_pair(p->product(), storey_elevation);
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (section_heights_storage.empty()) {
|
||||
Logger::Warning("No global section height and unable to determine building storey for:", o->product());
|
||||
boost::optional<TopoDS_Edge> edge_from_compound(TopoDS_Shape& compound) {
|
||||
TopoDS_Iterator it(compound);
|
||||
if (it.More()) {
|
||||
TopoDS_Shape wire = it.Value();
|
||||
it.Next();
|
||||
if (!it.More() && wire.ShapeType() == TopAbs_WIRE) {
|
||||
TopoDS_Iterator jt(wire);
|
||||
if (jt.More()) {
|
||||
TopoDS_Shape edge = jt.Value();
|
||||
jt.Next();
|
||||
if (!jt.More() && edge.ShapeType() == TopAbs_EDGE) {
|
||||
return TopoDS::Edge(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* brep_obj) {
|
||||
|
||||
boost::optional<std::string> object_type;
|
||||
if (!brep_obj->product()->get("ObjectType")->isNull()) {
|
||||
object_type = static_cast<std::string>(*brep_obj->product()->get("ObjectType"));
|
||||
}
|
||||
|
||||
TopoDS_Shape compound_local = brep_obj->geometry().as_compound();
|
||||
const gp_Trsf& trsf = brep_obj->transformation().data();
|
||||
|
||||
const bool is_section = (section_ref_ && object_type && *section_ref_ == *object_type);
|
||||
const bool is_elevation = (elevation_ref_ && object_type && *elevation_ref_ == *object_type);
|
||||
|
||||
if (is_section || is_elevation) {
|
||||
BRepBuilderAPI_Transform make_transform_global(compound_local, trsf, true);
|
||||
make_transform_global.Build();
|
||||
// (When determinant < 0, copy is implied and the input is not mutated.)
|
||||
auto compound_unmirrored = make_transform_global.Shape();
|
||||
|
||||
auto e = edge_from_compound(compound_unmirrored);
|
||||
if (e) {
|
||||
TopoDS_Edge global_edge = TopoDS::Edge(e->Moved(trsf));
|
||||
double u0, u1;
|
||||
auto crv = BRep_Tool::Curve(global_edge, u0, u1);
|
||||
if (crv->DynamicType() == STANDARD_TYPE(Geom_Line)) {
|
||||
gp_Pnt P;
|
||||
gp_Vec V;
|
||||
crv->D1((u0 + u1) / 2., P, V);
|
||||
auto N = V.Crossed(gp::DZ());
|
||||
gp_Pln pln(gp_Ax3(P, N, V));
|
||||
if (!deferred_section_data_) {
|
||||
deferred_section_data_.emplace();
|
||||
}
|
||||
std::string name = brep_obj->name();
|
||||
if (name.empty()) {
|
||||
name = boost::lexical_cast<std::string>(brep_obj->id());
|
||||
}
|
||||
if (is_section) {
|
||||
deferred_section_data_->push_back(vertical_section{ pln , "Section " + name, false });
|
||||
}
|
||||
if (is_elevation) {
|
||||
deferred_section_data_->push_back(vertical_section{ pln , "Elevation " + name, true });
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto p = storey_elevation_from_element(brep_obj);
|
||||
IfcUtil::IfcBaseEntity* storey = p ? p->first : nullptr;
|
||||
double elev = p ? p->second : std::numeric_limits<double>::quiet_NaN();
|
||||
geometry_data data{ compound_local, trsf, brep_obj->product(), storey, elev, brep_obj->name(), nameElement(storey, brep_obj) };
|
||||
|
||||
if (buffer_elements_) {
|
||||
element_buffer_.push_back(data);
|
||||
}
|
||||
|
||||
write(data);
|
||||
}
|
||||
|
||||
void SvgSerializer::write(const geometry_data& data) {
|
||||
std::vector<section_data> section_heights_storage;
|
||||
const std::vector<section_data>* section_heights_used = §ion_heights_storage;
|
||||
|
||||
if (section_data_) {
|
||||
section_heights_used = section_data_.get_ptr();
|
||||
} else {
|
||||
if (data.storey) {
|
||||
section_heights_storage.push_back(horizontal_plan{ data.storey, data.storey_elevation, +1. });
|
||||
} else {
|
||||
Logger::Warning("No global section height and unable to determine building storey for:", data.product);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TopoDS_Shape compound_local = o->geometry().as_compound();
|
||||
const gp_Trsf& trsf = o->transformation().data();
|
||||
BRepBuilderAPI_Transform make_transform_global(compound_local, trsf, true);
|
||||
BRepBuilderAPI_Transform make_transform_global(data.compound_local, data.trsf, true);
|
||||
make_transform_global.Build();
|
||||
// (When determinant < 0, copy is implied and the input is not mutated.)
|
||||
auto compound = make_transform_global.Shape();
|
||||
auto compound_unmirrored = make_transform_global.Shape();
|
||||
|
||||
// SVG has a coordinate system with the origin in the *upper*-left corner
|
||||
// therefore we mirror the shape along the XZ-plane.
|
||||
gp_Trsf trsf_mirror;
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
BRepBuilderAPI_Transform make_transform_mirror(compound, trsf_mirror, true);
|
||||
BRepBuilderAPI_Transform make_transform_mirror(compound_unmirrored, trsf_mirror, true);
|
||||
make_transform_mirror.Build();
|
||||
// (When determinant < 0, copy is implied and the input is not mutated.)
|
||||
compound = make_transform_mirror.Shape();
|
||||
auto compound = make_transform_mirror.Shape();
|
||||
|
||||
TopoDS_Wire annotation;
|
||||
|
||||
if (draw_door_arcs_ && o->product()->declaration().is("IfcDoor")) {
|
||||
if (is_floor_plan_ && draw_door_arcs_ && data.product->declaration().is("IfcDoor")) {
|
||||
|
||||
boost::optional<std::string> operation_type;
|
||||
|
||||
try {
|
||||
IfcEntityList::ptr rels;
|
||||
if (o->product()->declaration().schema()->name() == "IFC2X3") {
|
||||
rels = o->product()->get_inverse("IsDefinedBy");
|
||||
if (data.product->declaration().schema()->name() == "IFC2X3") {
|
||||
rels = data.product->get_inverse("IsDefinedBy");
|
||||
} else {
|
||||
// Damn you, IFC
|
||||
rels = o->product()->get_inverse("IsTypedBy");
|
||||
rels = data.product->get_inverse("IsTypedBy");
|
||||
}
|
||||
for (auto& rel : *rels) {
|
||||
if (rel->declaration().name() == "IfcRelDefinesByType") {
|
||||
@@ -372,7 +465,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
const bool is_left = *operation_type == "SINGLE_SWING_LEFT";
|
||||
|
||||
Bnd_Box bb;
|
||||
BRepBndLib::Add(compound_local, bb);
|
||||
BRepBndLib::Add(data.compound_local, bb);
|
||||
|
||||
if (bb.IsVoid()) {
|
||||
return;
|
||||
@@ -405,9 +498,9 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
make_transform_mirror.Perform(edge_global, true);
|
||||
auto edge_global_mirrored = make_transform_mirror.Shape();
|
||||
|
||||
center.Transform(trsf);
|
||||
p1.Transform(trsf);
|
||||
p2.Transform(trsf);
|
||||
center.Transform(data.trsf);
|
||||
p1.Transform(data.trsf);
|
||||
p2.Transform(data.trsf);
|
||||
center.Transform(trsf_mirror);
|
||||
p1.Transform(trsf_mirror);
|
||||
p2.Transform(trsf_mirror);
|
||||
@@ -431,22 +524,48 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
bool emitted = false;
|
||||
|
||||
for (auto sit = section_heights_used->begin(); sit != section_heights_used->end(); ++sit) {
|
||||
const auto& pair = *sit;
|
||||
const auto& variant = *sit;
|
||||
|
||||
// Elev + offset
|
||||
auto cut_z = pair.first.first + pair.first.second;
|
||||
double cut_z = std::numeric_limits<double>::infinity();
|
||||
|
||||
// Elev .. Elev(next)
|
||||
std::pair<double, double> range{ pair.first.first, std::numeric_limits<double>::infinity() };
|
||||
if (sit == section_heights_used->begin()) {
|
||||
range.first = -range.second;
|
||||
}
|
||||
if (sit + 1 != section_heights_used->end()) {
|
||||
range.second = (sit + 1)->first.first;
|
||||
}
|
||||
auto storey = pair.second;
|
||||
std::pair<double, double> range;
|
||||
|
||||
TopoDS_Iterator it(compound);
|
||||
gp_Vec projection_direction;
|
||||
|
||||
IfcUtil::IfcBaseEntity* storey = nullptr;
|
||||
std::string drawing_name;
|
||||
|
||||
bool use_hlr = false;
|
||||
|
||||
// @todo use visitor
|
||||
// horizontal_plan, horizontal_plan_at_element, vertical_section
|
||||
if (variant.which() == 0) {
|
||||
const auto& plan = boost::get<horizontal_plan>(variant);
|
||||
storey = plan.storey;
|
||||
cut_z = plan.elevation + plan.offset;
|
||||
range = { plan.elevation, plan.next_elevation };
|
||||
if (sit == section_heights_used->begin()) {
|
||||
range.first = -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
projection_direction = gp::DZ();
|
||||
} else if (variant.which() == 1) {
|
||||
projection_direction = gp::DZ();
|
||||
} else if (variant.which() == 2) {
|
||||
const auto& section = boost::get<vertical_section>(variant);
|
||||
projection_direction = section.plane.Axis().Direction();
|
||||
drawing_name = section.name;
|
||||
use_hlr = section.with_projection;
|
||||
}
|
||||
|
||||
auto& compound_to_use = is_floor_plan_ ? compound : compound_unmirrored;
|
||||
|
||||
if (use_hlr && hlr) {
|
||||
hlr->Add(compound_to_use);
|
||||
}
|
||||
|
||||
TopoDS_Iterator it(compound_to_use);
|
||||
|
||||
TopoDS_Face largest_closed_wire_face;
|
||||
double largest_closed_wire_area = 0.;
|
||||
@@ -456,7 +575,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
for (; it.More(); it.Next()) {
|
||||
|
||||
const TopoDS_Shape& subshape = it.Value();
|
||||
|
||||
|
||||
Bnd_Box bb;
|
||||
try {
|
||||
BRepBndLib::Add(it.Value(), bb);
|
||||
@@ -469,19 +588,28 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
|
||||
double x1, y1, zmin, x2, y2, zmax;
|
||||
bb.Get(x1, y1, zmin, x2, y2, zmax);
|
||||
|
||||
|
||||
// Determine slicing plane z coordinate, priority:
|
||||
// 1) explicitly set global section height
|
||||
// 2) containing building storey elevation + 1m
|
||||
// 3) zmin (from geometry bounding box) + 1m
|
||||
|
||||
if (std::isnan(cut_z)) {
|
||||
if (variant.which() == 1) {
|
||||
cut_z = zmin + 1.;
|
||||
}
|
||||
|
||||
if (o->type() == "IfcAnnotation" && ((zmax - zmin) < 1.e-5) && zmin >= range.first && zmin <= range.second) {
|
||||
|
||||
gp_Vec bbmin(x1, y1, zmin);
|
||||
gp_Vec bbmax(x2, y2, zmax);
|
||||
auto bbdif = bbmax - bbmin;
|
||||
auto proj = projection_direction ^ bbdif ^ projection_direction;
|
||||
|
||||
if (data.product->declaration().is("IfcAnnotation") && (proj.Magnitude() > 1.e-5) && zmin >= range.first && zmin <= range.second) {
|
||||
if (po == nullptr) {
|
||||
po = &start_path(storey, nameElement(storey, o));
|
||||
if (storey) {
|
||||
po = &start_path(storey, data.svg_name);
|
||||
} else {
|
||||
po = &start_path(drawing_name, data.svg_name);
|
||||
}
|
||||
}
|
||||
|
||||
TopExp_Explorer exp(subshape, TopAbs_EDGE, TopAbs_FACE);
|
||||
@@ -499,6 +627,9 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
B.Add(W, e);
|
||||
write(*po, W);
|
||||
|
||||
|
||||
|
||||
|
||||
util::string_buffer path;
|
||||
// dominant-baseline="central" is not well supported in IE.
|
||||
// so we add a 0.35 offset to the dy of the tspans
|
||||
@@ -543,18 +674,42 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
}
|
||||
|
||||
// No intersection with bounding box, fail early
|
||||
if (zmin > cut_z || zmax < cut_z) continue;
|
||||
if (variant.which() < 2) {
|
||||
if (zmin > cut_z || zmax < cut_z) continue;
|
||||
}
|
||||
|
||||
emitted = true;
|
||||
|
||||
if (po == nullptr) {
|
||||
po = &start_path(storey, nameElement(storey, o));
|
||||
if (storey) {
|
||||
po = &start_path(storey, data.svg_name);
|
||||
} else {
|
||||
po = &start_path(drawing_name, data.svg_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a horizontal cross section 1 meter above the bottom point of the shape
|
||||
const gp_Pln pln(gp_Pnt(0, 0, cut_z), gp::DZ());
|
||||
gp_Pln pln;
|
||||
if (variant.which() < 2) {
|
||||
pln = gp_Pln(gp_Pnt(0, 0, cut_z), gp::DZ());
|
||||
} else {
|
||||
const auto& section = boost::get<vertical_section>(variant);
|
||||
pln = section.plane;
|
||||
}
|
||||
TopoDS_Shape result = BRepAlgoAPI_Section(subshape, pln);
|
||||
|
||||
if (variant.which() == 2) {
|
||||
gp_Trsf trsf;
|
||||
trsf.SetTransformation(gp::XOY(), pln.Position());
|
||||
result.Move(trsf);
|
||||
|
||||
gp_Trsf trsf_mirror;
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
BRepBuilderAPI_Transform make_transform_mirror(result, trsf_mirror, true);
|
||||
make_transform_mirror.Build();
|
||||
result = make_transform_mirror.Shape();
|
||||
}
|
||||
|
||||
Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape();
|
||||
Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape();
|
||||
{
|
||||
@@ -569,7 +724,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
|
||||
for (int i = 1; i <= wires->Length(); ++i) {
|
||||
const TopoDS_Wire& wire = TopoDS::Wire(wires->Value(i));
|
||||
if (wire.Closed() && (print_space_names_ || print_space_areas_) && o->type() == "IfcSpace") {
|
||||
if (wire.Closed() && (print_space_names_ || print_space_areas_) && data.product->declaration().is("IfcSpace")) {
|
||||
// we explicitly specify the surface here, to later on
|
||||
// simplify the projection from {x,y,z} to {u, v} because
|
||||
// we know we can simply discard z.
|
||||
@@ -636,10 +791,10 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
if (center_point) {
|
||||
std::vector<std::string> labels;
|
||||
if (print_space_names_) {
|
||||
labels.push_back(o->name());
|
||||
labels.push_back(data.ifc_name);
|
||||
}
|
||||
if (print_space_names_ && o->type() == "IfcSpace") {
|
||||
auto attr = o->product()->get("LongName");
|
||||
if (print_space_names_ && data.product->declaration().is("IfcSpace")) {
|
||||
auto attr = data.product->get("LongName");
|
||||
if (!attr->isNull()) {
|
||||
std::string long_name = *attr;
|
||||
if (!long_name.empty()) {
|
||||
@@ -689,7 +844,7 @@ void SvgSerializer::write(const IfcGeom::BRepElement<real_t>* o)
|
||||
}
|
||||
|
||||
if (!emitted) {
|
||||
Logger::Warning("Element not written to SVG due to section heights", o->product());
|
||||
Logger::Warning("Element not written to SVG due to section heights", data.product);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,8 +854,7 @@ void SvgSerializer::setBoundingRectangle(double width, double height) {
|
||||
this->rescale = true;
|
||||
}
|
||||
|
||||
void SvgSerializer::finalize() {
|
||||
|
||||
void SvgSerializer::resize() {
|
||||
if (rescale) {
|
||||
// Scale the resulting image to a bounding rectangle specified by command line arguments
|
||||
const double dx = xmax - xmin;
|
||||
@@ -709,43 +863,132 @@ void SvgSerializer::finalize() {
|
||||
double sc, cx, cy;
|
||||
if (scale_) {
|
||||
sc = (*scale_) * 1000;
|
||||
cx = (xmax + xmin) / 2. * sc - width / 2.;
|
||||
cy = (ymax + ymin) / 2. * sc - height / 2.;
|
||||
cx = (xmax + xmin) / 2. * sc - width * center_x_.get_value_or(0.5);
|
||||
cy = (ymax + ymin) / 2. * sc - height * center_y_.get_value_or(0.5);
|
||||
} else {
|
||||
if (dx / width > dy / height) {
|
||||
sc = width / dx;
|
||||
if (calculated_scale_) {
|
||||
sc = *calculated_scale_;
|
||||
} else {
|
||||
sc = height / dy;
|
||||
if (dx / width > dy / height) {
|
||||
sc = width / dx;
|
||||
} else {
|
||||
sc = height / dy;
|
||||
}
|
||||
calculated_scale_ = sc;
|
||||
}
|
||||
cx = xmin * sc;
|
||||
cy = ymin * sc;
|
||||
}
|
||||
|
||||
{std::vector< boost::shared_ptr<util::string_buffer::float_item> >::const_iterator it;
|
||||
for (it = xcoords.begin(); it != xcoords.end(); ++it) {
|
||||
float_item_list::const_iterator it;
|
||||
for (it = xcoords.begin() + xcoords_begin; it != xcoords.end(); ++it, ++xcoords_begin) {
|
||||
double& v = (*it)->value();
|
||||
v = v * sc - cx;
|
||||
}
|
||||
for (it = ycoords.begin(); it != ycoords.end(); ++it) {
|
||||
for (it = ycoords.begin() + ycoords_begin; it != ycoords.end(); ++it, ++ycoords_begin) {
|
||||
double& v = (*it)->value();
|
||||
v = v * sc - cy;
|
||||
}
|
||||
for (it = radii.begin(); it != radii.end(); ++it) {
|
||||
for (it = radii.begin() + radii_begin; it != radii.end(); ++it, ++radii_begin) {
|
||||
(*it)->value() *= sc;
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
std::multimap<IfcUtil::IfcBaseEntity*, path_object>::const_iterator it;
|
||||
// reset the bounding box, as a subsequent drawing (elevation, section) will be centered, but use the same scale.
|
||||
xmin = +std::numeric_limits<double>::infinity();
|
||||
ymin = +std::numeric_limits<double>::infinity();
|
||||
xmax = -std::numeric_limits<double>::infinity();
|
||||
ymax = -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
IfcUtil::IfcBaseEntity* previous = 0;
|
||||
bool first = true;
|
||||
void SvgSerializer::finalize() {
|
||||
resize();
|
||||
|
||||
if (deferred_section_data_ && deferred_section_data_->size() && element_buffer_.size()) {
|
||||
|
||||
// Draw door arcs only on floor plans.
|
||||
is_floor_plan_ = false;
|
||||
|
||||
for (auto& sd : *deferred_section_data_) {
|
||||
bool use_hlr = false;
|
||||
std::string drawing_name;
|
||||
if (sd.which() == 2) {
|
||||
const auto& section = boost::get<vertical_section>(sd);
|
||||
use_hlr = section.with_projection;
|
||||
drawing_name = section.name;
|
||||
}
|
||||
|
||||
if (use_hlr) {
|
||||
hlr = new HLRBRep_Algo;
|
||||
}
|
||||
|
||||
*section_data_ = { sd };
|
||||
for (auto& e : element_buffer_) {
|
||||
write(e);
|
||||
}
|
||||
|
||||
if (use_hlr) {
|
||||
const auto& section = boost::get<vertical_section>(sd);
|
||||
gp_Ax2 transform = section.plane.Position().Ax2();
|
||||
HLRAlgo_Projector projector(transform);
|
||||
hlr->Projector(projector);
|
||||
|
||||
hlr->Update();
|
||||
hlr->Hide();
|
||||
|
||||
HLRBRep_HLRToShape hlr_shapes(hlr);
|
||||
auto hlr_compound_unmirrored = hlr_shapes.VCompound();
|
||||
|
||||
// Compound 3D curves for mirroring to work
|
||||
ShapeFix_Edge sfe;
|
||||
TopExp_Explorer exp(hlr_compound_unmirrored, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
sfe.FixAddCurve3d(TopoDS::Edge(exp.Current()));
|
||||
}
|
||||
|
||||
// Mirror to match SVG coord system.
|
||||
// @todo this is very wasteful. We better do the Y-mirror in the SVG writing and
|
||||
// not on the TopoDS_Shape input.
|
||||
|
||||
gp_Trsf trsf_mirror;
|
||||
trsf_mirror.SetMirror(gp_Ax2(gp::Origin(), gp::DY()));
|
||||
BRepBuilderAPI_Transform make_transform_mirror(hlr_compound_unmirrored, trsf_mirror, true);
|
||||
make_transform_mirror.Build();
|
||||
auto hlr_compound = make_transform_mirror.Shape();
|
||||
|
||||
exp.Init(hlr_compound, TopAbs_EDGE);
|
||||
BRep_Builder B;
|
||||
auto& po = start_path(drawing_name, "class=\"projection\"");
|
||||
for (; exp.More(); exp.Next()) {
|
||||
TopoDS_Wire w;
|
||||
B.MakeWire(w);
|
||||
B.Add(w, exp.Current());
|
||||
write(po, w);
|
||||
}
|
||||
}
|
||||
|
||||
resize();
|
||||
|
||||
if (use_hlr) {
|
||||
hlr.Nullify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::multimap<drawing_key, path_object, storey_sorter>::const_iterator it;
|
||||
|
||||
boost::optional<drawing_key> previous;
|
||||
for (it = paths.begin(); it != paths.end(); ++it) {
|
||||
if (it->first != previous || first) {
|
||||
if (!first) {
|
||||
if (!previous || it->first != *previous) {
|
||||
if (previous) {
|
||||
svg_file << " </g>\n";
|
||||
}
|
||||
std::ostringstream oss;
|
||||
svg_file << " <g " << nameElement(it->first) << ">\n";
|
||||
if (it->first.first) {
|
||||
svg_file << " <g " << nameElement(it->first.first) << ">\n";
|
||||
} else {
|
||||
svg_file << " <g data-name=\"" << it->first.second << "\" class=\"section\">\n";
|
||||
}
|
||||
}
|
||||
svg_file << " <g " << it->second.first << ">\n";
|
||||
std::vector<util::string_buffer>::const_iterator jt;
|
||||
@@ -754,10 +997,9 @@ void SvgSerializer::finalize() {
|
||||
}
|
||||
svg_file << " </g>\n";
|
||||
previous = it->first;
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
if (previous) {
|
||||
svg_file << " </g>\n";
|
||||
}
|
||||
svg_file << "</svg>" << std::endl;
|
||||
@@ -783,15 +1025,32 @@ void SvgSerializer::writeHeader() {
|
||||
" </defs>\n"
|
||||
" <style type=\"text/css\" >\n"
|
||||
" <![CDATA[\n"
|
||||
" path {\n"
|
||||
" stroke: #222222;\n"
|
||||
" fill: #444444;\n"
|
||||
" }\n"
|
||||
" .IfcDoor path {\n"
|
||||
" fill: none;\n"
|
||||
" }\n"
|
||||
" .IfcSpace path {\n"
|
||||
" fill-opacity: .2;\n"
|
||||
" }\n"
|
||||
" .IfcAnnotation path {\n"
|
||||
" marker-end: url(#arrowend);\n"
|
||||
" marker-start: url(#arrowstart);\n"
|
||||
" }\n";
|
||||
|
||||
if (scale_) {
|
||||
// previously:
|
||||
// (pt) (px) (in) (mm)
|
||||
// approx 12 / 0.75 / 96 * 25.4
|
||||
|
||||
svg_file <<
|
||||
" text {\n" // (pt) (px) (in) (mm)
|
||||
" font-size: 4;\n" // approx 12 / 0.75 / 96 * 25.4
|
||||
" text {\n"
|
||||
" font-size: 2;\n" // (reduced to two).
|
||||
" }\n"
|
||||
" path {\n"
|
||||
" stroke-width: 0.3;\n"
|
||||
" }\n";
|
||||
}
|
||||
|
||||
@@ -803,11 +1062,11 @@ void SvgSerializer::writeHeader() {
|
||||
namespace {
|
||||
std::string nameElement_(const std::vector<std::pair<std::string, std::string> >& attrs) {
|
||||
std::ostringstream oss;
|
||||
for (auto& a : attrs) {
|
||||
// @todo while we're at it might as well implement escaping
|
||||
oss << a.first << "=\"" << a.second << "\" ";
|
||||
}
|
||||
return oss.str();
|
||||
for (auto& a : attrs) {
|
||||
// @todo while we're at it might as well implement escaping
|
||||
oss << a.first << "=\"" << a.second << "\" ";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,7 +1076,7 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* storey, con
|
||||
{"class", elem->type()},
|
||||
{"data-name", elem->name()},
|
||||
{"data-guid", elem->guid()}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
std::string SvgSerializer::idElement(const IfcUtil::IfcBaseEntity* elem) {
|
||||
@@ -843,11 +1102,11 @@ std::string SvgSerializer::nameElement(const IfcUtil::IfcBaseEntity* elem) {
|
||||
}
|
||||
|
||||
return nameElement_({
|
||||
{"id", idElement(elem)},
|
||||
{"class", entity},
|
||||
{"id", idElement(elem)},
|
||||
{"class", entity},
|
||||
{"data-name", ifc_name},
|
||||
{"data-guid", *elem->get("GlobalId")}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SvgSerializer::setFile(IfcParse::IfcFile* f) {
|
||||
@@ -855,9 +1114,9 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) {
|
||||
|
||||
auto storeys = f->instances_by_type("IfcBuildingStorey");
|
||||
if (!storeys || storeys->size() == 0) {
|
||||
|
||||
|
||||
IfcGeom::Kernel kernel(f);
|
||||
|
||||
|
||||
std::vector<const IfcParse::declaration*> to_derive_from;
|
||||
to_derive_from.push_back(f->schema()->declaration_by_name("IfcBuilding"));
|
||||
to_derive_from.push_back(f->schema()->declaration_by_name("IfcSite"));
|
||||
@@ -883,13 +1142,13 @@ void SvgSerializer::setFile(IfcParse::IfcFile* f) {
|
||||
}
|
||||
|
||||
void SvgSerializer::setSectionHeight(double h, IfcUtil::IfcBaseEntity* storey) {
|
||||
section_heights.emplace();
|
||||
section_heights->push_back({ {h, 0.}, storey });
|
||||
section_data_.emplace();
|
||||
section_data_->push_back(horizontal_plan{ storey, h, 0., std::numeric_limits<double>::infinity() });
|
||||
}
|
||||
|
||||
void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
|
||||
with_section_heights_from_storey_ = true;
|
||||
section_heights.emplace();
|
||||
section_data_.emplace();
|
||||
auto storeys = file->instances_by_type("IfcBuildingStorey");
|
||||
const double lu = file->getUnit("LENGTHUNIT").second;
|
||||
if (storeys && storeys->size() > 0) {
|
||||
@@ -903,10 +1162,13 @@ void SvgSerializer::setSectionHeightsFromStoreys(double offset) {
|
||||
Logger::Error(e);
|
||||
continue;
|
||||
}
|
||||
section_heights->push_back({ {elev * lu, offset} , (IfcUtil::IfcBaseEntity*)s });
|
||||
if (!section_data_->empty()) {
|
||||
boost::get<horizontal_plan>(section_data_->back()).next_elevation = elev * lu;
|
||||
}
|
||||
section_data_->push_back(horizontal_plan{ (IfcUtil::IfcBaseEntity*)s, elev * lu, offset, std::numeric_limits<double>::infinity() });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
section_heights->push_back({ {std::numeric_limits<double>::quiet_NaN(), 0.}, nullptr });
|
||||
section_data_->push_back(horizontal_plan_at_element{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,29 @@
|
||||
|
||||
#include "../ifcparse/utils.h"
|
||||
|
||||
#include <HLRBRep_Algo.hxx>
|
||||
#include <HLRBRep_HLRToShape.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
typedef std::pair<IfcUtil::IfcBaseEntity*, std::string> drawing_key;
|
||||
|
||||
struct storey_sorter {
|
||||
bool operator()(IfcUtil::IfcBaseEntity* a, IfcUtil::IfcBaseEntity* b) const {
|
||||
bool operator()(const drawing_key& ad, const drawing_key& bd) const {
|
||||
if (ad.first == nullptr && bd.first != nullptr) {
|
||||
return false;
|
||||
} else if (bd.first == nullptr && ad.first != nullptr) {
|
||||
return true;
|
||||
} else if (ad.first == nullptr && bd.first == nullptr) {
|
||||
return std::less<std::string>()(ad.second, bd.second);
|
||||
}
|
||||
|
||||
auto a = ad.first;
|
||||
auto b = bd.first;
|
||||
|
||||
const bool a_is_storey = a->declaration().is("IfcBuildingStorey");
|
||||
const bool b_is_storey = b->declaration().is("IfcBuildingStorey");
|
||||
if (a_is_storey && b_is_storey) {
|
||||
@@ -66,21 +83,56 @@ struct storey_sorter {
|
||||
}
|
||||
};
|
||||
|
||||
struct horizontal_plan {
|
||||
IfcUtil::IfcBaseEntity* storey;
|
||||
double elevation, offset, next_elevation;
|
||||
};
|
||||
|
||||
struct horizontal_plan_at_element {};
|
||||
|
||||
struct vertical_section {
|
||||
gp_Pln plane;
|
||||
std::string name;
|
||||
bool with_projection;
|
||||
};
|
||||
|
||||
typedef boost::variant<horizontal_plan, horizontal_plan_at_element, vertical_section> section_data;
|
||||
|
||||
struct geometry_data {
|
||||
TopoDS_Shape compound_local;
|
||||
gp_Trsf trsf;
|
||||
IfcUtil::IfcBaseEntity* product;
|
||||
IfcUtil::IfcBaseEntity* storey;
|
||||
double storey_elevation;
|
||||
std::string ifc_name, svg_name;
|
||||
};
|
||||
|
||||
class SvgSerializer : public GeometrySerializer {
|
||||
public:
|
||||
typedef std::pair<std::string, std::vector<util::string_buffer> > path_object;
|
||||
typedef std::vector< boost::shared_ptr<util::string_buffer::float_item> > float_item_list;
|
||||
protected:
|
||||
std::ofstream svg_file;
|
||||
double xmin, ymin, xmax, ymax, width, height;
|
||||
boost::optional<std::vector<std::pair<std::pair<double, double>, IfcUtil::IfcBaseEntity*>>> section_heights;
|
||||
boost::optional<double> scale_;
|
||||
bool rescale, print_space_names_, print_space_areas_, draw_door_arcs_, with_section_heights_from_storey_;
|
||||
std::multimap<IfcUtil::IfcBaseEntity*, path_object, storey_sorter> paths;
|
||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > xcoords;
|
||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > ycoords;
|
||||
std::vector< boost::shared_ptr<util::string_buffer::float_item> > radii;
|
||||
boost::optional<std::vector<section_data>> section_data_;
|
||||
boost::optional<std::vector<section_data>> deferred_section_data_;
|
||||
boost::optional<double> scale_, calculated_scale_, center_x_, center_y_;
|
||||
|
||||
bool rescale, print_space_names_, print_space_areas_, draw_door_arcs_;
|
||||
bool with_section_heights_from_storey_, buffer_elements_;
|
||||
bool is_floor_plan_;
|
||||
|
||||
std::multimap<drawing_key, path_object, storey_sorter> paths;
|
||||
|
||||
float_item_list xcoords, ycoords, radii;
|
||||
size_t xcoords_begin, ycoords_begin, radii_begin;
|
||||
|
||||
boost::optional<std::string> section_ref_, elevation_ref_;
|
||||
IfcParse::IfcFile* file;
|
||||
IfcUtil::IfcBaseEntity* storey_;
|
||||
std::list<geometry_data> element_buffer_;
|
||||
|
||||
Handle(HLRBRep_Algo) hlr;
|
||||
public:
|
||||
SvgSerializer(const std::string& out_filename, const SerializerSettings& settings)
|
||||
: GeometrySerializer(settings)
|
||||
@@ -94,8 +146,13 @@ public:
|
||||
, print_space_names_(false)
|
||||
, print_space_areas_(false)
|
||||
, draw_door_arcs_(false)
|
||||
, buffer_elements_(false)
|
||||
, is_floor_plan_(true)
|
||||
, file(0)
|
||||
, storey_(0)
|
||||
, xcoords_begin(0)
|
||||
, ycoords_begin(0)
|
||||
, radii_begin(0)
|
||||
{}
|
||||
void addXCoordinate(const boost::shared_ptr<util::string_buffer::float_item>& fi) { xcoords.push_back(fi); }
|
||||
void addYCoordinate(const boost::shared_ptr<util::string_buffer::float_item>& fi) { ycoords.push_back(fi); }
|
||||
@@ -106,8 +163,10 @@ public:
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* /*o*/) {}
|
||||
void write(const IfcGeom::BRepElement<real_t>* o);
|
||||
void write(path_object& p, const TopoDS_Wire& wire);
|
||||
void write(const geometry_data& data);
|
||||
path_object& start_path(IfcUtil::IfcBaseEntity* storey, const std::string& id);
|
||||
bool isTesselated() const { return false; }
|
||||
path_object& start_path(const std::string& drawing_name, const std::string& id);
|
||||
bool isTesselated() const { return false; }
|
||||
void finalize();
|
||||
void setUnitNameAndMagnitude(const std::string& /*name*/, float /*magnitude*/) {}
|
||||
void setFile(IfcParse::IfcFile* f);
|
||||
@@ -117,12 +176,28 @@ public:
|
||||
void setPrintSpaceNames(bool b) { print_space_names_ = b; }
|
||||
void setPrintSpaceAreas(bool b) { print_space_areas_ = b; }
|
||||
void setDrawDoorArcs(bool b) { draw_door_arcs_ = b; }
|
||||
void resize();
|
||||
void setSectionRef(const boost::optional<std::string>& s) {
|
||||
section_ref_ = s;
|
||||
buffer_elements_ = true;
|
||||
}
|
||||
void setElevationRef(const boost::optional<std::string>& s) {
|
||||
elevation_ref_ = s;
|
||||
buffer_elements_ = true;
|
||||
}
|
||||
void setScale(double s) { scale_ = s; }
|
||||
void setDrawingCenter(double x, double y) {
|
||||
center_x_ = x; center_y_ = y;
|
||||
}
|
||||
std::string nameElement(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element<real_t>* elem);
|
||||
std::string nameElement(const IfcUtil::IfcBaseEntity* elem);
|
||||
std::string idElement(const IfcUtil::IfcBaseEntity* elem);
|
||||
std::string object_id(const IfcUtil::IfcBaseEntity* storey, const IfcGeom::Element<real_t>* o) {
|
||||
return idElement(storey) + "-" + GeometrySerializer::object_id(o);
|
||||
if (storey) {
|
||||
return idElement(storey) + "-" + GeometrySerializer::object_id(o);
|
||||
} else {
|
||||
return GeometrySerializer::object_id(o);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user