diff --git a/src/blenderbim/blenderbim/bim/data/styles/default.css b/src/blenderbim/blenderbim/bim/data/styles/default.css
index 57ce10670f..78c9ac41b1 100644
--- a/src/blenderbim/blenderbim/bim/data/styles/default.css
+++ b/src/blenderbim/blenderbim/bim/data/styles/default.css
@@ -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); }
diff --git a/src/blenderbim/blenderbim/bim/data/templates/markers.svg b/src/blenderbim/blenderbim/bim/data/templates/markers.svg
index 0726e59c88..cff4a43af3 100644
--- a/src/blenderbim/blenderbim/bim/data/templates/markers.svg
+++ b/src/blenderbim/blenderbim/bim/data/templates/markers.svg
@@ -19,6 +19,12 @@
+
+
+
+
+
+
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py
index c998b2e886..066df06674 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py
@@ -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
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
index 98dd520eca..469b030f51 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py
@@ -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
diff --git a/src/blenderbim/blenderbim/bim/module/drawing/ui.py b/src/blenderbim/blenderbim/bim/module/drawing/ui.py
index a22f04e010..592f68db97 100644
--- a/src/blenderbim/blenderbim/bim/module/drawing/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/drawing/ui.py
@@ -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
diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py
index cbc07c849c..8f0372dd30 100644
--- a/src/blenderbim/blenderbim/bim/module/model/slab.py
+++ b/src/blenderbim/blenderbim/bim/module/model/slab.py
@@ -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"}
diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py
index cdd7b56e4e..3cf5a58d0d 100644
--- a/src/blenderbim/blenderbim/tool/drawing.py
+++ b/src/blenderbim/blenderbim/tool/drawing.py
@@ -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",