mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 10:33:20 +00:00
2D annotation now extracts from IFC directly, and considers bounding box intersection with the section plane
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user