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:
Andrej730
2023-05-12 14:54:01 +05:00
parent dff88c7560
commit 41ef46cb88
2 changed files with 128 additions and 17 deletions
@@ -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?