mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 21:50:01 +00:00
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:
@@ -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-PLANLEVEL { marker-end: url(#plan-level-marker); }
|
||||||
.PredefinedType-DIMENSION { marker-start: url(#dimension-marker-start); marker-end: url(#dimension-marker-end); }
|
.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-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-DIAMETER { marker-start: url(#diameter-marker-start); marker-end: url(#diameter-marker-end); }
|
||||||
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
|
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
|
||||||
.PredefinedType-STAIRARROW { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
|
.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;" />
|
<path d="M 0 3.5 L 0 10.5 L 10 7" class="annotation" style="fill:black;" />
|
||||||
</g>
|
</g>
|
||||||
</marker>
|
</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">
|
<marker id="angle-marker-start" markerHeight="14" markerWidth="11" orient="auto" refX="1" refY="7">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 11 3.5 L 11 10.5 L 1 7" class="annotation" style="fill:black;" />
|
<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 ifcopenshell
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim.module.drawing.helper as helper
|
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 functools import reduce
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from bpy.types import SpaceView3D
|
from bpy.types import SpaceView3D
|
||||||
@@ -236,12 +236,20 @@ class BaseDecorator:
|
|||||||
if element.is_a("IfcAnnotation"):
|
if element.is_a("IfcAnnotation"):
|
||||||
if element.ObjectType == self.objecttype:
|
if element.ObjectType == self.objecttype:
|
||||||
results.append(obj)
|
results.append(obj)
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
self.objecttype == "MISC"
|
self.objecttype == "MISC"
|
||||||
and element.ObjectType not in decoration_presets
|
and element.ObjectType not in decoration_presets
|
||||||
and isinstance(obj.data, bpy.types.Mesh)
|
and isinstance(obj.data, bpy.types.Mesh)
|
||||||
):
|
):
|
||||||
results.append(obj)
|
results.append(obj)
|
||||||
|
|
||||||
|
elif self.objecttype == "FALL" and element.ObjectType in (
|
||||||
|
"SLOPE_ANGLE",
|
||||||
|
"SLOPE_FRACTION",
|
||||||
|
"SLOPE_PERCENT",
|
||||||
|
):
|
||||||
|
results.append(obj)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_path_geom(self, obj, topo=True):
|
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)
|
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):
|
class StairDecorator(BaseDecorator):
|
||||||
"""Decorating stairs
|
"""Decorating stairs
|
||||||
- head point with arrow
|
- head point with arrow
|
||||||
@@ -1461,9 +1588,8 @@ class BattingDecorator(BaseDecorator):
|
|||||||
obj,
|
obj,
|
||||||
verts[:2],
|
verts[:2],
|
||||||
idxs,
|
idxs,
|
||||||
extra_float_kwargs={
|
extra_float_kwargs={"batting_thickness_winspace": winspace_thickness},
|
||||||
"batting_thickness_winspace": winspace_thickness},
|
is_scale_dependant=False,
|
||||||
is_scale_dependant = False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1924,6 +2050,7 @@ class DecorationsHandler:
|
|||||||
ElevationDecorator,
|
ElevationDecorator,
|
||||||
TextDecorator,
|
TextDecorator,
|
||||||
BattingDecorator,
|
BattingDecorator,
|
||||||
|
FallDecorator,
|
||||||
]
|
]
|
||||||
|
|
||||||
installed = None
|
installed = None
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import ifcopenshell.util.representation
|
|||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim.module.drawing.helper as helper
|
import blenderbim.bim.module.drawing.helper as helper
|
||||||
import blenderbim.bim.module.drawing.annotation as annotation
|
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 Vector
|
||||||
from mathutils import geometry
|
from mathutils import geometry
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
@@ -162,6 +162,8 @@ class SvgWriter:
|
|||||||
self.draw_section_level_annotation(obj)
|
self.draw_section_level_annotation(obj)
|
||||||
elif element.ObjectType == "TEXT":
|
elif element.ObjectType == "TEXT":
|
||||||
self.draw_text_annotation(obj, obj.location)
|
self.draw_text_annotation(obj, obj.location)
|
||||||
|
elif element.ObjectType in ("FALL", "SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||||
|
self.draw_fall_annotations(obj)
|
||||||
else:
|
else:
|
||||||
self.draw_misc_annotation(obj)
|
self.draw_misc_annotation(obj)
|
||||||
|
|
||||||
@@ -884,12 +886,86 @@ class SvgWriter:
|
|||||||
"dominant-baseline": "middle",
|
"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)
|
radius = helper.format_distance(radius)
|
||||||
tag = element.Description or f"R{radius}"
|
tag = element.Description or f"R{radius}"
|
||||||
|
|
||||||
self.svg.add(self.svg.text(tag, insert=tuple(text_position), class_="RADIUS", **text_style))
|
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):
|
def draw_diameter_annotations(self, obj):
|
||||||
classes = self.get_attribute_classes(obj)
|
classes = self.get_attribute_classes(obj)
|
||||||
matrix_world = obj.matrix_world
|
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 = row.operator("bim.add_annotation", text="Fill Area", icon="NODE_TEXTURE")
|
||||||
op.object_type = "FILL_AREA"
|
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 = layout.row(align=True)
|
||||||
row.prop(self.props, "should_draw_decorations", text="Viewport Annotations")
|
row.prop(self.props, "should_draw_decorations", text="Viewport Annotations")
|
||||||
row.enabled = context.scene.camera is not None
|
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")
|
bpy.ops.object.mode_set(mode="EDIT")
|
||||||
ProfileDecorator.install(context)
|
ProfileDecorator.install(context)
|
||||||
# TODO: test it from properties panel?
|
|
||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
|
bpy.ops.wm.tool_set_by_id(tool.Blender.get_viewport_context(), name="bim.cad_tool")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
"BREAKLINE": "mesh",
|
"BREAKLINE": "mesh",
|
||||||
"DIAMETER": "curve",
|
"DIAMETER": "curve",
|
||||||
"DIMENSION": "curve",
|
"DIMENSION": "curve",
|
||||||
|
"FALL": "curve",
|
||||||
"FILL_AREA": "mesh",
|
"FILL_AREA": "mesh",
|
||||||
"HIDDEN_LINE": "mesh",
|
"HIDDEN_LINE": "mesh",
|
||||||
"LINEWORK": "mesh",
|
"LINEWORK": "mesh",
|
||||||
|
|||||||
Reference in New Issue
Block a user