diff --git a/src/blenderbim/blenderbim/bim/data/pset/EPset_Drawing.ifc b/src/blenderbim/blenderbim/bim/data/pset/EPset_Drawing.ifc index f0728a6340..6f1bc493bd 100644 --- a/src/blenderbim/blenderbim/bim/data/pset/EPset_Drawing.ifc +++ b/src/blenderbim/blenderbim/bim/data/pset/EPset_Drawing.ifc @@ -9,6 +9,7 @@ DATA; #2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); +#4=IFCSIMPLEPROPERTYTEMPLATE('2T$a4OFsv2LeD5JeBKEV4f',$,'IsNTS','Whether or not the scale is intended to be significant',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #5=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #6=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #7=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); diff --git a/src/blenderbim/blenderbim/bim/module/drawing/annotation.py b/src/blenderbim/blenderbim/bim/module/drawing/annotation.py index ac6130e604..8b6819ea68 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/annotation.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/annotation.py @@ -63,12 +63,7 @@ class Annotator: font_size = 1.6 / 1000 font_size *= float(text_obj.data.BIMTextProperties.font_size) - if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": - human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|") - else: - human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|") - numerator, denominator = fraction.split("/") - font_size /= float(numerator) / float(denominator) + font_size /= tool.Drawing.get_scale_ratio(tool.Drawing.get_diagram_scale(camera)["Scale"]) text_obj.data.size = font_size diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py index 3a18f10166..a6a26c8f39 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py @@ -171,14 +171,6 @@ class BaseDecorator: def is_landscape(render): return render.resolution_x > render.resolution_y - def get_scale(camera): - if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM": - human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|") - else: - human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|") - numerator, denominator = fraction.split("/") - return float(numerator) / float(denominator) - camera = bpy.context.scene.camera render = bpy.context.scene.render if is_landscape(render): @@ -186,7 +178,8 @@ class BaseDecorator: else: camera_width_model = camera.data.ortho_scale / render.resolution_y * render.resolution_x - camera_width_mm = get_scale(camera) * camera_width_model + scale = tool.Drawing.get_scale_ratio(tool.Drawing.get_diagram_scale(camera)["Scale"]) + camera_width_mm = scale * camera_width_model return camera_width_mm def camera_zoom_to_factor(self, zoom): diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 52b3a9631e..2414abd83c 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -947,53 +947,13 @@ class CreateDrawing(bpy.types.Operator): return svg_path def get_scale(self, context): - camera_props = self.camera.data.BIMCameraProperties - if camera_props.diagram_scale == "CUSTOM": - if context.scene.unit_settings.system == "IMPERIAL": - if ( - camera_props.custom_diagram_scale_input1[-1] == "'" - or camera_props.custom_diagram_scale_input1[-1] == '"' - ): - camera_props.custom_diagram_scale_input1 = camera_props.custom_diagram_scale_input1[:-1] - if camera_props.custom_diagram_scale_input2[-1] == "'": - camera_props.custom_diagram_scale_input2 = camera_props.custom_diagram_scale_input2[:-1] - if ( - camera_props.custom_diagram_scale_input2[1:] == "'-0\"" - or camera_props.custom_diagram_scale_input2[1:] == "' 0\"" - ): - camera_props.custom_diagram_scale_input2 = camera_props.custom_diagram_scale_input2[0] - if "/" in camera_props.custom_diagram_scale_input1: - if " " in camera_props.custom_diagram_scale_input1: - n1, n2 = camera_props.custom_diagram_scale_input1.split(" ") - n2_1, n2_2 = n2.split("/") - numerator = int(n1) + (int(n2_1) / int(n2_2)) - elif "-" in camera_props.custom_diagram_scale_input1: - n1, n2 = camera_props.custom_diagram_scale_input1.split("-") - n2_1, n2_2 = n2.split("/") - numerator = int(n1) + (int(n2_1) / int(n2_2)) - else: - n1, n2 = camera_props.custom_diagram_scale_input1.split("/") - numerator = int(n1) / int(n2) - else: - numerator = 1 - denominator = (int(camera_props.custom_diagram_scale_input2) / numerator) * 12 - scale = "1" + "/" + str(denominator) - human_scale = camera_props.custom_diagram_scale_input1 + "=" + camera_props.custom_diagram_scale_input2 - else: - scale = camera_props.custom_diagram_scale_input1 + "/" + camera_props.custom_diagram_scale_input2 - human_scale = camera_props.custom_diagram_scale_input1 + ":" + camera_props.custom_diagram_scale_input2 + diagram_scale = tool.Drawing.get_diagram_scale(self.camera) + self.human_scale = diagram_scale["HumanScale"] + self.scale = tool.Drawing.get_scale_ratio(diagram_scale["Scale"]) - camera_props.custom_diagram_scale = human_scale + "|" + scale - self.human_scale, fraction = camera_props.custom_diagram_scale.split("|") - else: - self.human_scale, fraction = camera_props.diagram_scale.split("|") - - if camera_props.is_nts: + if ifcopenshell.util.element.get_pset(self.camera_element, "EPset_Drawing", "IsNTS"): self.human_scale = "NTS" - numerator, denominator = fraction.split("/") - self.scale = float(numerator) / float(denominator) - def is_landscape(self, render): return render.resolution_x > render.resolution_y diff --git a/src/blenderbim/blenderbim/bim/module/drawing/prop.py b/src/blenderbim/blenderbim/bim/module/drawing/prop.py index a89e83ffff..257992569e 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/prop.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/prop.py @@ -65,12 +65,27 @@ def get_location_hint(self, context): def update_diagram_scale(self, context): - scale = self.diagram_scale - if scale == "CUSTOM": - scale = self.custom_diagram_scale - if "|" not in scale: + try: + element = ( + tool.Ifc.get() + .by_id(self.id_data.BIMMeshProperties.ifc_definition_id) + .OfProductRepresentation[0] + .ShapeOfProduct[0] + ) + except: return - human_scale, scale = scale.split("|") + diagram_scale = tool.Drawing.get_diagram_scale(tool.Ifc.get_object(element)) + if not diagram_scale: + return + pset = ifcopenshell.util.element.get_pset(element, "EPset_Drawing") + if pset: + pset = tool.Ifc.get().by_id(pset["id"]) + else: + pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing") + ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=diagram_scale) + + +def update_is_nts(self, context): try: element = ( tool.Ifc.get() @@ -85,9 +100,7 @@ def update_diagram_scale(self, context): pset = tool.Ifc.get().by_id(pset["id"]) else: pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing") - ifcopenshell.api.run( - "pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Scale": scale, "HumanScale": human_scale} - ) + ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"IsNTS": self.is_nts}) def get_diagram_scales(self, context): @@ -351,12 +364,11 @@ class BIMCameraProperties(PropertyGroup): representation: StringProperty(name="Representation") view_name: StringProperty(name="View Name") diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale", update=update_diagram_scale) - custom_diagram_scale: StringProperty(name="Custom Scale", update=update_diagram_scale) - custom_diagram_scale_input1: StringProperty(name="Custom Scale Input 1", update=update_diagram_scale) - custom_diagram_scale_input2: StringProperty(name="Custom Scale Input 2", update=update_diagram_scale) + custom_scale_numerator: bpy.props.StringProperty(default="1", update=update_diagram_scale) + custom_scale_denominator: bpy.props.StringProperty(default="100", update=update_diagram_scale) raster_x: IntProperty(name="Raster X", default=1000) raster_y: IntProperty(name="Raster Y", default=1000) - is_nts: BoolProperty(name="Is NTS") + is_nts: BoolProperty(name="Is NTS", update=update_is_nts) active_drawing_style_index: IntProperty(name="Active Drawing Style Index") # For now, this JSON dump are all the parameters that determine a camera's "Block representation" diff --git a/src/blenderbim/blenderbim/bim/module/drawing/ui.py b/src/blenderbim/blenderbim/bim/module/drawing/ui.py index 24d10c8add..0709261a9a 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/ui.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/ui.py @@ -71,28 +71,19 @@ class BIM_PT_camera(Panel): row = layout.row() row.prop(dprops, "should_extract") - row = layout.row() - row.prop(props, "is_nts") - - row = layout.row() - row.operator("bim.resize_text") - row = layout.row() row.prop(props, "raster_x") row = layout.row() row.prop(props, "raster_y") - row = layout.row() + row = layout.row(align=True) row.prop(props, "diagram_scale") + row.prop(props, "is_nts", text="", icon="MOD_EDGESPLIT") + if props.diagram_scale == "CUSTOM": row = layout.row(align=True) - row.prop(props, "custom_diagram_scale_input1", text="Custom Scale") - if context.scene.unit_settings.system == "IMPERIAL": - separator = " =" - else: - separator = " :" - row.label(text=separator) - row.prop(props, "custom_diagram_scale_input2", text="") + row.prop(props, "custom_scale_numerator", text="Custom Scale") + row.prop(props, "custom_scale_denominator", text="") row = layout.row(align=True) row.operator("bim.create_drawing", text="Create Drawing", icon="OUTPUT") diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index c10abf326f..c68808e85d 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -20,6 +20,7 @@ import os import re import bpy import math +import lark import bmesh import shutil import logging @@ -35,11 +36,10 @@ import blenderbim.bim.module.drawing.sheeter as sheeter import blenderbim.bim.module.drawing.scheduler as scheduler import blenderbim.bim.module.drawing.annotation as annotation import blenderbim.bim.module.drawing.helper as helper - from blenderbim.bim.module.drawing.data import FONT_SIZES, DecoratorData from blenderbim.bim.module.drawing.prop import get_diagram_scales, BOX_ALIGNMENT_POSITIONS, ANNOTATION_TYPES_DATA - from mathutils import Vector +from fractions import Fraction import collections @@ -580,13 +580,20 @@ class Drawing(blenderbim.core.tool.Drawing): camera.BIMCameraProperties.diagram_scale = valid_scales[0] else: camera.BIMCameraProperties.diagram_scale = "CUSTOM" - camera.BIMCameraProperties.custom_diagram_scale = pset["HumanScale"] + "|" + pset["Scale"] + if ":" in pset["HumanScale"]: + numerator, denominator = pset["HumanScale"].split(":") + else: + numerator, denominator = pset["HumanScale"].split("=") + camera.BIMCameraProperties.custom_scale_numerator = numerator + camera.BIMCameraProperties.custom_scale_denominator = denominator if "HasUnderlay" in pset: camera.BIMCameraProperties.has_underlay = pset["HasUnderlay"] if "HasLinework" in pset: camera.BIMCameraProperties.has_linework = pset["HasLinework"] if "HasAnnotation" in pset: camera.BIMCameraProperties.has_annotation = pset["HasAnnotation"] + if "IsNTS" in pset: + camera.BIMCameraProperties.is_nts = pset["IsNTS"] tool.Loader.link_mesh(shape, camera) @@ -1330,7 +1337,8 @@ class Drawing(blenderbim.core.tool.Drawing): @classmethod def get_drawing_human_scale(cls, drawing): - return ifcopenshell.util.element.get_psets(drawing)["EPset_Drawing"].get("HumanScale", "NTS") + pset = ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing") or {} + return "NTS" if pset.get("IsNTS", False) else pset.get("HumanScale", "NTS") @classmethod def get_drawing_metadata(cls, drawing): @@ -1610,3 +1618,65 @@ class Drawing(blenderbim.core.tool.Drawing): bm.free() return verts, edges + + @classmethod + def get_scale_ratio(cls, scale): + numerator, denominator = scale.split("/") + return float(numerator) / float(denominator) + + @classmethod + def get_diagram_scale(cls, obj): + props = obj.data.BIMCameraProperties + scale = props.diagram_scale + if scale != "CUSTOM": + human_scale, scale = scale.split("|") + return {"HumanScale": human_scale, "Scale": scale} + numerator_string = props.custom_scale_numerator + denominator_string = props.custom_scale_denominator + numerator = tool.Drawing.convert_scale_string(numerator_string) + denominator = tool.Drawing.convert_scale_string(denominator_string) + if not numerator or not denominator: + return + scale = str(Fraction(numerator / denominator).limit_denominator(1000)) # Any ratio >1000 is stupid. + if "'" in scale or '"' in scale: + human_separator = "=" # Imperial scales use "=", like 1" = 1' - 0" + # If for some crazy reason we mix metric and imperial, assume metric is SI units, like 1m = 1' + if "'" not in numerator_string and '"' not in numerator_string: + numerator_string += "m" + if "'" not in denominator_string and '"' not in denominator_string: + denominator_string += "m" + else: + human_separator = ":" # Metric scales use ":", like 1:100 + human_scale = f"{numerator_string}{human_separator}{denominator_string}" + return {"HumanScale": human_scale, "Scale": scale} + + @classmethod + def convert_scale_string(cls, value): + try: + return float(value) + except: + pass # Perhaps it's imperial? + l = lark.Lark( + """start: feet? "-"? inches? + feet: NUMBER? "-"? fraction? "'" + inches: NUMBER? "-"? fraction? "\\"" + fraction: NUMBER "/" NUMBER + %import common.NUMBER + %import common.WS + %ignore WS // Disregard spaces in text + """ + ) + + try: + start = l.parse(value) + except: + return 0 + result = 0 + for dimension in start.children: + factor = 12 if dimension.data == "feet" else 1 + for child in dimension.children: + if getattr(child, "data", None) == "fraction": + result += (float(child.children[0]) / float(child.children[1])) * factor + else: + result += float(child) * factor + return result * 0.0254