mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-01 20:49:44 +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
|
||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||||
this_file = os.path.join(cwd, 'cut_ifc.py')
|
this_file = os.path.join(cwd, 'cut_ifc.py')
|
||||||
@@ -203,6 +204,8 @@ class IfcCutter:
|
|||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
print('# Get cut polygons')
|
print('# Get cut polygons')
|
||||||
self.get_cut_polygons()
|
self.get_cut_polygons()
|
||||||
|
print('# Get annotation')
|
||||||
|
self.get_annotation()
|
||||||
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
print('# Get cut polygon metadata')
|
print('# Get cut polygon metadata')
|
||||||
@@ -680,6 +683,75 @@ class IfcCutter:
|
|||||||
else:
|
else:
|
||||||
self.get_pickled_cut_polygons()
|
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):
|
def get_cut_polygon_metadata(self):
|
||||||
if not self.should_extract:
|
if not self.should_extract:
|
||||||
if os.path.isfile(self.metadata_pickle_file):
|
if os.path.isfile(self.metadata_pickle_file):
|
||||||
|
|||||||
@@ -288,11 +288,11 @@ class IfcImporter():
|
|||||||
self.time = time.time()
|
self.time = time.time()
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
self.profile_code('Starting import process')
|
||||||
self.load_diff()
|
self.load_diff()
|
||||||
self.profile_code('Load diff')
|
self.profile_code('Load diff')
|
||||||
self.purge_diff()
|
self.purge_diff()
|
||||||
self.profile_code('Purge diffs')
|
self.profile_code('Purge diffs')
|
||||||
self.profile_code('Starting import process')
|
|
||||||
self.load_existing_rooted_elements()
|
self.load_existing_rooted_elements()
|
||||||
self.profile_code('Load existing rooted elements')
|
self.profile_code('Load existing rooted elements')
|
||||||
self.cache_file()
|
self.cache_file()
|
||||||
|
|||||||
@@ -2174,18 +2174,6 @@ class CutSection(bpy.types.Operator):
|
|||||||
elif obj.type == 'FONT':
|
elif obj.type == 'FONT':
|
||||||
ifc_cutter.text_objs.append(obj)
|
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 = {
|
ifc_cutter.section_box = {
|
||||||
'projection': tuple(projection),
|
'projection': tuple(projection),
|
||||||
'x_axis': tuple(x_axis),
|
'x_axis': tuple(x_axis),
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ class SvgWriter():
|
|||||||
'dominant-baseline': 'middle'
|
'dominant-baseline': 'middle'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
self.draw_ifc_annotation()
|
||||||
|
|
||||||
for obj_data in self.ifc_cutter.hidden_objs:
|
for obj_data in self.ifc_cutter.hidden_objs:
|
||||||
self.draw_line_annotation(obj_data, ['hidden'])
|
self.draw_line_annotation(obj_data, ['hidden'])
|
||||||
|
|
||||||
@@ -244,6 +246,21 @@ class SvgWriter():
|
|||||||
|
|
||||||
self.draw_text_annotations()
|
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):
|
def draw_line_annotation(self, obj_data, classes):
|
||||||
# TODO: properly scope these offsets
|
# TODO: properly scope these offsets
|
||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
|
|||||||
Reference in New Issue
Block a user