IFC text - box alignment in viewport

This commit is contained in:
Andrej730
2023-03-17 19:00:53 +05:00
parent a669c194cf
commit 32e47d03d4
4 changed files with 89 additions and 46 deletions
@@ -193,7 +193,7 @@ class DecoratorData:
return display_data
@classmethod
def get_ifc_text_font_size(cls, obj):
def get_ifc_text_data(cls, obj):
"""returns font size in mm for current ifc text object"""
result = cls.data.get(obj.name, None)
if result is not None:
@@ -203,21 +203,25 @@ class DecoratorData:
if not element:
return
if obj.BIMTextProperties.is_editing:
font_size = float(obj.BIMTextProperties.font_size)
props = obj.BIMTextProperties
# getting font size
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
# use `regular` as default
if classes:
classes_split = classes.split()
# prioritize smaller font sizes just like in svg
font_size_type = next(
(font_size_type for font_size_type in FONT_SIZES if font_size_type in classes_split), "regular"
)
else:
classes = ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "Classes")
font_size_type = "regular"
font_size = FONT_SIZES[font_size_type]
# use `regular` as default
if classes:
classes_split = classes.split()
# prioritize smaller font sizes just like in svg
font_size_type = next(
(font_size_type for font_size_type in FONT_SIZES if font_size_type in classes_split), "regular"
)
else:
font_size_type = "regular"
# other attributes
literal = tool.Drawing.get_text_literal(obj)
box_alignment = literal.BoxAlignment
text = props.value
font_size = FONT_SIZES[font_size_type]
cls.data[obj.name] = font_size
return font_size
text_data = {"text": text, "font_size": font_size, "box_alignment": box_alignment}
cls.data[obj.name] = text_data
return text_data
@@ -379,7 +379,19 @@ class BaseDecorator:
batch.draw(self.shader)
def draw_label(self, context, text, pos, dir, gap=4, center=True, vcenter=False, font_size=2.5, line_no=0):
def draw_label(
self,
context,
text,
pos,
dir,
gap=4,
center=True,
vcenter=False,
font_size_mm=2.5,
line_no=0,
box_alignment=None,
):
"""Draw text label
Args:
@@ -409,26 +421,42 @@ class BaseDecorator:
# In particular it works only for the OpenGOST font and produces a 2.5mm font size.
# It probably should be dynamically calculated using system.dpi or something.
# font_size = 16 <-- this is a good default
font_size = int(0.004118616 * mm_to_px) * font_size / 2.5
pos = pos - Vector((0, line_no * font_size))
# TODO: need to synchronize it better with svg
font_size_px = int(0.004118616 * mm_to_px) * font_size_mm / 2.5
pos = pos - Vector((0, line_no * font_size_px))
blf.size(font_id, font_size, dpi)
blf.size(font_id, font_size_px, dpi)
w, h = 0, 0
if center or vcenter:
if box_alignment or center or vcenter:
w, h = blf.dimensions(font_id, text)
if center:
# horizontal centering
pos -= Vector((cos, sin)) * w * 0.5
if box_alignment:
# TODO: need to work out the rotation too
if "bottom" in box_alignment:
pass
elif "top" in box_alignment:
pos -= Vector((0, h))
else:
pos -= Vector((0, h / 2)) # middle / center
if vcenter:
# vertical centering
pos -= Vector((-sin, cos)) * h * 0.5
if "left" in box_alignment:
pass
elif "right" in box_alignment:
pos -= Vector((w, 0))
else:
pos -= Vector((w / 2, 0))
else:
if center:
# horizontal centering
pos -= Vector((cos, sin)) * w * 0.5
if gap:
# side-shifting
pos += Vector((-sin, cos)) * gap
if vcenter:
# vertical centering
pos -= Vector((-sin, cos)) * h * 0.5
if gap:
# side-shifting
pos += Vector((-sin, cos)) * gap
blf.enable(font_id, blf.ROTATION)
blf.position(font_id, pos.x, pos.y, 0) if hasattr(pos, "x") else blf.position(font_id, 0, 0, 0)
@@ -2072,13 +2100,21 @@ class TextDecorator(BaseDecorator):
# TODO: support text rotation
dir = Vector((1, 0))
pos = location_3d_to_region_2d(region, region3d, obj.matrix_world.translation)
font_size = DecoratorData.get_ifc_text_font_size(obj)
props = obj.BIMTextProperties
text = props.get_text() if props.is_editing else props.value
for line_i, line in enumerate(text.split("\\n")):
text_data = props.get_text_edited_data() if props.is_editing else DecoratorData.get_ifc_text_data(obj)
box_alignment = text_data["box_alignment"]
for line_i, line in enumerate(text_data["text"].split("\\n")):
self.draw_label(
context, line, pos, dir, gap=0, center=False, vcenter=False, font_size=font_size, line_no=line_i
context,
line,
pos,
dir,
gap=0,
center=False,
vcenter=False,
font_size_mm=text_data["font_size"],
line_no=line_i,
box_alignment=box_alignment,
)
@@ -416,10 +416,6 @@ class BIMTextProperties(PropertyGroup):
def get_box_alignment(self):
return self.get("box_alignment", DEFAULT_BOX_ALIGNMENT)
def refresh_font_size(self, context):
# force update this object's font size for viewport display
DecoratorData.data.pop(context.object.name, None)
is_editing: BoolProperty(name="Is Editing", default=False)
attributes: CollectionProperty(name="Attributes", type=Attribute)
test_prop: StringProperty(name="test_prop", default="TEXT")
@@ -436,16 +432,23 @@ class BIMTextProperties(PropertyGroup):
("7.0", "7.0 - Title", ""),
],
default="2.5",
update=refresh_font_size,
name="Font Size",
)
box_alignment: BoolVectorProperty(
name="Box alignment", size=9, set=set_box_alignment, get=get_box_alignment, default=DEFAULT_BOX_ALIGNMENT
)
def get_text(self):
text_attribute = self.attributes.get("Literal", None)
return text_attribute.string_value if text_attribute else self.text
def get_text_edited_data(self):
"""should be called only if `is_editing`
etherwise should use `DecoratorData.get_ifc_text_data(obj)` instead
because this data could be out of date
"""
text_data = {
"text": self.attributes["Literal"].string_value,
"font_size": float(self.font_size),
"box_alignment": self.attributes["BoxAlignment"].string_value,
}
return text_data
class BIMAssignedProductProperties(PropertyGroup):
+2 -2
View File
@@ -465,8 +465,8 @@ class Drawing(blenderbim.core.tool.Drawing):
box_alignment_mask[BOX_ALIGNMENT_POSITIONS.index(position_string)] = True
props.box_alignment = box_alignment_mask
font_size = DecoratorData.get_ifc_text_font_size(obj)
props.font_size = str(font_size)
text_data = DecoratorData.get_ifc_text_data(obj)
props.font_size = str(text_data["font_size"])
@classmethod
def import_assigned_product(cls, obj):