2D annotation now extracts from IFC directly, and considers bounding box intersection with the section plane

This commit is contained in:
Dion Moult
2020-09-01 22:44:28 +10:00
parent 3c8c411dc5
commit 03b8503049
4 changed files with 90 additions and 13 deletions
@@ -72,6 +72,7 @@ except ImportError:
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.selector
import ifcopenshell.util.element
cwd = os.path.dirname(os.path.realpath(__file__))
this_file = os.path.join(cwd, 'cut_ifc.py')
@@ -203,6 +204,8 @@ class IfcCutter:
start_time = time.time()
print('# Get cut polygons')
self.get_cut_polygons()
print('# Get annotation')
self.get_annotation()
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
start_time = time.time()
print('# Get cut polygon metadata')
@@ -680,6 +683,75 @@ class IfcCutter:
else:
self.get_pickled_cut_polygons()
def get_annotation(self):
import mathutils
self.annotation_objs = []
settings_2d = ifcopenshell.geom.settings()
settings_2d.set(settings_2d.INCLUDE_CURVES, True)
settings_py = ifcopenshell.geom.settings()
settings_py.set(settings_py.USE_PYTHON_OPENCASCADE, True)
for ifc_file in self.ifc_files.values():
for element in ifc_file.by_type('IfcElement'):
annotation_representation = None
box_representation = None
for representation in element.Representation.Representations:
if representation.ContextOfItems.ContextType == 'Plan' \
and representation.ContextOfItems.ContextIdentifier == 'Annotation':
annotation_representation = representation
elif representation.ContextOfItems.ContextType == 'Model' \
and representation.ContextOfItems.ContextIdentifier == 'Box':
box_representation = representation
if not annotation_representation or not box_representation:
continue
# This is bad code. See bug #85 to make it slightly less bad.
# Effectively if the bbox does not intersect with the camera
# plane, then we should "continue" and not process the 2D
# wireframe. This approach works but is not very smart.
for subelement in ifc_file.traverse(box_representation):
if subelement.is_a('IfcBoundingBox'):
block = ifc_file.createIfcBlock(
ifc_file.createIfcAxis2Placement3D(subelement.Corner, None, None),
subelement.XDim,
subelement.YDim,
subelement.ZDim
)
for inverse in ifc_file.get_inverse(subelement):
ifcopenshell.util.element.replace_attribute(inverse, subelement, block)
element.Representation.Representations = [box_representation]
shape = ifcopenshell.geom.create_shape(settings_py, element)
section = BRepAlgoAPI.BRepAlgoAPI_Section(self.section_box['face'], shape.geometry).Shape()
section_edges = get_booleaned_edges(section)
if len(section_edges) <= 0:
# The bounding box of the annotation object does not
# intersect with the camera plane, so don't bother drawing
# the annotation
continue
# Monkey patch - see bug #771.
element.Representation.Representations = [annotation_representation]
shape = ifcopenshell.geom.create_shape(settings_2d, element)
if hasattr(shape, 'geometry'):
geometry = shape.geometry
else:
geometry = shape
e = geometry.edges
v = geometry.verts
m = shape.transformation.matrix.data
mat = mathutils.Matrix(([m[0], m[1], m[2], 0],
[m[3], m[4], m[5], 0],
[m[6], m[7], m[8], 0],
[m[9], m[10], m[11], 1]))
mat.transpose()
self.annotation_objs.append({
'raw': element,
'classes': self.get_classes(element, 'annotation'),
'edges': [[e[i], e[i + 1]] for i in range(0, len(e), 2)],
'vertices': [mat @ mathutils.Vector((v[i], v[i + 1], v[i + 2])) for i in range(0, len(v), 3)]
})
def get_cut_polygon_metadata(self):
if not self.should_extract:
if os.path.isfile(self.metadata_pickle_file):
@@ -288,11 +288,11 @@ class IfcImporter():
self.time = time.time()
def execute(self):
self.profile_code('Starting import process')
self.load_diff()
self.profile_code('Load diff')
self.purge_diff()
self.profile_code('Purge diffs')
self.profile_code('Starting import process')
self.load_existing_rooted_elements()
self.profile_code('Load existing rooted elements')
self.cache_file()
@@ -2174,18 +2174,6 @@ class CutSection(bpy.types.Operator):
elif obj.type == 'FONT':
ifc_cutter.text_objs.append(obj)
# TODO: this should be detected from the IFC, not from Blender, and
# should use a bbox calculation to determine whether it is shown
for obj in bpy.context.visible_objects:
for subcontext in obj.BIMObjectProperties.representation_contexts:
if subcontext.context == 'Plan' \
and subcontext.name == 'Annotation' \
and subcontext.target_view == 'PLAN_VIEW' \
and '/' in obj.data.name:
data = bpy.data.meshes.get('Plan/Annotation/PLAN_VIEW/{}'.format(obj.data.name.split('/')[-1]))
if data:
ifc_cutter.solid_objs.append((obj, data))
ifc_cutter.section_box = {
'projection': tuple(projection),
'x_axis': tuple(x_axis),
@@ -157,6 +157,8 @@ class SvgWriter():
'dominant-baseline': 'middle'
}))
self.draw_ifc_annotation()
for obj_data in self.ifc_cutter.hidden_objs:
self.draw_line_annotation(obj_data, ['hidden'])
@@ -244,6 +246,21 @@ class SvgWriter():
self.draw_text_annotations()
def draw_ifc_annotation(self):
x_offset = self.raw_width / 2
y_offset = self.raw_height / 2
for annotation in self.ifc_cutter.annotation_objs:
for edge in annotation['edges']:
v0_global = annotation['vertices'][edge[0]]
v1_global = annotation['vertices'][edge[1]]
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(annotation['classes'])))
def draw_line_annotation(self, obj_data, classes):
# TODO: properly scope these offsets
x_offset = self.raw_width / 2