radial_bar_graph.py (8566B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\dynamicresources\client\ui\radial_bar_graph.py 2 from __future__ import division 3 import math 4 import chroma 5 import eveui 6 import threadutils 7 from carbonui import uiconst 8 from carbonui.primitives.container import Container 9 from carbonui.primitives.polygon import Polygon 10 11 class RadialBarGraph(Container): 12 13 def __init__(self, parent, align, data, radius = 120, inner_radius = 100, start_angle = 0.0, end_angle = 360.0, progress = 1.0, color = (1.0, 1.0, 1.0, 1.0), background_color = (1.0, 1.0, 1.0, 0.3), bar_gap_ratio = 2.5, value_radius_modifier = 1.0): 14 self._data = data 15 self._radius = eveui.scale_dpi(radius) 16 self._inner_radius = eveui.scale_dpi(inner_radius) 17 self._start_angle = start_angle 18 self._end_angle = end_angle 19 self._progress = progress 20 self._color = chroma.Color.from_any(color).rgba 21 self._background_color = chroma.Color.from_any(background_color).rgba 22 self._bar_gap_ratio = bar_gap_ratio 23 self._value_radius_modifier = value_radius_modifier 24 super(RadialBarGraph, self).__init__(parent=parent, align=align, height=self._radius * 2, width=self._radius * 2) 25 self._polygon = Polygon(parent=self, align=uiconst.CENTER) 26 self._need_redraw = True 27 self._start_frame_update_loop() 28 29 @property 30 def data(self): 31 return self._data 32 33 @data.setter 34 def data(self, data): 35 self._data = data 36 self._need_redraw = True 37 38 @property 39 def inner_radius(self): 40 return self._inner_radius 41 42 @inner_radius.setter 43 def inner_radius(self, radius): 44 if self._inner_radius != radius: 45 self._inner_radius = radius 46 self._need_redraw = True 47 48 @property 49 def radius(self): 50 return self._radius 51 52 @radius.setter 53 def radius(self, radius): 54 if self.radius != radius: 55 self._radius = radius 56 self._need_redraw = True 57 size = math.ceil(self._radius * 2.0) 58 if size % 2 == 1: 59 size += 1 60 self.width = size 61 self.height = size 62 63 @property 64 def start_angle(self): 65 return self._start_angle 66 67 @start_angle.setter 68 def start_angle(self, angle): 69 if self._start_angle != angle: 70 self._start_angle = angle 71 self._need_redraw = True 72 73 @property 74 def end_angle(self): 75 return self._end_angle 76 77 @end_angle.setter 78 def end_angle(self, angle): 79 if self._end_angle != angle: 80 self._end_angle = angle 81 self._need_redraw = True 82 83 @property 84 def progress(self): 85 return self._progress 86 87 @progress.setter 88 def progress(self, progress): 89 if self._progress != progress: 90 old_progress = self._progress 91 self._progress = progress 92 bar_count = len(self._data) 93 if int(bar_count * old_progress) != int(bar_count * self._progress): 94 self._need_redraw = True 95 96 @property 97 def color(self): 98 return self._color 99 100 @color.setter 101 def color(self, color): 102 new_color = chroma.Color.from_any(color).rgba 103 if new_color != self._color: 104 self._color = new_color 105 self._need_redraw = True 106 107 @property 108 def background_color(self): 109 return self._background_color 110 111 @background_color.setter 112 def background_color(self, color): 113 new_color = chroma.Color.from_any(color).rgba 114 if new_color != self._background_color: 115 self._background_color = new_color 116 self._need_redraw = True 117 118 @property 119 def bar_gap_ratio(self): 120 return self._bar_gap_ratio 121 122 @bar_gap_ratio.setter 123 def bar_gap_ratio(self, bar_gap_ratio): 124 if self._bar_gap_ratio != bar_gap_ratio: 125 self._bar_gap_ratio = bar_gap_ratio 126 self._need_redraw = True 127 128 @property 129 def value_radius_modifier(self): 130 return self._value_radius_modifier 131 132 @value_radius_modifier.setter 133 def value_radius_modifier(self, modifier): 134 if self._value_radius_modifier != modifier: 135 self._value_radius_modifier = modifier 136 self._need_redraw = True 137 138 def _draw(self): 139 value_max = max(self.data) 140 normalized_data = [ v / value_max for v in self.data ] 141 bar_height_max = self._radius - self._inner_radius 142 bar_count = len(normalized_data) 143 bar_step_angle = abs(self._end_angle - self._start_angle) / bar_count 144 gap_sweep = bar_step_angle / (self._bar_gap_ratio + 1.0) 145 start_angle = self._start_angle - 90.0 146 segment_count = max(1, 60 // len(normalized_data)) 147 color = self.color 148 first_background_bar = round(self.progress * bar_count) 149 all_vertices = [] 150 all_colors = [] 151 all_triangles = [] 152 for i, value in enumerate(normalized_data): 153 if i == first_background_bar: 154 color = self.background_color 155 vertices, colors, triangles = _create_arc_polygon(inner_radius=self._inner_radius, outer_radius=self._radius - (1.0 - value) * self._value_radius_modifier * bar_height_max, segments=segment_count, color=color, start_angle=start_angle + bar_step_angle * i, end_angle=start_angle + bar_step_angle * (i + 1) - gap_sweep) 156 vertex_offset = len(all_vertices) 157 all_vertices.extend(vertices) 158 all_colors.extend(colors) 159 all_triangles.extend(((a + vertex_offset, b + vertex_offset, c + vertex_offset) for a, b, c in triangles)) 160 161 self._polygon.AppendVertices(positions=all_vertices, colors=all_colors, transform=None) 162 self._polygon.SetTriangles(all_triangles) 163 164 def _redraw(self): 165 self._polygon.Flush() 166 self._draw() 167 168 @threadutils.threaded 169 def _start_frame_update_loop(self): 170 while not self.destroyed: 171 if self._need_redraw: 172 self._redraw() 173 self._need_redraw = False 174 eveui.wait_for_next_frame() 175 176 177 def _create_arc_polygon(inner_radius, outer_radius, start_angle, end_angle, color = (1.0, 1.0, 1.0, 1.0), segments = 10, feather = 1.0): 178 vertices = [] 179 colors = [] 180 triangles = [] 181 total_sweep = abs(end_angle - start_angle) 182 segment_sweep = total_sweep / segments 183 for i in xrange(segments + 1): 184 a = math.radians(start_angle + i * segment_sweep) 185 x = math.cos(a) 186 y = math.sin(a) 187 vertices.append((x * inner_radius, y * inner_radius)) 188 vertices.append((x * outer_radius, y * outer_radius)) 189 colors.extend((color, color)) 190 191 for i in xrange(segments * 2): 192 triangles.append((i, i + 1, i + 2)) 193 194 shift = len(vertices) 195 fade_color = (color[0], 196 color[1], 197 color[2], 198 0.0) 199 inner_circumference = 2.0 * math.pi * inner_radius 200 inner_px_per_deg = inner_circumference * (total_sweep / 360.0) / total_sweep 201 inner_feather_angle = feather / inner_px_per_deg 202 outer_circumference = 2.0 * math.pi * outer_radius 203 outer_px_per_deg = outer_circumference * (total_sweep / 360.0) / total_sweep 204 outer_feather_angle = feather / outer_px_per_deg 205 radius_inner_feather = inner_radius - feather 206 radius_outer_feather = outer_radius + feather 207 for i in xrange(segments + 1): 208 if i == 0: 209 inner_angle = math.radians(start_angle - inner_feather_angle) 210 outer_angle = math.radians(start_angle - outer_feather_angle) 211 elif i == segments: 212 inner_angle = math.radians(start_angle + i * segment_sweep + inner_feather_angle) 213 outer_angle = math.radians(start_angle + i * segment_sweep + outer_feather_angle) 214 else: 215 inner_angle = outer_angle = math.radians(start_angle + i * segment_sweep) 216 vertices.extend([(math.cos(inner_angle) * radius_inner_feather, math.sin(inner_angle) * radius_inner_feather), (math.cos(outer_angle) * radius_outer_feather, math.sin(outer_angle) * radius_outer_feather)]) 217 colors.extend((fade_color, fade_color)) 218 219 for i in xrange(segments * 2): 220 triangles.append((i, shift + i, shift + i + 2)) 221 triangles.append((shift + i + 2, i + 2, i)) 222 223 vertex_count = len(vertices) 224 triangles.append((0, 1, shift)) 225 triangles.append((shift, shift + 1, 1)) 226 triangles.append((shift - 2, shift - 1, vertex_count - 2)) 227 triangles.append((vertex_count - 2, vertex_count - 1, shift - 1)) 228 return (vertices, colors, triangles)