mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Batting Annotation pattern
Added batting pattern to line annotations. The way to use it is: 1) Add "Line" Annotation; 2) Change object type to "BATTING" 3) Add object property set "Pset_BBIM_Bating" (also added in this commit) to specify the batting thickness. Currently there is no preview for batting in viewport. Other changes: - EPset_Annotation.ifc -> Psets_BBIM_Annotation.ifc (more general psets ifc library for the future use)
This commit is contained in:
+3
@@ -9,5 +9,8 @@ DATA;
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('2P7JN79n96Q9pElZ83LKe4',$,'ZIndex','',.P_SINGLEVALUE.,'IfcInteger',$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('1Wpx_r2xj1_9w5JpI0QRJy',$,'Symbol','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#4=IFCSIMPLEPROPERTYTEMPLATE('3q0oxMUKP47vZ4jnyG$dDb',$,'Classes','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||
#5=IFCPROPERTYSETTEMPLATE('0iKwujnQL9IevVQato8f7Z',$,'Pset_BBIM_Batting','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#6,#7));
|
||||
#6=IFCSIMPLEPROPERTYTEMPLATE('0t2LEesGT1QRQtrIZUAR8L',$,'Thickness','Batting thickness',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
|
||||
#7=IFCSIMPLEPROPERTYTEMPLATE('082PndS6v2kBOiJoSboMnh',$,'Reverse pattern direction','Reverse batting pattern (swap starting and ending points)',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
@@ -37,7 +37,6 @@ text { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT';
|
||||
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
|
||||
.PredefinedType-STAIRARROW { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
|
||||
.PredefinedType-BOUNDARY { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12, 4, 3, 4, 3, 4; }
|
||||
.PredefinedType-BATTING { marker-start: url(#batting); stroke: none; }
|
||||
.PredefinedType-SEALANT { fill: url(#crosshatch1); stroke-width: 0.25; }
|
||||
.PredefinedType-FILLAREA { fill: white; stroke: black; }
|
||||
.PredefinedType-BREAKLINE { fill: none; stroke: black; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<marker id="batting" markerHeight="10" markerWidth="4" orient="auto" refX="0" refY="5" markerUnits="userSpaceOnUse">
|
||||
<!-- Tip: https://svg-path-visualizer.netlify.app/ -->
|
||||
<path style="fill: none; stroke:black; stroke-width:0.18" d="M 0 2 A 2 2 0 0 1 4 2 L 2 8 M 0 2 L 2 8 A 2 2 0 0 0 4 10 M 2 8 A 2 2 0 0 1 0 10" />
|
||||
</marker>
|
||||
<!-- Tip: https://www.svgviewer.dev/ -->
|
||||
<marker id="eng-dimension-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.4 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -137,8 +137,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
Only available if :
|
||||
- IFC file is created
|
||||
- Camera is in Orthographic mode
|
||||
"""
|
||||
- Camera is in Orthographic mode"""
|
||||
|
||||
bl_idname = "bim.create_drawing"
|
||||
bl_label = "Create Drawing"
|
||||
|
||||
@@ -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
|
||||
from math import pi, ceil
|
||||
from mathutils import Vector
|
||||
from mathutils import geometry
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -363,22 +363,95 @@ class SvgWriter:
|
||||
self.draw_edge_annotation(obj, classes)
|
||||
|
||||
def draw_edge_annotation(self, obj, classes):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
matrix_world = obj.matrix_world
|
||||
for edge in obj.data.edges:
|
||||
v0_global = matrix_world @ obj.data.vertices[edge.vertices[0]].co.xyz
|
||||
v1_global = matrix_world @ obj.data.vertices[edge.vertices[1]].co.xyz
|
||||
predefined_type = classes[2]
|
||||
|
||||
if predefined_type.endswith("-BATTING"):
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
matrix_world = obj.matrix_world
|
||||
|
||||
v0_global = matrix_world @ obj.data.vertices[0].co.xyz
|
||||
v1_global = matrix_world @ obj.data.vertices[1].co.xyz
|
||||
v0 = self.project_point_onto_camera(v0_global)
|
||||
v1 = self.project_point_onto_camera(v1_global)
|
||||
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||
vector = end - start
|
||||
line = self.svg.add(
|
||||
self.svg.line(
|
||||
start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
|
||||
)
|
||||
start_svg = Vector(((x_offset + v0.x), (y_offset - v0.y))) * self.svg_scale
|
||||
end_svg = Vector(((x_offset + v1.x), (y_offset - v1.y))) * self.svg_scale
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
pset_data = tool.Pset.get_element_pset_data(element, "Pset_BBIM_Batting") or {}
|
||||
|
||||
# SVG uses mm as default unit
|
||||
conversion_to_mm = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) / 0.001
|
||||
thickness = pset_data["Thickness"] * conversion_to_mm if "Thickness" in pset_data else 15.0
|
||||
reverse_x = pset_data.get("Reverse pattern direction", False)
|
||||
|
||||
if reverse_x:
|
||||
start_svg, end_svg = end_svg, start_svg
|
||||
|
||||
pattern_dir = (end_svg - start_svg).normalized()
|
||||
pattern_length = (end_svg - start_svg).length
|
||||
segment_width = thickness / 2.5
|
||||
|
||||
segments = ceil(pattern_length / segment_width)
|
||||
end_marker_width = pattern_length - segment_width * (segments - 1)
|
||||
|
||||
points = [start_svg + pattern_dir * segment_width * i for i in range(segments)]
|
||||
marker_id = f"batting-{element.GlobalId}"
|
||||
marker_end_id = f"batting-end-{element.GlobalId}"
|
||||
path_data = f"""M 0 {0.2*thickness}
|
||||
A {0.5*segment_width} {0.2*thickness} 0 0 1 {segment_width} {0.2*thickness}
|
||||
L {0.5*segment_width} {0.8*thickness}
|
||||
M 0 {0.2*thickness}
|
||||
L {0.5*segment_width} {0.8*thickness}
|
||||
A {0.5*segment_width} {0.2*thickness} 0 0 0 {segment_width} {1.0*thickness}
|
||||
M {0.5*segment_width} {0.8*thickness}
|
||||
A {0.5*segment_width} {0.2*thickness} 0 0 1 0 {1.0*thickness}
|
||||
"""
|
||||
path_data = " ".join(path_data.split())
|
||||
|
||||
svg_path = self.svg.path(style="fill: none; stroke:black; stroke-width:0.18", d=path_data)
|
||||
if reverse_x:
|
||||
svg_path.update({"transform": f"scale(-1,-1) translate(-{segment_width}, -{thickness})"})
|
||||
|
||||
marker = self.svg.marker(
|
||||
markerUnits="userSpaceOnUse",
|
||||
insert=(0, thickness / 2),
|
||||
size=(segment_width, thickness),
|
||||
orient="auto",
|
||||
id=marker_id,
|
||||
)
|
||||
marker.add(svg_path)
|
||||
self.svg.add(marker)
|
||||
|
||||
# separate marker for the end of the pattern to truncate it more gracefully
|
||||
marker_end = marker.copy()
|
||||
marker_end.update({"markerWidth": end_marker_width, "id": marker_end_id})
|
||||
self.svg.add(marker_end)
|
||||
|
||||
polyline_style = (
|
||||
f"marker-start: url(#{marker_id}); "
|
||||
f"marker-mid: url(#{marker_id}); stroke: none; "
|
||||
f"marker-end: url(#{marker_end_id}); stroke: none; "
|
||||
)
|
||||
self.svg.add(self.svg.polyline(points=points, class_=" ".join(classes), style=polyline_style))
|
||||
|
||||
else:
|
||||
x_offset = self.raw_width / 2
|
||||
y_offset = self.raw_height / 2
|
||||
matrix_world = obj.matrix_world
|
||||
for edge in obj.data.edges:
|
||||
v0_global = matrix_world @ obj.data.vertices[edge.vertices[0]].co.xyz
|
||||
v1_global = matrix_world @ obj.data.vertices[edge.vertices[1]].co.xyz
|
||||
v0 = self.project_point_onto_camera(v0_global)
|
||||
v1 = self.project_point_onto_camera(v1_global)
|
||||
start = Vector(((x_offset + v0.x), (y_offset - v0.y)))
|
||||
end = Vector(((x_offset + v1.x), (y_offset - v1.y)))
|
||||
vector = end - start
|
||||
line = self.svg.add(
|
||||
self.svg.line(
|
||||
start=tuple(start * self.svg_scale), end=tuple(end * self.svg_scale), class_=" ".join(classes)
|
||||
)
|
||||
)
|
||||
|
||||
def draw_leader_annotation(self, obj):
|
||||
self.draw_line_annotation(obj)
|
||||
|
||||
@@ -486,6 +486,7 @@ class Profile:
|
||||
@interface
|
||||
class Pset:
|
||||
def get_element_pset(cls, element, pset_name): pass
|
||||
def get_element_pset_data(cls, element, pset_name): pass
|
||||
|
||||
|
||||
@interface
|
||||
|
||||
@@ -29,3 +29,8 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
pset = psets.get(pset_name, None)
|
||||
if pset:
|
||||
return tool.Ifc.get().by_id(pset["id"])
|
||||
|
||||
@classmethod
|
||||
def get_element_pset_data(cls, element, pset_name):
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
return psets.get(pset_name, None)
|
||||
|
||||
@@ -707,9 +707,17 @@ def hit_undo():
|
||||
bpy.ops.ed.undo_push(message="UNDO STEP")
|
||||
bpy.ops.ed.undo()
|
||||
|
||||
|
||||
# These definitions are not to be used in tests but simply in debugging failing tests
|
||||
|
||||
|
||||
@given(parsers.parse("I run test code"))
|
||||
@when(parsers.parse("I run test code"))
|
||||
@then(parsers.parse("I run test code"))
|
||||
def run_test_code():
|
||||
pass
|
||||
|
||||
|
||||
@given(parsers.parse("I save sample test files"))
|
||||
@when(parsers.parse("I save sample test files"))
|
||||
@then(parsers.parse("I save sample test files"))
|
||||
|
||||
Reference in New Issue
Block a user