Fall annotations

Added fall annotation - they appear both in viewport and svg.

There are three types of annotations (SLOPE_ANGLE, SLOPE_FRACTION, SLOPE_PERCENT) you can switch them by changing annotation's ObjectType to one of those values.

By default it uses angle representation.
This commit is contained in:
Andrej730
2023-03-09 11:03:11 +05:00
parent cdee5532b6
commit 972d701801
7 changed files with 227 additions and 9 deletions
@@ -33,7 +33,11 @@ text { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT',
.PredefinedType-PLANLEVEL { marker-end: url(#plan-level-marker); }
.PredefinedType-DIMENSION { marker-start: url(#dimension-marker-start); marker-end: url(#dimension-marker-end); }
.PredefinedType-ANGLE { marker-start: url(#angle-marker-start); marker-end: url(#angle-marker-end); }
.PredefinedType-RADIUS { marker-end: url(#radius-marker-end); }
.PredefinedType-RADIUS { marker-end: url(#fall-marker-end); }
.PredefinedType-FALL { marker-end: url(#radius-marker-end); }
.PredefinedType-SLOPEANGLE { marker-end: url(#radius-marker-end); }
.PredefinedType-SLOPEPERCENT { marker-end: url(#radius-marker-end); }
.PredefinedType-SLOPEFRACTION { marker-end: url(#radius-marker-end); }
.PredefinedType-DIAMETER { marker-start: url(#diameter-marker-start); marker-end: url(#diameter-marker-end); }
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
.PredefinedType-STAIRARROW { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
@@ -19,6 +19,12 @@
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
</g>
</marker>
<!-- same as for the radius -->
<marker id="fall-marker-end" markerHeight="14" markerWidth="11" orient="auto" refX="10" refY="7">
<g>
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
</g>
</marker>
<marker id="angle-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
<g>
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -26,7 +26,7 @@ import bmesh
import ifcopenshell
import blenderbim.tool as tool
import blenderbim.bim.module.drawing.helper as helper
from math import acos, pi
from math import acos, pi, atan, degrees
from functools import reduce
from itertools import chain
from bpy.types import SpaceView3D
@@ -236,12 +236,20 @@ class BaseDecorator:
if element.is_a("IfcAnnotation"):
if element.ObjectType == self.objecttype:
results.append(obj)
elif (
self.objecttype == "MISC"
and element.ObjectType not in decoration_presets
and isinstance(obj.data, bpy.types.Mesh)
):
results.append(obj)
elif self.objecttype == "FALL" and element.ObjectType in (
"SLOPE_ANGLE",
"SLOPE_FRACTION",
"SLOPE_PERCENT",
):
results.append(obj)
return results
def get_path_geom(self, obj, topo=True):
@@ -857,6 +865,125 @@ class RadiusDecorator(BaseDecorator):
self.draw_label(context, text, pos, dir, gap=0, center=False, vcenter=False)
class FallDecorator(BaseDecorator):
"""Decorating text with arrows
- head point with arrow
"""
objecttype = "FALL"
DEF_GLSL = (
BaseDecorator.DEF_GLSL
+ """
#define ARROW_ANGLE PI / 12.0
#define ARROW_SIZE 16.0
"""
)
GEOM_GLSL = """
uniform vec2 winsize;
uniform float viewportDrawingScale;
layout(lines) in;
layout(line_strip, max_vertices=MAX_POINTS) out;
in uint type[];
void main() {
vec4 clip2win = matCLIP2WIN();
vec4 win2clip = matWIN2CLIP();
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
uint t0 = type[0], t1 = type[1];
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
vec4 edge = p1w - p0w, dir = normalize(edge);
vec4 gap1 = vec4(0);
vec4 p;
// end edge arrow for last segment
if (t1 == 2u) {
vec4 head[3];
arrow_head(dir, viewportDrawingScale * ARROW_SIZE, ARROW_ANGLE, head);
gl_Position = p1;
EmitVertex();
p = p1w - head[1];
gl_Position = WIN2CLIP(p);
EmitVertex();
p = p1w - head[2];
gl_Position = WIN2CLIP(p);
EmitVertex();
gl_Position = p1;
EmitVertex();
EndPrimitive();
gap1 = dir * viewportDrawingScale * ARROW_SIZE;
}
// stem, adjusted for and arrow
gl_Position = p0;
EmitVertex();
p = p1w - gap1;
gl_Position = WIN2CLIP(p);
EmitVertex();
EndPrimitive();
}
"""
def decorate(self, context, obj):
verts, idxs, topo = self.get_path_geom(obj)
self.draw_lines(context, obj, verts, idxs, topo)
self.draw_labels(context, obj)
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 = obj.data.splines[0]
spline_points = spline.bezier_points if spline.bezier_points else spline.points
# generate label text
# same function as in svgwriter.py
def get_label_text():
element = tool.Ifc.get_entity(obj)
B, A = [v.co.xyz for v in spline_points[:2]]
rise = abs(A.z - B.z)
O = A.copy()
O.z = B.z
run = (B - O).length
if run != 0:
angle_tg = rise / run
angle = round(degrees(atan(angle_tg)))
else:
angle = 90
# ues SLOPE_ANGLE as default
if element.ObjectType in ("FALL", "SLOPE_ANGLE"):
return f"{angle}°"
elif element.ObjectType == "SLOPE_FRACTION":
if angle == 90:
return "-"
return f"{self.format_value(context, rise)} / {self.format_value(context, run)}"
elif element.ObjectType == "SLOPE_PERCENT":
if angle == 90:
return "-"
return f"{round(angle_tg * 100)} %"
if spline_points:
text = get_label_text()
self.draw_label(context, text, pos, dir, gap=0, center=False, vcenter=False)
def get_spline_end(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
class StairDecorator(BaseDecorator):
"""Decorating stairs
- head point with arrow
@@ -1444,7 +1571,7 @@ class BattingDecorator(BaseDecorator):
verts, idxs = self.get_editmesh_geom(obj)
else:
verts, idxs = self.get_mesh_geom(obj)
# TODO: find the less ugly way to figure thickness
thickness = DecoratorData.get_batting_thickness(obj)
region = context.region
@@ -1461,9 +1588,8 @@ class BattingDecorator(BaseDecorator):
obj,
verts[:2],
idxs,
extra_float_kwargs={
"batting_thickness_winspace": winspace_thickness},
is_scale_dependant = False
extra_float_kwargs={"batting_thickness_winspace": winspace_thickness},
is_scale_dependant=False,
)
@@ -1924,6 +2050,7 @@ class DecorationsHandler:
ElevationDecorator,
TextDecorator,
BattingDecorator,
FallDecorator,
]
installed = None
@@ -31,7 +31,7 @@ import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.bim.module.drawing.helper as helper
import blenderbim.bim.module.drawing.annotation as annotation
from math import pi, ceil
from math import pi, ceil, atan, degrees
from mathutils import Vector
from mathutils import geometry
from blenderbim.bim.ifc import IfcStore
@@ -162,6 +162,8 @@ class SvgWriter:
self.draw_section_level_annotation(obj)
elif element.ObjectType == "TEXT":
self.draw_text_annotation(obj, obj.location)
elif element.ObjectType in ("FALL", "SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
self.draw_fall_annotations(obj)
else:
self.draw_misc_annotation(obj)
@@ -884,12 +886,86 @@ class SvgWriter:
"dominant-baseline": "middle",
}
radius = ((matrix_world @ points[-1].co) - (matrix_world @ points[-2].co)).length
radius = (points[-1].co - points[-2].co).length
radius = helper.format_distance(radius)
tag = element.Description or f"R{radius}"
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
y_offset = self.raw_height / 2
classes = self.get_attribute_classes(obj)
element = tool.Ifc.get_entity(obj)
matrix_world = obj.matrix_world
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
]
)
d = "M{}".format(d[1:])
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
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,
)
)
# generate label text
# same function as in decoration.py
def get_label_text():
B, A = [v.co.xyz for v in points[:2]]
rise = abs(A.z - B.z)
O = A.copy()
O.z = B.z
run = (B - O).length
if run != 0:
angle_tg = rise / run
angle = round( degrees( atan(angle_tg) ))
else:
angle = 90
# ues SLOPE_ANGLE as default
if element.ObjectType in ("FALL", "SLOPE_ANGLE"):
return f"{angle}°"
elif element.ObjectType == "SLOPE_FRACTION":
if angle == 90:
return "-"
return f"{helper.format_distance(rise)} / {helper.format_distance(run)}"
elif element.ObjectType == "SLOPE_PERCENT":
if angle == 90:
return "-"
return f"{round(angle_tg * 100)} %"
tag = element.Description or get_label_text()
text_offset = (p0 - p1).xy.normalized() * len(tag)
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_style = {
"text-anchor": "middle",
"alignment-baseline": "middle",
"dominant-baseline": "middle",
}
self.svg.add(self.svg.text(tag, insert=tuple(text_position), class_="RADIUS", **text_style))
def draw_diameter_annotations(self, obj):
classes = self.get_attribute_classes(obj)
matrix_world = obj.matrix_world
@@ -425,6 +425,11 @@ class BIM_PT_annotation_utilities(Panel):
op = row.operator("bim.add_annotation", text="Fill Area", icon="NODE_TEXTURE")
op.object_type = "FILL_AREA"
row = layout.row(align=True)
op = row.operator("bim.add_annotation", text="Fall", icon="SORT_ASC")
op.object_type = "FALL"
op.data_type = "curve"
row = layout.row(align=True)
row.prop(self.props, "should_draw_decorations", text="Viewport Annotations")
row.enabled = context.scene.camera is not None
@@ -612,7 +612,6 @@ class EnableEditingExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
bpy.ops.object.mode_set(mode="EDIT")
ProfileDecorator.install(context)
# TODO: test it from properties panel?
if not bpy.app.background:
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
return {"FINISHED"}
@@ -53,6 +53,7 @@ class Drawing(blenderbim.core.tool.Drawing):
"BREAKLINE": "mesh",
"DIAMETER": "curve",
"DIMENSION": "curve",
"FALL": "curve",
"FILL_AREA": "mesh",
"HIDDEN_LINE": "mesh",
"LINEWORK": "mesh",