11"""Viewport decorations"""
2+ import math
23from bpy .types import SpaceView3D
4+ from mathutils import Vector
35import bpy
46import blf
7+ from bpy_extras .view3d_utils import location_3d_to_region_2d
58
69
710class ViewDecorator (object ):
@@ -15,7 +18,10 @@ def install(cls, *args, **kwargs):
1518
1619 @classmethod
1720 def uninstall (cls ):
18- SpaceView3D .draw_handler_remove (cls .installed , 'WINDOW' )
21+ try :
22+ SpaceView3D .draw_handler_remove (cls .installed , 'WINDOW' )
23+ except ValueError :
24+ pass
1925 cls .installed = None
2026
2127
@@ -25,23 +31,79 @@ class DimensionDecorator(ViewDecorator):
2531 - puts metric text next to each segment
2632 """
2733
28- def __init__ (self , scene , context ):
29- self .scene = scene
34+ def __init__ (self , props , context ):
3035 self .context = context
31- self .font_id = 0
36+ self .props = props
37+ self .font_id = 0 # TODO: take font from styles
3238 self .dpi = context .preferences .system .dpi
33- print ("Created decorator" , scene )
3439
3540 def __call__ (self ):
3641 # get active drawing, if any
37- if self .scene .active_drawing_index is None or len (self .scene .drawings ) == 0 :
42+ if self .props .active_drawing_index is None or len (self .props .drawings ) == 0 :
3843 return
39- drawing = self .scene .drawings [self .scene .active_drawing_index ]
44+ drawing = self .props .drawings [self .props .active_drawing_index ]
4045 collection = bpy .data .collections .get ("IfcGroup/" + drawing .name )
46+ # get curve object
4147 if 'IfcAnnotation/Dimension' not in collection .all_objects :
4248 return
43- curve = collection .all_objects ['IfcAnnotation/Dimension' ].data
44- text = repr (curve )
45- blf .position (self .font_id , 100 , 100 , 0 )
46- blf .size (self .font_id , 10 , self .dpi )
49+ curve = collection .all_objects ['IfcAnnotation/Dimension' ]
50+
51+ for segm in self .iter_segments (curve ):
52+ self .draw_label (segm )
53+ self .draw_arrow (segm )
54+
55+ def iter_segments (self , curve ):
56+ """Yields each segment converted to world coords
57+ (v0, v1, length)
58+ """
59+ for spline in curve .data .splines :
60+ points = [curve .matrix_world @ p .co for p in spline .points ]
61+ for i in range (len (points )- 1 ):
62+ p0 = points [i ]
63+ p1 = points [i + 1 ]
64+ length = (p1 - p0 ).length
65+ yield (p0 , p1 , length )
66+
67+ def draw_label (self , segm ):
68+ """Draw text of segment length
69+ aligned and centered at segment middle
70+ """
71+ p0 , p1 , length = segm
72+
73+ # convert to view coords
74+ region = self .context .region
75+ region3d = self .context .region_data
76+ p0 = location_3d_to_region_2d (region , region3d , p0 )
77+ p1 = location_3d_to_region_2d (region , region3d , p1 )
78+
79+ text = f"{ length :.2f} "
80+
81+ ang = - Vector ((1 , 0 )).angle_signed (p1 - p0 )
82+ cos = math .cos (ang )
83+ sin = math .sin (ang )
84+
85+ # midpoint
86+ pos = p0 + (p1 - p0 ) * .5
87+
88+ # TODO: take font size from styles
89+ blf .size (self .font_id , 16 , self .dpi )
90+ w , h = blf .dimensions (self .font_id , text )
91+
92+ # align centered
93+ pos -= Vector ((cos , sin )) * w * 0.5
94+
95+ # add padding
96+ # TODO: take padding from styles and adjust to line width
97+ pos += Vector ((- sin , cos )) * 4
98+
99+ # TODO: handle overlapping of text with arrows for narrow segments
100+
101+ blf .enable (self .font_id , blf .ROTATION )
102+ blf .position (self .font_id , pos .x , pos .y , 0 )
103+
104+ blf .rotation (self .font_id , ang )
47105 blf .draw (self .font_id , text )
106+ blf .disable (self .font_id , blf .ROTATION )
107+
108+ def draw_arrow (self , segm ):
109+ pass
0 commit comments