Text annotation can now extract dynamic variables from IFC data

This commit is contained in:
Dion Moult
2020-05-04 12:20:34 +10:00
parent dce5c40007
commit 3ce71e855f
6 changed files with 109 additions and 17 deletions
@@ -122,7 +122,10 @@ if bpy is not None:
operator.SelectDocIfcFile,
operator.AddAnnotation,
operator.ResizeText,
operator.AddVariable,
operator.RemoveVariable,
prop.StrProperty,
prop.Variable,
prop.Classification,
prop.ClassificationReference,
prop.ClassificationView,
@@ -4,6 +4,17 @@ from mathutils import Vector
class Annotator:
@staticmethod
def get_svg_text_size(size):
sizes = {
'1.8': '2.97',
'2.5': '4.13',
'3.5': '5.78',
'5.0': '8.25',
'7.0': '11.55',
}
return float(sizes[str(size)])
@staticmethod
def add_text():
curve = bpy.data.curves.new(type='FONT', name='Text')
+46 -11
View File
@@ -3,8 +3,10 @@ import math
import time
import numpy
import pickle
import pystache
import sys
from pathlib import Path
from . import annotation
mathutils = sys.modules.get('mathutils')
if mathutils is not None:
from mathutils import Vector
@@ -121,17 +123,21 @@ def do_cut(process_data):
class IfcCutter:
def __init__(self):
self.ifc_attribute_extractor = None
self.product_shapes = []
self.background_elements = []
self.cut_polygons = []
self.template_variables = {}
self.data_dir = ''
self.ifc_files = []
self.unit = None
self.resolved_pixels = set()
self.should_get_background = False
self.shapes_pickle_file = 'shapes.pickle'
self.text_pickle_file = 'text.pickle'
self.cut_pickle_file = 'cut.pickle'
self.should_recut = True
self.should_extract = True
self.diagram_name = None
self.background_image = None
self.section_box = {
@@ -152,6 +158,10 @@ class IfcCutter:
self.load_ifc_files()
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
start_time = time.time()
print('# Extract template variables')
self.get_template_variables()
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
start_time = time.time()
print('# Get product shapes')
self.get_product_shapes()
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
@@ -185,7 +195,7 @@ class IfcCutter:
print('# Timer logged at {:.2f} seconds'.format(time.time() - start_time))
def load_ifc_files(self):
if not self.should_recut:
if not self.should_recut and not self.should_extract:
return
loaded_files = []
@@ -195,6 +205,29 @@ class IfcCutter:
loaded_files.append(ifcopenshell.open(ifc_file))
self.ifc_files = loaded_files
def get_template_variables(self):
if not self.should_extract:
if os.path.isfile(self.text_pickle_file):
with open(self.text_pickle_file, 'rb') as text_file:
self.template_variables = pickle.load(text_file)
return
data = {}
for text_obj in self.text_objs:
text_obj_data = {}
text_body = text_obj.data.body
for variable in text_obj.data.BIMTextProperties.variables:
element = self.get_ifc_element(variable.global_id)
if element:
text_obj_data[variable.name] = self.ifc_attribute_extractor.get_element_key(element, variable.prop_key)
if text_obj_data:
data[text_obj.name] = text_obj_data
with open(self.text_pickle_file, 'wb') as text_file:
pickle.dump(data, text_file, protocol=pickle.HIGHEST_PROTOCOL)
self.template_variables = data
def get_product_shapes(self):
if not self.should_recut:
return
@@ -797,14 +830,14 @@ class SvgWriter():
line['marker-end'] = 'url(#grid-marker)'
line['stroke-dasharray'] = '12.5, 3, 3, 3'
self.svg.add(self.svg.text(grid_obj.name.split('/')[1], insert=tuple(start * self.scale), **{
'font-size': '8.25', # 5
'font-size': annotation.Annotator.get_svg_text_size(5.0),
'font-family': 'OpenGost Type B TT',
'text-anchor': 'middle',
'alignment-baseline': 'middle',
'dominant-baseline': 'middle'
}))
self.svg.add(self.svg.text(grid_obj.name.split('/')[1], insert=tuple(end * self.scale), **{
'font-size': '8.25', # 5
'font-size': annotation.Annotator.get_svg_text_size(5.0),
'font-family': 'OpenGost Type B TT',
'text-anchor': 'middle',
'alignment-baseline': 'middle',
@@ -859,7 +892,7 @@ class SvgWriter():
else:
text_anchor = 'start'
self.svg.add(self.svg.text('RL +{:.3f}m'.format(rl), insert=tuple(text_position), **{
'font-size': '4.13', # 2.5
'font-size': annotation.Annotator.get_svg_text_size(2.5),
'font-family': 'OpenGost Type B TT',
'text-anchor': text_anchor,
'alignment-baseline': 'baseline',
@@ -886,7 +919,7 @@ class SvgWriter():
# TODO: unhardcode m unit
rl = (matrix_world @ points[0].co.xyz).z
self.svg.add(self.svg.text('RL +{:.3f}m'.format(rl), insert=tuple(text_position), **{
'font-size': '4.13', # 2.5
'font-size': annotation.Annotator.get_svg_text_size(2.5),
'font-family': 'OpenGost Type B TT',
'text-anchor': 'start',
'alignment-baseline': 'baseline',
@@ -906,7 +939,7 @@ class SvgWriter():
path['marker-start'] = 'url(#stair-marker-start)'
path['marker-end'] = 'url(#stair-marker-end)'
self.svg.add(self.svg.text('UP', insert=tuple(text_position), **{
'font-size': '4.13', # 2.5
'font-size': annotation.Annotator.get_svg_text_size(2.5),
'font-family': 'OpenGost Type B TT',
'text-anchor': 'middle',
'alignment-baseline': 'middle',
@@ -944,12 +977,16 @@ class SvgWriter():
else:
alignment_baseline = 'baseline'
for line_number, text_line in enumerate(text_obj.data.body.split('\n')):
text_body = text_obj.data.body
if text_obj.name in self.ifc_cutter.template_variables:
text_body = pystache.render(text_body, self.ifc_cutter.template_variables[text_obj.name])
for line_number, text_line in enumerate(text_body.split('\n')):
self.svg.add(self.svg.text(
text_line,
insert=tuple((text_position * self.scale) + Vector((0, 3.5*line_number))),
**{
'font-size': '4.13', # 2.5
'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,
@@ -990,8 +1027,6 @@ class SvgWriter():
end=tuple(end * self.scale), class_=' '.join(classes)))
line['marker-start'] = 'url(#dimension-marker-start)'
line['marker-end'] = 'url(#dimension-marker-end)'
# Standard font sizes 1.8, 2.5, 3.5, 5, 7
# Equivalent for OpenGost Type B: 2.97, 4.13, 5.78, 8.25, 11.55
if text_override is not None:
text = text_override
else:
@@ -1002,7 +1037,7 @@ class SvgWriter():
text_position.x,
text_position.y
),
'font-size': '4.13', # 2.5
'font-size': annotation.Annotator.get_svg_text_size(2.5),
'font-family': 'OpenGost Type B TT',
'text-anchor': 'middle'
}))
@@ -1575,6 +1575,8 @@ class CutSection(bpy.types.Operator):
y_axis = camera.matrix_world.to_quaternion() @ Vector((0, -1, 0))
top_left_corner = location - (width / 2 * x_axis) - (height / 2 * y_axis)
ifc_cutter = cut_ifc.IfcCutter()
import ifccsv
ifc_cutter.ifc_attribute_extractor = ifccsv.IfcAttributeExtractor
ifc_cutter.ifc_files = [i.name for i in bpy.context.scene.DocProperties.ifc_files]
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
ifc_cutter.diagram_name = self.diagram_name
@@ -1625,6 +1627,7 @@ class CutSection(bpy.types.Operator):
ifc_cutter.shapes_pickle_file = os.path.join(ifc_cutter.data_dir, 'shapes.pickle')
ifc_cutter.cut_pickle_file = os.path.join(ifc_cutter.data_dir, '{}-cut.pickle'.format(self.diagram_name))
ifc_cutter.should_recut = bpy.context.scene.DocProperties.should_recut
ifc_cutter.should_extract = bpy.context.scene.DocProperties.should_extract
svg_writer = cut_ifc.SvgWriter(ifc_cutter)
numerator, denominator = camera.data.BIMCameraProperties.diagram_scale.split(':')
if camera.data.BIMCameraProperties.is_nts:
@@ -2421,3 +2424,22 @@ class ResizeText(bpy.types.Operator):
if isinstance(obj.data, bpy.types.TextCurve):
annotation.Annotator.resize_text(obj)
return {'FINISHED'}
class AddVariable(bpy.types.Operator):
bl_idname = 'bim.add_variable'
bl_label = 'Add Variable'
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.add()
return {'FINISHED'}
class RemoveVariable(bpy.types.Operator):
bl_idname = 'bim.remove_variable'
bl_label = 'Remove Variable'
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.remove(self.index)
return {'FINISHED'}
+13 -5
View File
@@ -375,6 +375,12 @@ class StrProperty(PropertyGroup):
pass
class Variable(PropertyGroup):
name: StringProperty(name="Name")
global_id: StringProperty(name="Global ID")
prop_key: StringProperty(name="Property Key")
class Subcontext(PropertyGroup):
name: StringProperty(name='Name')
target_view: StringProperty(name='Target View')
@@ -383,6 +389,7 @@ class Subcontext(PropertyGroup):
class DocProperties(PropertyGroup):
should_recut: BoolProperty(name="Should Recut", default=True)
should_render: BoolProperty(name="Should Render", default=True)
should_extract: BoolProperty(name="Should Extract", default=True)
view_name: StringProperty(name="View Name")
available_views: EnumProperty(items=getViews, name="Available Views")
sheet_name: StringProperty(name="Sheet Name", update=refreshSheets)
@@ -398,16 +405,17 @@ class BIMCameraProperties(PropertyGroup):
class BIMTextProperties(PropertyGroup):
font_size: EnumProperty(items=[
('1.8', '1.8', ''),
('2.5', '2.5', ''),
('3.5', '3.5', ''),
('5.0', '5.0', ''),
('7.0', '7.0', ''),
('1.8', '1.8 - Small', ''),
('2.5', '2.5 - Regular', ''),
('3.5', '3.5 - Large', ''),
('5.0', '5.0 - Header', ''),
('7.0', '7.0 - Title', ''),
], update=refreshFontSize, name='Font Size')
symbol: EnumProperty(items=[
('None', 'None', ''),
('rectangle-tag', 'Rectangle Tag', ''),
], update=refreshFontSize, name='Symbol')
variables: CollectionProperty(name='Variables', type=Variable)
class DocumentInformation(PropertyGroup):
+14 -1
View File
@@ -626,9 +626,10 @@ class BIM_PT_camera(Panel):
row = layout.row()
row.prop(dprops, 'should_recut')
row = layout.row()
row.prop(dprops, 'should_render')
row = layout.row()
row.prop(dprops, 'should_extract')
row = layout.row()
row.prop(props, 'is_nts')
@@ -663,6 +664,18 @@ class BIM_PT_text(Panel):
row = layout.row()
row.prop(props, 'symbol')
row = layout.row()
row.operator('bim.add_variable')
for index, variable in enumerate(props.variables):
row = layout.row(align=True)
row.prop(variable, 'name')
row.operator('bim.remove_variable', icon='X', text='').index = index
row = layout.row()
row.prop(variable, 'global_id')
row = layout.row()
row.prop(variable, 'prop_key')
class BIM_PT_owner(Panel):
bl_label = "Owner History"