Skip to content

Commit 43869c3

Browse files
committed
Rebuild text annotations to have leaders in the same object and use text literals. See IfcOpenShell#1153.
1 parent fcfdc17 commit 43869c3

File tree

23 files changed

+694
-163
lines changed

23 files changed

+694
-163
lines changed

src/blenderbim/blenderbim/bim/module/drawing/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
operator.ResizeText,
3636
operator.AddVariable,
3737
operator.RemoveVariable,
38-
operator.PropagateTextData,
3938
operator.RemoveDrawing,
4039
operator.AddDrawingStyle,
4140
operator.RemoveDrawingStyle,
@@ -54,6 +53,9 @@
5453
operator.CleanWireframes,
5554
operator.CopyGrid,
5655
operator.AddSectionsAnnotations,
56+
operator.EditText,
57+
operator.DisableEditingText,
58+
operator.EnableEditingText,
5759
prop.Variable,
5860
prop.Drawing,
5961
prop.Schedule,
@@ -81,6 +83,7 @@
8183
def register():
8284
bpy.types.Scene.DocProperties = bpy.props.PointerProperty(type=prop.DocProperties)
8385
bpy.types.Camera.BIMCameraProperties = bpy.props.PointerProperty(type=prop.BIMCameraProperties)
86+
bpy.types.Object.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
8487
bpy.types.TextCurve.BIMTextProperties = bpy.props.PointerProperty(type=prop.BIMTextProperties)
8588
bpy.app.handlers.load_post.append(handler.toggleDecorationsOnLoad)
8689
bpy.app.handlers.depsgraph_update_pre.append(handler.depsgraph_update_pre_handler)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# BlenderBIM Add-on - OpenBIM Blender Add-on
2+
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
3+
#
4+
# This file is part of BlenderBIM Add-on.
5+
#
6+
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# BlenderBIM Add-on is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
18+
19+
import bpy
20+
import ifcopenshell.util.representation
21+
import blenderbim.tool as tool
22+
23+
24+
def refresh():
25+
TextData.is_loaded = False
26+
27+
28+
class TextData:
29+
data = {}
30+
is_loaded = False
31+
32+
@classmethod
33+
def load(cls):
34+
cls.data = {"attributes": cls.attributes()}
35+
cls.is_loaded = True
36+
37+
@classmethod
38+
def attributes(cls):
39+
element = tool.Ifc.get_entity(bpy.context.active_object)
40+
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in ["TEXT", "TEXT_LEADER"]:
41+
return []
42+
rep = ifcopenshell.util.representation.get_representation(element, "Plan", "Annotation")
43+
text_literal = [i for i in rep.Items if i.is_a("IfcTextLiteral")][0]
44+
return [
45+
{"name": "Literal", "value": text_literal.Literal},
46+
{"name": "BoxAlignment", "value": text_literal.BoxAlignment},
47+
]

src/blenderbim/blenderbim/bim/module/drawing/decoration.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,90 @@ def decorate(self, context, obj):
12281228
self.draw_lines(context, obj, verts, [(0, 1)])
12291229

12301230

1231+
class TextDecorator(BaseDecorator):
1232+
"""Decorator for text objects
1233+
- draws the text next to the origin
1234+
"""
1235+
1236+
objecttype = "TEXT"
1237+
1238+
DEF_GLSL = (
1239+
BaseDecorator.DEF_GLSL
1240+
+ """
1241+
#define ARROW_ANGLE PI / 12.0
1242+
#define ARROW_SIZE 16.0
1243+
"""
1244+
)
1245+
1246+
GEOM_GLSL = """
1247+
uniform vec2 winsize;
1248+
uniform float viewportDrawingScale;
1249+
1250+
layout(lines) in;
1251+
layout(line_strip, max_vertices=MAX_POINTS) out;
1252+
1253+
void main() {
1254+
vec4 clip2win = matCLIP2WIN();
1255+
vec4 win2clip = matWIN2CLIP();
1256+
1257+
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
1258+
1259+
vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1);
1260+
vec4 edge = p1w - p0w, dir = normalize(edge);
1261+
1262+
vec4 p;
1263+
1264+
vec4 head[3];
1265+
arrow_head(dir, viewportDrawingScale * ARROW_SIZE, ARROW_ANGLE, head);
1266+
1267+
// start edge arrow
1268+
gl_Position = p0;
1269+
EmitVertex();
1270+
p = p0w + head[1];
1271+
gl_Position = WIN2CLIP(p);
1272+
EmitVertex();
1273+
p = p0w + head[2];
1274+
gl_Position = WIN2CLIP(p);
1275+
EmitVertex();
1276+
gl_Position = p0;
1277+
EmitVertex();
1278+
EndPrimitive();
1279+
1280+
// end edge arrow
1281+
gl_Position = p1;
1282+
EmitVertex();
1283+
p = p1w - head[1];
1284+
gl_Position = WIN2CLIP(p);
1285+
EmitVertex();
1286+
p = p1w - head[2];
1287+
gl_Position = WIN2CLIP(p);
1288+
EmitVertex();
1289+
gl_Position = p1;
1290+
EmitVertex();
1291+
EndPrimitive();
1292+
1293+
// stem, with gaps for arrows
1294+
p = p0w + head[0];
1295+
gl_Position = WIN2CLIP(p);
1296+
EmitVertex();
1297+
p = p1w - head[0];
1298+
gl_Position = WIN2CLIP(p);
1299+
EmitVertex();
1300+
EndPrimitive();
1301+
}
1302+
"""
1303+
1304+
def decorate(self, context, obj):
1305+
self.draw_labels(context, obj)
1306+
1307+
def draw_labels(self, context, obj):
1308+
region = context.region
1309+
region3d = context.region_data
1310+
dir = Vector((1, 0))
1311+
pos = location_3d_to_region_2d(region, region3d, obj.matrix_world.translation)
1312+
self.draw_label(context, obj.BIMTextProperties.value, pos, dir, gap=0, center=False, vcenter=False)
1313+
1314+
12311315
class DecorationsHandler:
12321316
decorators_classes = [
12331317
DimensionDecorator,
@@ -1241,6 +1325,7 @@ class DecorationsHandler:
12411325
StairDecorator,
12421326
BreakDecorator,
12431327
SectionViewDecorator,
1328+
TextDecorator,
12441329
]
12451330

12461331
installed = None

src/blenderbim/blenderbim/bim/module/drawing/operator.py

Lines changed: 67 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import ifcopenshell.util.representation
3333
import blenderbim.bim.schema
3434
import blenderbim.tool as tool
35+
import blenderbim.core.drawing as core
3536
import blenderbim.bim.module.drawing.svgwriter as svgwriter
3637
import blenderbim.bim.module.drawing.annotation as annotation
3738
import blenderbim.bim.module.drawing.sheeter as sheeter
@@ -57,6 +58,13 @@ def open_with_user_command(user_command, path):
5758
webbrowser.open("file://" + path)
5859

5960

61+
class Operator:
62+
def execute(self, context):
63+
IfcStore.execute_ifc_operator(self, context)
64+
blenderbim.bim.handler.refresh_ui_data()
65+
return {"FINISHED"}
66+
67+
6068
class AddDrawing(bpy.types.Operator):
6169
bl_idname = "bim.add_drawing"
6270
bl_label = "Add Drawing"
@@ -79,7 +87,7 @@ def _execute(self, context):
7987
view_collection = bpy.data.collections.new("IfcGroup/" + new.name)
8088
views_collection.children.link(view_collection)
8189
camera = bpy.data.objects.new(new.name, bpy.data.cameras.new(new.name))
82-
camera.location = (0, 0, 1.7) # The view shall be 1.7m above the origin
90+
camera.location = (0, 0, 1.5) # The view shall be 1.5m above the origin
8391
camera.data.type = "ORTHO"
8492
camera.data.ortho_scale = 50 # The default of 6m is too small
8593
camera.data.clip_end = 10 # A slightly more reasonable default
@@ -573,28 +581,45 @@ def _execute(self, context):
573581
)
574582
if not subcontext:
575583
return {"FINISHED"}
576-
if self.data_type == "text":
577-
if context.selected_objects:
578-
for selected_object in context.selected_objects:
579-
obj = annotation.Annotator.add_text(context, related_element=selected_object)
580-
else:
581-
obj = annotation.Annotator.add_text(context)
582-
else:
583-
obj = annotation.Annotator.get_annotation_obj(self.obj_name, self.data_type, context)
584-
if self.obj_name == "Break":
585-
obj = annotation.Annotator.add_plane_to_annotation(obj, context)
586-
else:
587-
obj = annotation.Annotator.add_line_to_annotation(obj, context)
584+
# TODO: reimplement bulk smart tagging
585+
# if self.data_type == "text":
586+
# if context.selected_objects:
587+
# for selected_object in context.selected_objects:
588+
# obj = annotation.Annotator.add_text(context, related_element=selected_object)
589+
# else:
590+
# obj = annotation.Annotator.add_text(context)
591+
# else:
592+
obj = annotation.Annotator.get_annotation_obj(self.object_type, self.data_type, context)
593+
if self.object_type == "BREAKLINE":
594+
obj = annotation.Annotator.add_plane_to_annotation(obj, context)
595+
elif self.object_type != "TEXT":
596+
obj = annotation.Annotator.add_line_to_annotation(obj, context)
588597

589598
if not obj.BIMObjectProperties.ifc_definition_id:
590-
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcAnnotation", context_id=subcontext.id())
599+
ifc_representation_class = ""
600+
if self.object_type == "TEXT":
601+
ifc_representation_class = "IfcTextLiteral"
602+
elif self.object_type == "TEXT_LEADER":
603+
ifc_representation_class = "IfcGeometricCurveSet/IfcTextLiteral"
604+
bpy.ops.bim.assign_class(
605+
obj=obj.name,
606+
ifc_class="IfcAnnotation",
607+
context_id=subcontext.id(),
608+
ifc_representation_class=ifc_representation_class,
609+
)
610+
element = tool.Ifc.get_entity(obj)
611+
element.ObjectType = self.object_type
612+
camera = tool.Ifc.get_entity(context.scene.camera)
613+
group = [r for r in camera.HasAssignments if r.is_a("IfcRelAssignsToGroup")][0].RelatingGroup
614+
bpy.ops.bim.assign_group(product=obj.name, group=group.id())
591615
else:
592616
bpy.ops.bim.update_representation(obj=obj.name)
593617

594618
bpy.ops.object.select_all(action="DESELECT")
595619
context.view_layer.objects.active = obj
596620
obj.select_set(True)
597-
bpy.ops.object.mode_set(mode="EDIT")
621+
if obj.data:
622+
bpy.ops.object.mode_set(mode="EDIT")
598623
return {"FINISHED"}
599624

600625

@@ -888,29 +913,6 @@ def execute(self, context):
888913
return {"FINISHED"}
889914

890915

891-
class PropagateTextData(bpy.types.Operator):
892-
bl_idname = "bim.propagate_text_data"
893-
bl_label = "Propagate Text Data"
894-
bl_options = {"REGISTER", "UNDO"}
895-
896-
def execute(self, context):
897-
source = context.active_object
898-
for obj in context.selected_objects:
899-
if obj == source:
900-
continue
901-
obj.data.body = source.data.body
902-
obj.data.align_x = source.data.align_x
903-
obj.data.align_y = source.data.align_y
904-
obj.data.BIMTextProperties.font_size = source.data.BIMTextProperties.font_size
905-
obj.data.BIMTextProperties.symbol = source.data.BIMTextProperties.symbol
906-
obj.data.BIMTextProperties.variables.clear()
907-
for variable in source.data.BIMTextProperties.variables:
908-
new_variable = obj.data.BIMTextProperties.variables.add()
909-
new_variable.name = variable.name
910-
new_variable.prop_key = variable.prop_key
911-
return {"FINISHED"}
912-
913-
914916
class RemoveDrawing(bpy.types.Operator):
915917
bl_idname = "bim.remove_drawing"
916918
bl_label = "Remove Drawing"
@@ -1380,3 +1382,30 @@ def annotation(drawing, points):
13801382
view_coll.objects.link(obj)
13811383

13821384
return {"FINISHED"}
1385+
1386+
1387+
class EditText(bpy.types.Operator, Operator):
1388+
bl_idname = "bim.edit_text"
1389+
bl_label = "Edit Text"
1390+
bl_options = {"REGISTER", "UNDO"}
1391+
1392+
def _execute(self, context):
1393+
core.edit_text(tool.Ifc, tool.Drawing, obj=context.active_object)
1394+
1395+
1396+
class EnableEditingText(bpy.types.Operator, Operator):
1397+
bl_idname = "bim.enable_editing_text"
1398+
bl_label = "Enable Editing Text"
1399+
bl_options = {"REGISTER", "UNDO"}
1400+
1401+
def _execute(self, context):
1402+
core.enable_editing_text(tool.Drawing, obj=context.active_object)
1403+
1404+
1405+
class DisableEditingText(bpy.types.Operator, Operator):
1406+
bl_idname = "bim.disable_editing_text"
1407+
bl_label = "Disable Editing Text"
1408+
bl_options = {"REGISTER", "UNDO"}
1409+
1410+
def _execute(self, context):
1411+
core.disable_editing_text(tool.Drawing, obj=context.active_object)

src/blenderbim/blenderbim/bim/module/drawing/prop.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ def refreshTitleblocks(self, context):
169169
def toggleDecorations(self, context):
170170
toggle = self.should_draw_decorations
171171
if toggle:
172+
# TODO: design a proper text variable templating renderer
173+
collection = context.scene.camera.users_collection[0]
174+
for obj in collection.objects:
175+
tool.Drawing.update_text_value(obj)
172176
decoration.DecorationsHandler.install(context)
173177
else:
174178
decoration.DecorationsHandler.uninstall()
@@ -353,6 +357,9 @@ def update_representation(self, obj):
353357

354358

355359
class BIMTextProperties(PropertyGroup):
360+
is_editing: BoolProperty(name="Is Editing", default=False)
361+
attributes: CollectionProperty(name="Attributes", type=Attribute)
362+
value: StringProperty(name="Value", default="TEXT")
356363
font_size: EnumProperty(
357364
items=[
358365
("1.8", "1.8 - Small", ""),
@@ -361,6 +368,7 @@ class BIMTextProperties(PropertyGroup):
361368
("5.0", "5.0 - Header", ""),
362369
("7.0", "7.0 - Title", ""),
363370
],
371+
default="2.5",
364372
update=refreshFontSize,
365373
name="Font Size",
366374
)

0 commit comments

Comments
 (0)