diff --git a/src/blenderbim/blenderbim/bim/module/drawing/data.py b/src/blenderbim/blenderbim/bim/module/drawing/data.py index 3aeebc46ce..3d0a65b48f 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/data.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/data.py @@ -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 diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py index d63fedd051..9c12c421c4 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py @@ -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 diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py index 898914e499..ac347445e1 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py @@ -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