radius annotation - support for prefix/suffix/fill-bg

and more consistency between viewport and svg
This commit is contained in:
Andrej730
2023-07-12 10:45:47 +05:00
parent becbfccae9
commit 669dbd2e8d
3 changed files with 124 additions and 95 deletions
@@ -291,14 +291,14 @@ class DecoratorData:
def get_dimension_data(cls, obj):
"""used by Ifc Annotations with ObjectType:
DIMENSION / DIAMETER / SECTION_LEVEL / PLAN_LEVEL
DIMENSION / DIAMETER / SECTION_LEVEL / PLAN_LEVEL / RADIUS
"""
result = cls.data.get(obj.name, None)
if result is not None:
return result
element = tool.Ifc.get_entity(obj)
supported_object_types = ("DIMENSION", "DIAMETER", "SECTION_LEVEL", "PLAN_LEVEL")
supported_object_types = ("DIMENSION", "DIAMETER", "SECTION_LEVEL", "PLAN_LEVEL", "RADIUS")
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in supported_object_types:
return None
@@ -454,6 +454,30 @@ class BaseDecorator:
blf.draw(font_id, text)
blf.disable(font_id, blf.ROTATION)
def draw_dimension_text(self, context, get_text, description, dimension_data, **draw_label_kwargs):
prefix = dimension_data["text_prefix"]
suffix = dimension_data["text_suffix"]
show_description_only = dimension_data["show_description_only"]
line_number_start = 0
if not show_description_only:
text = get_text()
full_prefix = ((description + "\\n") if description else "") + prefix
text = full_prefix + text + suffix
line_number_start -= full_prefix.count("\\n")
else:
if not description:
return
text = description
self.draw_label(
context,
text=text,
line_no=line_number_start,
multiline=True,
**draw_label_kwargs
)
@lru_cache(maxsize=None)
def format_value(self, context, value):
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
@@ -822,12 +846,15 @@ class RadiusDecorator(BaseDecorator):
objecttype = "RADIUS"
def get_spline_end(self, obj):
def get_spline_points(self, obj):
spline = obj.data.splines[0]
spline_points = spline.bezier_points if spline.bezier_points else spline.points
if not spline_points:
return Vector((0, 0, 0))
return obj.matrix_world @ spline_points[0].co
spline_points = [Vector([0,0,0])] * 2
elif len(spline_points) == 1:
spline_points = [spline_points[0]] * 2
return [(obj.matrix_world @ p.co) for p in spline_points[:2]]
def decorate(self, context, obj):
self.draw_arrow(context, obj)
@@ -836,15 +863,26 @@ class RadiusDecorator(BaseDecorator):
def draw_labels(self, context, obj):
region = context.region
region3d = context.region_data
dir = Vector((1, 0))
pos = location_3d_to_region_2d(region, region3d, self.get_spline_end(obj))
spline_points = self.get_spline_points(obj)
p0, p1 = [location_3d_to_region_2d(region, region3d, p) for p in self.get_spline_points(obj)]
element = tool.Ifc.get_entity(obj)
description = element.Description
dimension_data = DecoratorData.get_dimension_data(obj)
viewportDrawingScale = self.get_viewport_drawing_scale(context)
text_offset = 20 * viewportDrawingScale
spline = obj.data.splines[0]
spline_points = spline.bezier_points if spline.bezier_points else spline.points
if spline_points:
length = (spline_points[-1].co - spline_points[-2].co).length
text = self.format_value(context, length)
self.draw_label(context, text, pos, dir, gap=0, center=False, vcenter=False)
pos = p0 - (p1-p0).normalized() * text_offset
def get_text():
length = (spline_points[-1] - spline_points[-2]).length
return "R" + self.format_value(context, length)
self.draw_dimension_text(context,
get_text, description, dimension_data,
pos=pos,
text_dir=Vector((1, 0)),
box_alignment="center"
)
class FallDecorator(BaseDecorator):
@@ -1058,9 +1096,6 @@ class PlanLevelDecorator(BaseDecorator):
element = tool.Ifc.get_entity(obj)
description = element.Description
dimension_data = DecoratorData.get_dimension_data(obj)
show_description_only = dimension_data["show_description_only"]
text_prefix = dimension_data["text_prefix"]
text_suffix = dimension_data["text_suffix"]
for verts in splines:
p0, p1 = [location_3d_to_region_2d(region, region3d, v) for v in verts[:2]]
@@ -1074,30 +1109,19 @@ class PlanLevelDecorator(BaseDecorator):
box_alignment = "bottom-right"
text_dir *= -1
line_number_start = 0
if not show_description_only:
def get_text():
z = verts[0].z / unit_scale
z = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), z)
z *= unit_scale
text = "RL " + self.format_value(context, z)
full_prefix = ((description + "\\n") if description else "") + text_prefix
text = full_prefix + text + text_suffix
line_number_start -= full_prefix.count("\\n")
else:
if not description:
break
text = description
return text
self.draw_label(
context,
text=text,
self.draw_dimension_text(context, get_text, description, dimension_data,
box_alignment=box_alignment,
pos=p0,
text_dir=text_dir,
box_alignment=box_alignment,
line_no=line_number_start,
multiline=True,
text_dir=text_dir,
gap=8,
center=False,
center=False
)
@@ -1152,35 +1176,21 @@ class SectionLevelDecorator(BaseDecorator):
tag = storey.Name if storey else element.Description
dimension_data = DecoratorData.get_dimension_data(obj)
show_description_only = dimension_data["show_description_only"]
text_prefix = dimension_data["text_prefix"]
text_suffix = dimension_data["text_suffix"]
for verts in splines:
line_number_start = 0
if not show_description_only:
def get_text():
z = verts[0].z / unit_scale
z = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), z)
z *= unit_scale
text = "RL " + self.format_value(context, z)
full_prefix = ((tag + "\\n") if tag else "") + text_prefix
text = full_prefix + text + text_suffix
line_number_start -= full_prefix.count("\\n")
else:
if not tag:
break
text = tag
return text
self.draw_label(
context,
text=text,
self.draw_dimension_text(context,
get_text, tag, dimension_data,
box_alignment="bottom-left",
pos=text_position,
text_dir=text_dir,
box_alignment="bottom-left",
line_no=line_number_start,
multiline=True,
text_dir=text_dir,
)
break # support only 1 label
@@ -246,20 +246,23 @@ class SvgWriter:
angle = math.degrees(vector.angle_signed(Vector((1, 0))))
# TODO: allow metric to be configurable
rl_value = (matrix_world @ points[0].co.xyz).z
if bpy.context.scene.unit_settings.system == "IMPERIAL":
rl = helper.format_distance(rl_value, precision=self.precision, decimal_places=self.decimal_places)
else:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
rl = rl_value / unit_scale
rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl)
rl *= unit_scale
rl = "{:.3f}m".format(rl)
def get_text():
rl_value = (matrix_world @ points[0].co.xyz).z
if bpy.context.scene.unit_settings.system == "IMPERIAL":
rl = helper.format_distance(rl_value, precision=self.precision, decimal_places=self.decimal_places)
else:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
rl = rl_value / unit_scale
rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl)
rl *= unit_scale
rl = "{:.3f}m".format(rl)
text = "RL {}{}".format("" if rl_value < 0 else "+", rl)
return text
text_tags = []
line_number_start = 0
if not show_description_only:
text = "RL {}{}".format("" if rl_value < 0 else "+", rl)
text = get_text()
full_prefix = ((tag + "\\n") if tag else "") + prefix
text = full_prefix + text + suffix
line_number_start -= full_prefix.count("\\n")
@@ -1080,48 +1083,64 @@ class SvgWriter:
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
def draw_radius_annotations(self, obj):
x_offset = self.raw_width / 2
y_offset = self.raw_height / 2
offset = Vector([self.raw_width, self.raw_height]) / 2
classes = self.get_attribute_classes(obj)
element = tool.Ifc.get_entity(obj)
tag = element.Description
matrix_world = obj.matrix_world
dimension_data = DecoratorData.get_dimension_data(obj)
for spline in obj.data.splines:
points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
[
"L {} {}".format((x_offset + p.x) * self.svg_scale, (y_offset - p.y) * self.svg_scale)
for p in projected_points
]
)
projected_points_svg = [(offset + p.xy * Vector((1, -1))) * self.svg_scale for p in projected_points]
d = " ".join(["L {} {}".format(*p) for p in projected_points_svg])
d = "M{}".format(d[1:])
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
p0 = projected_points_svg[0]
p1 = projected_points_svg[1]
p0 = Vector(
(
(x_offset + projected_points[0].x) * self.svg_scale,
(y_offset - projected_points[0].y) * self.svg_scale,
)
)
p1 = Vector(
(
(x_offset + projected_points[1].x) * self.svg_scale,
(y_offset - projected_points[1].y) * self.svg_scale,
)
)
text_offset = (p0 - p1).xy.normalized() * 5
text_position = projected_points[0]
text_position = Vector(
((x_offset + text_position.x) * self.svg_scale, (y_offset - text_position.y) * self.svg_scale)
)
text_position += text_offset
text_offset = (p0 - p1).normalized() * 5
text_position = p0 + text_offset
text_style = SvgWriter.get_box_alignment_parameters("center")
radius = (points[-1].co - points[-2].co).length
radius = helper.format_distance(radius, precision=self.precision, decimal_places=self.decimal_places)
tag = element.Description or f"R{radius}"
def get_text():
radius = (points[-1].co - points[-2].co).length
radius = helper.format_distance(radius, precision=self.precision, decimal_places=self.decimal_places)
text = f"R{radius}"
return text
self.draw_dimension_text(get_text, tag, dimension_data, text_position=text_position, class_str="RADIUS", box_alignment="center")
def draw_dimension_text(self, get_text, tag, dimension_data, **create_text_kwargs):
prefix = dimension_data["text_prefix"]
suffix = dimension_data["text_suffix"]
show_description_only = dimension_data["show_description_only"]
fill_bg = dimension_data["fill_bg"]
text_tags = []
line_number_start = 0
if not show_description_only:
text = get_text()
full_prefix = ((tag + "\\n") if tag else "") + prefix
text = full_prefix + text + suffix
line_number_start -= full_prefix.count("\\n")
else:
if not tag:
return
text = tag
text_tags += self.create_text_tag(
text,
line_number_start=line_number_start,
fill_bg=fill_bg,
**create_text_kwargs
)
for text in text_tags:
self.svg.add(text)
self.svg.add(self.svg.text(tag, insert=tuple(text_position), class_="RADIUS", **text_style))
def draw_fall_annotations(self, obj):
x_offset = self.raw_width / 2