mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 09:05:18 +00:00
Multiline in dimension description / suffix / prefix #3005
Also added some offset for dimension text in viewport to make it more similar to svg
This commit is contained in:
@@ -377,17 +377,42 @@ class BaseDecorator:
|
|||||||
font_size_mm=2.5,
|
font_size_mm=2.5,
|
||||||
line_no=0,
|
line_no=0,
|
||||||
box_alignment=None,
|
box_alignment=None,
|
||||||
|
multiline=False,
|
||||||
|
multiline_to_bottom=True,
|
||||||
):
|
):
|
||||||
"""Draw text label
|
"""Draw text label
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pos: bottom-center
|
pos: bottom-center
|
||||||
|
multiline: \n characters will be interpreted as line breaks
|
||||||
|
|
||||||
aligned and centered at segment middle
|
aligned and centered at segment middle
|
||||||
|
|
||||||
NOTE: `blf.draw` seems to ignore the \n character, so we have to split the text ourselves
|
NOTE: `blf.draw` seems to ignore the \n character, so we have to split the text ourselves
|
||||||
and use the `line_no` argument of `draw_label`
|
and use the `line_no` argument of `draw_label`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if multiline:
|
||||||
|
line_no = 0
|
||||||
|
lines = text.split("\\n")
|
||||||
|
lines = lines if multiline_to_bottom else lines[::-1]
|
||||||
|
for line in lines:
|
||||||
|
self.draw_label(
|
||||||
|
context,
|
||||||
|
line,
|
||||||
|
pos,
|
||||||
|
text_dir,
|
||||||
|
gap=gap,
|
||||||
|
center=center,
|
||||||
|
vcenter=vcenter,
|
||||||
|
font_size_mm=font_size_mm,
|
||||||
|
line_no=line_no,
|
||||||
|
box_alignment=box_alignment,
|
||||||
|
)
|
||||||
|
line_no += 1 if multiline_to_bottom else -1
|
||||||
|
return
|
||||||
|
|
||||||
# 0 is the default font, but we're fancier than that
|
# 0 is the default font, but we're fancier than that
|
||||||
font_id = self.font_id
|
font_id = self.font_id
|
||||||
|
|
||||||
@@ -492,7 +517,7 @@ class BaseDecorator:
|
|||||||
start_i = add_verts_sequence(add_offsets(v0, segment), start_i, **out_kwargs)
|
start_i = add_verts_sequence(add_offsets(v0, segment), start_i, **out_kwargs)
|
||||||
self.draw_lines(context, obj, output_verts, output_edges)
|
self.draw_lines(context, obj, output_verts, output_edges)
|
||||||
|
|
||||||
def draw_text(self, context, obj, text_world_position=None):
|
def draw_text(self, context, obj, text_world_position=None, reverse_lines_order=False):
|
||||||
"""if `text_world_position` is not provided, the object's location will be used"""
|
"""if `text_world_position` is not provided, the object's location will be used"""
|
||||||
if not text_world_position:
|
if not text_world_position:
|
||||||
text_world_position = obj.location
|
text_world_position = obj.location
|
||||||
@@ -522,6 +547,7 @@ class BaseDecorator:
|
|||||||
text_scale = obj.scale.x
|
text_scale = obj.scale.x
|
||||||
|
|
||||||
line_i = 0
|
line_i = 0
|
||||||
|
font_size_mm = text_data["FontSize"] * text_scale
|
||||||
for literal_data in literals_data:
|
for literal_data in literals_data:
|
||||||
box_alignment = literal_data["BoxAlignment"]
|
box_alignment = literal_data["BoxAlignment"]
|
||||||
|
|
||||||
@@ -534,11 +560,11 @@ class BaseDecorator:
|
|||||||
gap=0,
|
gap=0,
|
||||||
center=False,
|
center=False,
|
||||||
vcenter=False,
|
vcenter=False,
|
||||||
font_size_mm=text_data["FontSize"] * text_scale,
|
font_size_mm=font_size_mm,
|
||||||
line_no=line_i,
|
line_no=line_i,
|
||||||
box_alignment=box_alignment,
|
box_alignment=box_alignment,
|
||||||
)
|
)
|
||||||
line_i += 1
|
line_i += 1 if not reverse_lines_order else -1
|
||||||
|
|
||||||
|
|
||||||
class DimensionDecorator(BaseDecorator):
|
class DimensionDecorator(BaseDecorator):
|
||||||
@@ -624,6 +650,8 @@ class DimensionDecorator(BaseDecorator):
|
|||||||
show_description_only = dimension_data["show_description_only"]
|
show_description_only = dimension_data["show_description_only"]
|
||||||
text_prefix = dimension_data["text_prefix"]
|
text_prefix = dimension_data["text_prefix"]
|
||||||
text_suffix = dimension_data["text_suffix"]
|
text_suffix = dimension_data["text_suffix"]
|
||||||
|
viewportDrawingScale = self.get_viewport_drawing_scale(context)
|
||||||
|
text_offset_value = viewportDrawingScale * 3
|
||||||
|
|
||||||
for i0, i1 in indices:
|
for i0, i1 in indices:
|
||||||
v0 = Vector(vertices[i0])
|
v0 = Vector(vertices[i0])
|
||||||
@@ -633,6 +661,8 @@ class DimensionDecorator(BaseDecorator):
|
|||||||
text_dir = p1 - p0
|
text_dir = p1 - p0
|
||||||
if text_dir.length < 1:
|
if text_dir.length < 1:
|
||||||
continue
|
continue
|
||||||
|
perpendicular = Vector((-text_dir.y, text_dir.x)).normalized()
|
||||||
|
text_offset = perpendicular * text_offset_value
|
||||||
|
|
||||||
if not show_description_only:
|
if not show_description_only:
|
||||||
length = (v1 - v0).length
|
length = (v1 - v0).length
|
||||||
@@ -641,12 +671,35 @@ class DimensionDecorator(BaseDecorator):
|
|||||||
text = self.format_value(context, length)
|
text = self.format_value(context, length)
|
||||||
text = text_prefix + text + text_suffix
|
text = text_prefix + text + text_suffix
|
||||||
|
|
||||||
self.draw_label(context, text, p0 + text_dir * 0.5, text_dir, box_alignment="bottom-middle")
|
self.draw_label(
|
||||||
|
context,
|
||||||
|
text,
|
||||||
|
p0 + text_dir * 0.5 + text_offset,
|
||||||
|
text_dir,
|
||||||
|
box_alignment="bottom-middle",
|
||||||
|
multiline=True,
|
||||||
|
multiline_to_bottom=False,
|
||||||
|
)
|
||||||
if description:
|
if description:
|
||||||
self.draw_label(context, description, p0 + text_dir * 0.5, text_dir, box_alignment="top-middle")
|
self.draw_label(
|
||||||
|
context,
|
||||||
|
description,
|
||||||
|
p0 + text_dir * 0.5 - text_offset,
|
||||||
|
text_dir,
|
||||||
|
box_alignment="top-middle",
|
||||||
|
multiline=True,
|
||||||
|
)
|
||||||
|
|
||||||
elif show_description_only and description:
|
elif show_description_only and description:
|
||||||
self.draw_label(context, description, p0 + text_dir * 0.5, text_dir, box_alignment="bottom-middle")
|
self.draw_label(
|
||||||
|
context,
|
||||||
|
description,
|
||||||
|
p0 + text_dir * 0.5 + text_offset,
|
||||||
|
text_dir,
|
||||||
|
box_alignment="bottom-middle",
|
||||||
|
multiline=True,
|
||||||
|
multiline_to_bottom=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AngleDecorator(BaseDecorator):
|
class AngleDecorator(BaseDecorator):
|
||||||
|
|||||||
@@ -1149,28 +1149,86 @@ class SvgWriter:
|
|||||||
|
|
||||||
# if annotation can't fit offset text to the right of marker
|
# if annotation can't fit offset text to the right of marker
|
||||||
text_position = mid if sheet_dimension > 5 else (end + (3 * vector.normalized()))
|
text_position = mid if sheet_dimension > 5 else (end + (3 * vector.normalized()))
|
||||||
rotation = math.degrees(vector.angle_signed(Vector((1, 0))))
|
angle = math.degrees(vector.angle_signed(Vector((1, 0))))
|
||||||
|
|
||||||
line = self.svg.line(start=start, end=end, class_=" ".join(classes))
|
line = self.svg.line(start=start, end=end, class_=" ".join(classes))
|
||||||
self.svg.add(line)
|
self.svg.add(line)
|
||||||
|
|
||||||
def create_text_tag(text, text_position, box_alignment):
|
if not show_description_only:
|
||||||
text_kwargs = {"transform": "rotate({} {} {})".format(rotation, text_position.x, text_position.y)}
|
text = f"{text_prefix}{str(dimension)}{text_suffix}"
|
||||||
|
text_tag = self.create_text_tag(
|
||||||
|
text,
|
||||||
|
text_position + perpendicular,
|
||||||
|
angle,
|
||||||
|
"bottom-middle",
|
||||||
|
"DIMENSION",
|
||||||
|
text_format=text_format,
|
||||||
|
multiline=True,
|
||||||
|
multiline_to_bottom=False,
|
||||||
|
)
|
||||||
|
self.svg.add(text_tag)
|
||||||
|
if dimension_text:
|
||||||
|
text_tag = self.create_text_tag(
|
||||||
|
dimension_text,
|
||||||
|
text_position - perpendicular,
|
||||||
|
angle,
|
||||||
|
"top-middle",
|
||||||
|
"DIMENSION",
|
||||||
|
text_format=text_format,
|
||||||
|
multiline=True,
|
||||||
|
multiline_to_bottom=True,
|
||||||
|
)
|
||||||
|
self.svg.add(text_tag)
|
||||||
|
|
||||||
|
elif show_description_only and dimension_text:
|
||||||
|
text_tag = self.create_text_tag(
|
||||||
|
dimension_text,
|
||||||
|
text_position + perpendicular,
|
||||||
|
angle,
|
||||||
|
"bottom-middle",
|
||||||
|
"DIMENSION",
|
||||||
|
text_format=text_format,
|
||||||
|
multiline=True,
|
||||||
|
multiline_to_bottom=False,
|
||||||
|
)
|
||||||
|
self.svg.add(text_tag)
|
||||||
|
|
||||||
|
def create_text_tag(
|
||||||
|
self,
|
||||||
|
text,
|
||||||
|
text_position,
|
||||||
|
angle,
|
||||||
|
box_alignment,
|
||||||
|
class_str,
|
||||||
|
text_format=lambda x: x,
|
||||||
|
multiline=False,
|
||||||
|
multiline_to_bottom=False,
|
||||||
|
):
|
||||||
|
if not multiline:
|
||||||
|
text_kwargs = {"transform": "rotate({} {} {})".format(angle, text_position.x, text_position.y)}
|
||||||
return self.svg.text(
|
return self.svg.text(
|
||||||
text_format(text),
|
text_format(text),
|
||||||
insert=text_position,
|
insert=text_position,
|
||||||
class_="DIMENSION",
|
class_=class_str,
|
||||||
**(text_kwargs | SvgWriter.get_box_alignment_parameters(box_alignment)),
|
**(text_kwargs | SvgWriter.get_box_alignment_parameters(box_alignment)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not show_description_only:
|
text_position_svg_str = ", ".join(map(str, text_position))
|
||||||
text = f"{text_prefix}{str(dimension)}{text_suffix}"
|
text_transform = f"translate({text_position_svg_str}) rotate({angle})"
|
||||||
self.svg.add(create_text_tag(text, text_position + perpendicular, "bottom-middle"))
|
text_kwargs = {
|
||||||
if dimension_text:
|
"transform": text_transform,
|
||||||
self.svg.add(create_text_tag(dimension_text, text_position - perpendicular, "top-middle"))
|
"style": "font-size: 0;",
|
||||||
|
}
|
||||||
|
|
||||||
elif show_description_only and dimension_text:
|
text_tag = self.svg.text("", **text_kwargs, **SvgWriter.get_box_alignment_parameters(box_alignment))
|
||||||
self.svg.add(create_text_tag(dimension_text, text_position + perpendicular, "bottom-middle"))
|
text_lines = text.replace("\\n", "\n").split("\n")
|
||||||
|
text_lines = text_lines if multiline_to_bottom else text_lines[::-1]
|
||||||
|
|
||||||
|
for line_number, text_line in enumerate(text_lines):
|
||||||
|
tspan = self.svg.tspan(text_format(text_line), class_=class_str, insert=(0, 0))
|
||||||
|
tspan.update({"dy": f"{line_number if multiline_to_bottom else -line_number}em"})
|
||||||
|
text_tag.add(tspan)
|
||||||
|
return text_tag
|
||||||
|
|
||||||
def project_point_onto_camera(self, point):
|
def project_point_onto_camera(self, point):
|
||||||
# TODO is this needlessly complex?
|
# TODO is this needlessly complex?
|
||||||
|
|||||||
Reference in New Issue
Block a user