diff --git a/src/blenderbim/blenderbim/bim/data/pset/Psets_BBIM_Annotation.ifc b/src/blenderbim/blenderbim/bim/data/pset/Psets_BBIM_Annotation.ifc index 5eb67995a3..0acd8088c8 100644 --- a/src/blenderbim/blenderbim/bim/data/pset/Psets_BBIM_Annotation.ifc +++ b/src/blenderbim/blenderbim/bim/data/pset/Psets_BBIM_Annotation.ifc @@ -14,9 +14,9 @@ DATA; #7=IFCSIMPLEPROPERTYTEMPLATE('082PndS6v2kBOiJoSboMnh',$,'Reverse pattern direction','Reverse batting pattern (swap starting and ending points)',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); #8=IFCPROPERTYSETTEMPLATE('1Dx2EiZnP67xotInXpwz90',$,'BBIM_Section','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#9,#10,#11,#12,#13)); #9=IFCSIMPLEPROPERTYTEMPLATE('2a_9s8spHDc9dZHtgg71XL',$,'ShowStartArrow','Display start arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); -#10=IFCSIMPLEPROPERTYTEMPLATE('3i6SH_GbT7zhKea$wVE56A',$,'StartArrowSymbol','Custom symbol for the start of the section marker arrow.',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); +#10=IFCSIMPLEPROPERTYTEMPLATE('3i6SH_GbT7zhKea$wVE56A',$,'StartArrowSymbol','Custom symbol for the start of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #11=IFCSIMPLEPROPERTYTEMPLATE('1DtsPn5a9FG8$zXHDDMavY',$,'ShowEndArrow','Display end arrow.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); -#12=IFCSIMPLEPROPERTYTEMPLATE('2$6U0mLI9AiPRWeBabdY3u',$,'EndArrowSymbol','Custom symbol for the end of the section marker arrow',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); +#12=IFCSIMPLEPROPERTYTEMPLATE('2$6U0mLI9AiPRWeBabdY3u',$,'EndArrowSymbol','Custom symbol for the end of the section marker arrow. Need to make sure it''s present in "symbols.svg".',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #13=IFCSIMPLEPROPERTYTEMPLATE('1naFqntIL7igCY7hCaE7kq',$,'HasConnectedSectionLine','Connect or disconnect section markers with line (by default = True).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); ENDSEC; END-ISO-10303-21; diff --git a/src/blenderbim/blenderbim/bim/module/drawing/data.py b/src/blenderbim/blenderbim/bim/module/drawing/data.py index 1effddfb78..b5d6f9776e 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/data.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/data.py @@ -126,6 +126,7 @@ class SchedulesData: class DecoratorData: + # stores 1 type of data per object data = {} @classmethod @@ -142,22 +143,42 @@ class DecoratorData: return thickness @classmethod - def get_section_pset_data(cls, obj): + def get_section_markers_display_data(cls, obj): result = cls.data.get(obj.name, None) if result is not None: return result - element = tool.Ifc.get_entity(obj) - if element: - # default values - pset_data = { - "HasConnectedSectionLine": True, - "ShowStartArrow": True, - "StartArrowSymbol": "", - "ShowEndArrow": True, - "EndArrowSymbol": "", - } - obj_pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Section") or {} - pset_data.update(obj_pset_data) - cls.data[obj.name] = pset_data - return pset_data + element = tool.Ifc.get_entity(obj) + if not element: + return + + # default values + pset_data = { + "HasConnectedSectionLine": True, + "ShowStartArrow": True, + "StartArrowSymbol": "", + "ShowEndArrow": True, + "EndArrowSymbol": "", + } + obj_pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Section") or {} + pset_data.update(obj_pset_data) + + # create more usable display data + start_symbol = pset_data["StartArrowSymbol"] + end_symbol = pset_data["EndArrowSymbol"] + display_data = { + "start": { + "add_circle": pset_data["ShowStartArrow"] and not start_symbol, + "add_symbol": pset_data["ShowStartArrow"] or bool(start_symbol), + "symbol": start_symbol or "section-arrow", + }, + "end": { + "add_circle": pset_data["ShowEndArrow"] and not end_symbol, + "add_symbol": pset_data["ShowEndArrow"] or bool(end_symbol), + "symbol": end_symbol or "section-arrow", + }, + "connect_markers": pset_data["HasConnectedSectionLine"], + } + + cls.data[obj.name] = display_data + return display_data diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py index 318c4351c7..7ddb8df279 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py @@ -1812,6 +1812,10 @@ class SectionDecorator(LevelDecorator): uniform vec2 winsize; uniform float viewportDrawingScale; uniform float connect_markers; + uniform float display_start_symbol; + uniform float display_end_symbol; + uniform float display_start_circle; + uniform float display_end_circle; layout(lines) in; layout(line_strip, max_vertices=MAX_POINTS) out; @@ -1835,7 +1839,7 @@ class SectionDecorator(LevelDecorator): vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1); vec4 edge = p1w - p0w, side = normalize(edge); vec4 gap = side * viewportDrawingScale * CIRCLE_SIZE; - vec4 dir = vec4(cross(vec3(side.xy, 0), vec3(0, 0, 1)).xy, 0, 0); + vec4 edge_ortho_dir = -vec4(cross(vec3(side.xy, 0), vec3(0, 0, 1)).xy, 0, 0); vec4 p; vec4 head[CIRCLE_SEGS]; @@ -1844,66 +1848,72 @@ class SectionDecorator(LevelDecorator): vec4 head5[5]; // start edge circle - for(int i=0; i 0) { + for(int i=0; i 0) { + // start edge triangle + triangle_head(side, edge_ortho_dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5); + p = p0w + head5[0]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p0w + head5[1]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p0w + head5[2]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p0w + head5[3]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p0w + head5[4]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + EndPrimitive(); + } // end edge circle - for(int i=0; i 0) { + for(int i=0; i 0) { + triangle_head(side, edge_ortho_dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5); + p = p1w + head5[0]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p1w + head5[1]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p1w + head5[2]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p1w + head5[3]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p1w + head5[4]; + gl_Position = WIN2CLIP(p); + EmitVertex(); + EndPrimitive(); + } float divider_line_size; if (connect_markers > 0) { @@ -1912,30 +1922,39 @@ class SectionDecorator(LevelDecorator): divider_line_size = CIRCLE_SIZE * 3; } + // NOTE: basically we consider first vertex to be + // start and the second to be the end marker + // which is a bit inconsistent with svg + // but not that anyone will use multiple edge section annotation + // start reference divider - p = p0w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * divider_line_size; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE; - gl_Position = WIN2CLIP(p); - EmitVertex(); - EndPrimitive(); + if (display_start_symbol > 0) { + p = p0w + side * viewportDrawingScale * divider_line_size; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p0w - side * viewportDrawingScale * CIRCLE_SIZE; + gl_Position = WIN2CLIP(p); + EmitVertex(); + EndPrimitive(); + } // end reference divider - p = p1w + normalize(vec4(-1, 0, 0, 0)) * viewportDrawingScale * CIRCLE_SIZE; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w + normalize(vec4(1, 0, 0, 0)) * viewportDrawingScale * divider_line_size; - gl_Position = WIN2CLIP(p); - EmitVertex(); - EndPrimitive(); + if (display_end_symbol > 0) { + p = p1w + side * viewportDrawingScale * CIRCLE_SIZE; + gl_Position = WIN2CLIP(p); + EmitVertex(); + p = p1w - side * viewportDrawingScale * divider_line_size; + gl_Position = WIN2CLIP(p); + EmitVertex(); + EndPrimitive(); + } // stem if (connect_markers > 0) { - p = p0w + gap; + p = p0w + (display_start_symbol > 0 ? gap : vec4(0)); gl_Position = WIN2CLIP(p); EmitVertex(); - p = p1w - gap; + p = p1w - (display_end_symbol > 0 ? gap : vec4(0)); gl_Position = WIN2CLIP(p); EmitVertex(); EndPrimitive(); @@ -1948,13 +1967,23 @@ class SectionDecorator(LevelDecorator): verts, idxs = self.get_editmesh_geom(obj) else: verts, idxs = self.get_mesh_geom(obj) - vert_map = {v.freeze(): (obj.matrix_world.inverted() @ v).x for v in verts} - verts = sorted(verts, key=lambda v: vert_map[v], reverse=True) + # TODO: add dashed line to shader # mind the vertices limit - section_pset_data = DecoratorData.get_section_pset_data(obj) - connect_markers = section_pset_data["HasConnectedSectionLine"] - self.draw_lines(context, obj, verts, idxs, extra_float_kwargs={"connect_markers": float(connect_markers)}) + display_data = DecoratorData.get_section_markers_display_data(obj) + self.draw_lines( + context, + obj, + verts, + idxs, + extra_float_kwargs={ + "connect_markers": float(display_data["connect_markers"]), + "display_start_symbol": float(display_data["start"]["add_symbol"]), + "display_end_symbol": float(display_data["end"]["add_symbol"]), + "display_start_circle": float(display_data["start"]["add_circle"]), + "display_end_circle": float(display_data["end"]["add_circle"]), + }, + ) class TextDecorator(BaseDecorator): diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py index dc88c8ec53..5c2840f161 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py @@ -454,13 +454,17 @@ class SvgWriter: self.svg.add(self.svg.polyline(points=points, class_=" ".join(classes), style=polyline_style)) elif predefined_type == "SECTION": - section_pset_data = DecoratorData.get_section_pset_data(obj) - connect_markers = section_pset_data["HasConnectedSectionLine"] + display_data = DecoratorData.get_section_markers_display_data(obj) + connect_markers = display_data["connect_markers"] + if connect_markers: for edge in obj.data.edges: draw_simple_edge_annotation(*edge.vertices[:]) else: for edge in obj.data.edges: + v0_marker_position = "start" if edge.vertices[0] == 0 else "end" + v1_marker_position = "start" if edge.vertices[1] == 0 else "end" + 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) @@ -472,20 +476,23 @@ class SvgWriter: circle_radius = 5 segment_size = circle_radius * 3 - self.svg.add( - self.svg.line( - start=start * self.svg_scale, - end=start * self.svg_scale + edge_dir * segment_size, - class_=" ".join(classes), + if display_data[v0_marker_position]["add_symbol"]: + self.svg.add( + self.svg.line( + start=start * self.svg_scale, + end=start * self.svg_scale + edge_dir * segment_size, + class_=" ".join(classes), + ) ) - ) - self.svg.add( - self.svg.line( - start=end * self.svg_scale, - end=end * self.svg_scale - edge_dir * segment_size, - class_=" ".join(classes), + + if display_data[v1_marker_position]["add_symbol"]: + self.svg.add( + self.svg.line( + start=end * self.svg_scale, + end=end * self.svg_scale - edge_dir * segment_size, + class_=" ".join(classes), + ) ) - ) else: for edge in obj.data.edges: @@ -507,58 +514,79 @@ class SvgWriter: self.draw_line_annotation(obj) - v1 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, 0))) - v2 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, -1))) - angle = -math.degrees((v2 - v1).xy.angle_signed(Vector((0, 1)))) + display_data = DecoratorData.get_section_markers_display_data(obj) - for vert in obj.data.vertices: - point = obj.matrix_world @ vert.co - symbol_position = self.project_point_onto_camera(point) - symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y))) - transform = "rotate({}, {}, {})".format( - angle, - (symbol_position * self.svg_scale)[0], - (symbol_position * self.svg_scale)[1], - ) + for edge in obj.data.edges: + edge_verts = [obj.data.vertices[v_i] for v_i in edge.vertices] - self.svg.add( - self.svg.use("#section-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform) - ) - self.svg.add(self.svg.use("#section-tag", insert=tuple(symbol_position * self.svg_scale))) + # convert edge vertiices coordinates to svg space + # to calculate the edge angle and for later use + edge_verts_svg = [] + for vert in edge_verts: + point = obj.matrix_world @ vert.co + symbol_position = self.project_point_onto_camera(point) + symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y))) + symbol_position_svg = symbol_position * self.svg_scale + edge_verts_svg.append(symbol_position_svg) - reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj)) - text_position = list(symbol_position * self.svg_scale) - text_style = { - "text-anchor": "middle", - "alignment-baseline": "middle", - "dominant-baseline": "middle", - } - self.svg.add(self.svg.text(reference_id, insert=(text_position[0], text_position[1] - 2.5), class_="SECTION", **text_style)) - self.svg.add(self.svg.text(sheet_id, insert=(text_position[0], text_position[1] + 2.5), class_="SECTION", **text_style)) + edge_dir = (edge_verts_svg[1] - edge_verts_svg[0]).normalized() + angle = degrees( edge_dir.xy.angle_signed( Vector([1,0]) ) ) + + for v_i, symbol_position_svg in zip(edge.vertices, edge_verts_svg): + current_marker_position = "start" if v_i == 0 else "end" + + # marker arrow symbol + if display_data[current_marker_position]["add_symbol"]: + transform = "rotate({}, {}, {})".format(angle, *symbol_position_svg.xy) + symbol_id = display_data[current_marker_position]["symbol"] + self.svg.add(self.svg.use(f"#{symbol_id}", insert=symbol_position_svg, transform=transform)) + + # marker circle and it's text + if display_data[current_marker_position]["add_circle"]: + self.svg.add(self.svg.use("#section-tag", insert=symbol_position_svg)) + + reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj)) + text_position = symbol_position_svg + text_style = { + "text-anchor": "middle", + "alignment-baseline": "middle", + "dominant-baseline": "middle", + } + self.svg.add( + self.svg.text( + reference_id, + insert=(text_position[0], text_position[1] - 2.5), + class_="SECTION", + **text_style, + ) + ) + self.svg.add( + self.svg.text( + sheet_id, + insert=(text_position[0], text_position[1] + 2.5), + class_="SECTION", + **text_style + ) + ) def draw_elevation_annotation(self, obj): x_offset = self.raw_width / 2 y_offset = self.raw_height / 2 symbol_position = self.project_point_onto_camera(obj.location) symbol_position = Vector(((x_offset + symbol_position.x), (y_offset - symbol_position.y))) + symbol_position_svg = symbol_position * self.svg_scale v1 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, 0))) v2 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, -1))) angle = -math.degrees((v2 - v1).xy.angle_signed(Vector((0, 1)))) - transform = "rotate({}, {}, {})".format( - angle, - (symbol_position * self.svg_scale)[0], - (symbol_position * self.svg_scale)[1], - ) + transform = "rotate({}, {}, {})".format(angle, *symbol_position_svg.xy) - self.svg.add( - self.svg.use("#elevation-arrow", insert=tuple(symbol_position * self.svg_scale), transform=transform) - ) - self.svg.add(self.svg.use("#elevation-tag", insert=tuple(symbol_position * self.svg_scale))) + self.svg.add(self.svg.use("#elevation-arrow", insert=symbol_position_svg, transform=transform)) + self.svg.add(self.svg.use("#elevation-tag", insert=symbol_position_svg)) reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj)) - text_position = list(symbol_position * self.svg_scale) + text_position = symbol_position_svg text_style = { "text-anchor": "middle", "alignment-baseline": "middle", @@ -766,7 +794,7 @@ class SvgWriter: bm.verts.index_update() bm.edges.index_update() new_verts = [bm.verts.new(p) for p in points_chunk] - new_edges = [bm.edges.new( (new_verts[e[0]], new_verts[e[1]]) ) for e in ((0, 1), (1, 2))] + new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in ((0, 1), (1, 2))] self.draw_svg_3point_arc(obj, bm) def draw_svg_3point_arc(self, obj, bm): @@ -967,7 +995,7 @@ class SvgWriter: run = (B - O).length if run != 0: angle_tg = rise / run - angle = round( degrees( atan(angle_tg) )) + angle = round(degrees(atan(angle_tg))) else: angle = 90