polygon.py (20077B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\drawingpolygon\polygon.py 2 from drawingpolygon.components.fill import PolygonFill 3 from drawingpolygon.components.perimeter import PolygonPerimeter, PolygonSegment 4 from drawingpolygon.components.vertex import PolygonVertex 5 import drawingpolygon.cursors as cursors 6 from carbon.common.script.util.timerstuff import AutoTimer 7 from carbonui import uiconst 8 from carbonui.primitives.container import Container 9 from gametime import GetTimeDiffInMs, GetWallclockTime 10 from signals.signal import Signal 11 import trinity 12 DRAG_UPDATE_TIMER_MSEC = 50 13 IS_RIGHT_CLICK_DELETE_ENABLED = False 14 VERTEX_CLOSED_TEXTURE_PATH = 'res:/UI/Texture/classes/DrawingTool/vertex.png' 15 VERTEX_OPEN_TEXTURE_PATH = 'res:/UI/Texture/classes/DrawingTool/vertex_open_ended.png' 16 VERTEX_DRAGGING_TEXTURE_PATH = 'res:/UI/Texture/classes/DrawingTool/vertex_moving.png' 17 18 def read(attributes, name, default): 19 value = attributes.get(name, default) 20 if value is not None: 21 return value 22 return default 23 24 25 class Polygon(Container): 26 default_state = uiconst.UI_PICKCHILDREN 27 default_align = uiconst.TOALL 28 default_idx = 0 29 default_max_vertices = 10 30 default_perimeter_width = 2 31 default_perimeter_width_factor = 2 32 default_show_vertices = True 33 default_show_fill = True 34 default_fill_bg_color_selected = (0.0, 0.0, 0.0, 0.6) 35 default_fill_bg_color_hovered = (0.0, 0.0, 0.0, 0.6) 36 default_fill_bg_color_deselected = (0.0, 0.0, 0.0, 0.0) 37 default_fill_color_selected = (0.2, 1.0, 1.0, 0.7) 38 default_fill_color_hovered = (0.2, 1.0, 1.0, 0.4) 39 default_fill_color_deselected = (0.0, 0.0, 0.0, 0.0) 40 default_perimeter_color = (0.2, 1.0, 1.0, 1.0) 41 default_perimeter_color_outer = (0.0, 0.0, 0.0, 1.0) 42 FILL_COLOR_SELECTED_INVALID = (0.97, 0.09, 0.13, 0.7) 43 FILL_COLOR_HOVERED_INVALID = (0.97, 0.09, 0.13, 0.5) 44 FILL_COLOR_DESELECTED_INVALID = (0.0, 0.0, 0.0, 0.0) 45 PERIMETER_COLOR_INVALID = (0.97, 0.09, 0.13, 1.0) 46 NO_COLOR = (0.0, 0.0, 0.0, 0.0) 47 48 def ApplyAttributes(self, attributes): 49 self.drawing_area = attributes.get('area') 50 self.max_vertices = read(attributes, 'max_vertices', self.default_max_vertices) 51 self.perimeter_width = read(attributes, 'perimeter_width', self.default_perimeter_width) 52 self.perimeter_width_factor = read(attributes, 'perimeter_width_factor', self.default_perimeter_width_factor) 53 self.show_vertices = read(attributes, 'show_vertices', self.default_show_vertices) 54 self.show_fill = read(attributes, 'show_fill', self.default_show_fill) 55 self.fill_bg_color_selected = read(attributes, 'fill_bg_color_selected', self.default_fill_bg_color_selected) 56 self.fill_bg_color_hovered = read(attributes, 'fill_bg_color_hovered', self.default_fill_bg_color_hovered) 57 self.fill_bg_color_deselected = read(attributes, 'fill_bg_color_deselected', self.default_fill_bg_color_deselected) 58 self.fill_color_selected = read(attributes, 'fill_color_selected', self.default_fill_color_selected) 59 self.fill_color_hovered = read(attributes, 'fill_color_hovered', self.default_fill_color_hovered) 60 self.fill_color_deselected = read(attributes, 'fill_color_deselected', self.default_fill_color_deselected) 61 self.perimeter_color = read(attributes, 'perimeter_color', self.default_perimeter_color) 62 self.perimeter_color_outer = read(attributes, 'perimeter_color_outer', self.default_perimeter_color_outer) 63 self.set_order(order=attributes.get('order')) 64 self.vertices = [] 65 self.active_vertex = None 66 self.is_complete = False 67 self.is_selected = True 68 self.is_valid = True 69 self.is_moving = False 70 self.is_vertex_moving = False 71 self.is_hovered = False 72 self.time_of_start = GetWallclockTime() 73 self.time_of_last_edit = GetWallclockTime() 74 self.vertex_drag_updates_thread = None 75 self.drag_updates_thread = None 76 self.drag_mouse_position = None 77 self.original_vertex_mouse_position = None 78 self.wip_segment = None 79 self.on_edit_started = Signal(signalName='on_edit_started') 80 self.on_deleted = Signal(signalName='on_deleted') 81 self.on_completed = Signal(signalName='on_completed') 82 self.on_wip_segment_added = Signal(signalName='on_wip_segment_added') 83 self.on_wip_segment_removed = Signal(signalName='on_wip_segment_removed') 84 super(Polygon, self).ApplyAttributes(attributes) 85 self._draw_fill() 86 self._draw_perimeter() 87 88 def Close(self): 89 self._set_active_vertex(None) 90 self._remove_x_button() 91 self.on_deleted(self.order) 92 super(Polygon, self).Close() 93 94 def get_data(self): 95 return {'vertices': [ self.drawing_area.transform_coordinates_to_proportions(vertex.x, vertex.y) for vertex in self.vertices ]} 96 97 def _get_top_right(self): 98 x = max([ vertex.x for vertex in self.vertices ]) 99 y = min([ vertex.y for vertex in self.vertices ]) 100 return (x, y) 101 102 def _get_right(self): 103 x, y = (0, 0) 104 for vertex in self.vertices: 105 if vertex.x > x: 106 x = vertex.x 107 y = vertex.y 108 109 return (x, y) 110 111 def get_time_spent(self): 112 return max(0, GetTimeDiffInMs(self.time_of_start, self.time_of_last_edit)) 113 114 def scale(self, x_scale, y_scale): 115 self.Hide() 116 self._remove_x_button() 117 for vertex in self.vertices: 118 vertex.update_position(vertex.x * x_scale, vertex.y * y_scale) 119 120 self._redraw_polygon() 121 self._add_x_button() 122 self.Show() 123 124 def set_order(self, order): 125 self.order = order 126 self.name = 'Polygon%d' % order 127 128 def set_arrangement_order(self, idx): 129 self.SetOrder(idx) 130 131 def set_cursor(self, cursor): 132 self.fill.set_cursor(cursor) 133 self.perimeter.set_cursor(cursor) 134 135 def reset_cursor(self): 136 self.fill.reset_cursor() 137 self.perimeter.reset_cursor() 138 139 def set_perimeter_color(self, perimeter_color): 140 self.perimeter_color = perimeter_color 141 self.perimeter_inner.Flush() 142 self.perimeter_inner.SetRGBA(*perimeter_color) 143 for vertex in self.vertices: 144 self.perimeter_inner.add_vertex(vertex.x, vertex.y) 145 146 if self.is_complete: 147 self.perimeter_inner.isLoop = True 148 149 def select(self): 150 self.is_selected = True 151 self._redraw_polygon() 152 if self.is_complete: 153 self.set_cursor(cursors.CURSOR_SELECTOR_TARGET) 154 self._add_x_button() 155 156 def deselect(self): 157 self.is_selected = False 158 self._remove_x_button() 159 self._redraw_polygon() 160 self.reset_cursor() 161 162 def on_hover(self): 163 self.is_hovered = True 164 self._redraw_polygon() 165 166 def on_dehover(self): 167 self.is_hovered = False 168 self._redraw_polygon() 169 170 def enable(self): 171 for vertex in self.vertices: 172 vertex.Enable() 173 174 self.perimeter.Enable() 175 self.fill.Enable() 176 177 def disable(self): 178 for vertex in self.vertices: 179 vertex.Disable() 180 181 self.perimeter.Disable() 182 self.fill.Disable() 183 184 def update_validation(self, is_valid): 185 if self.is_valid == is_valid: 186 return 187 self.is_valid = is_valid or not self.is_complete 188 self._redraw_polygon() 189 190 def add_point(self, x, y): 191 if self.is_complete: 192 return 193 self._add_segment(x, y) 194 self._add_vertex(x, y) 195 if self.max_vertices and len(self.vertices) >= self.max_vertices: 196 self.autocomplete_polygon() 197 198 def remove_point(self, index): 199 self._remove_segment(index) 200 self._remove_vertex(index) 201 202 def get_point_amount(self): 203 return len(self.vertices) 204 205 def _draw_perimeter(self): 206 self.perimeter = PolygonPerimeter(name='Perimeter', parent=self, polygon=self, color=self.perimeter_color_outer, lineWidth=self.perimeter_width * self.perimeter_width_factor) 207 self.perimeter.on_left_clicked.connect(self.on_left_clicked) 208 if IS_RIGHT_CLICK_DELETE_ENABLED: 209 self.perimeter.on_right_clicked.connect(self.on_right_clicked) 210 self.perimeter.on_drag_started.connect(self.on_drag_started) 211 self.perimeter.on_drag_ended.connect(self.on_drag_ended) 212 self.perimeter.on_hover_started.connect(self.on_hover) 213 self.perimeter.on_hover_ended.connect(self.on_dehover) 214 self.perimeter_inner = PolygonPerimeter(name='PerimeterInner', parent=self, polygon=self, color=self._get_perimeter_color(), lineWidth=self.perimeter_width, state=uiconst.UI_DISABLED) 215 216 def _draw_fill(self): 217 fill_color = self._get_fill_color() 218 fill_bg_color = self._get_fill_bg_color() 219 self.fill_bg = PolygonFill(name='FillBg', parent=self, polygon=self, color=fill_bg_color, state=uiconst.UI_DISABLED) 220 self.fill = PolygonFill(name='Fill', parent=self, polygon=self, color=fill_color, blendMode=trinity.TR2_SBM_ADD) 221 self.fill.on_left_clicked.connect(self.on_left_clicked) 222 if IS_RIGHT_CLICK_DELETE_ENABLED: 223 self.fill.on_right_clicked.connect(self.on_right_clicked) 224 self.fill.on_drag_started.connect(self.on_drag_started) 225 self.fill.on_drag_ended.connect(self.on_drag_ended) 226 self.fill.on_hover_started.connect(self.on_hover) 227 self.fill.on_hover_ended.connect(self.on_dehover) 228 229 def _get_invalid_fill_color(self): 230 if self.is_selected: 231 return self.FILL_COLOR_SELECTED_INVALID 232 if self.is_hovered: 233 return self.FILL_COLOR_HOVERED_INVALID 234 return self.FILL_COLOR_DESELECTED_INVALID 235 236 def _get_fill_bg_color(self): 237 if not self.show_fill or not self.is_valid: 238 return self.NO_COLOR 239 if self.is_selected: 240 return self.fill_bg_color_selected 241 if self.is_hovered: 242 return self.fill_bg_color_hovered 243 return self.fill_bg_color_deselected 244 245 def _get_fill_color(self): 246 if not self.show_fill: 247 return self.NO_COLOR 248 if not self.is_valid: 249 return self._get_invalid_fill_color() 250 if self.is_selected: 251 return self.fill_color_selected 252 if self.is_hovered: 253 return self.fill_color_hovered 254 return self.fill_color_deselected 255 256 def _get_perimeter_color(self): 257 if self.is_valid: 258 return self.perimeter_color 259 else: 260 return self.PERIMETER_COLOR_INVALID 261 262 def _redraw_polygon(self): 263 self.perimeter.Flush() 264 self.fill.Flush() 265 self.fill_bg.Flush() 266 self.perimeter_inner.Flush() 267 fill_color = self._get_fill_color() 268 self.fill.SetRGBA(*fill_color) 269 fill_bg_color = self._get_fill_bg_color() 270 self.fill_bg.SetRGBA(*fill_bg_color) 271 perimeter_color = self._get_perimeter_color() 272 self.perimeter_inner.SetRGBA(*perimeter_color) 273 for vertex in self.vertices: 274 self._add_segment(vertex.x, vertex.y) 275 276 if self.is_complete: 277 self._complete_polygon() 278 elif self.vertices: 279 self._set_active_vertex(self.vertices[-1]) 280 281 def _redraw_vertices(self): 282 if not self.vertices: 283 return 284 if self.is_complete: 285 for vertex in self.vertices: 286 vertex.set_base_texture(VERTEX_CLOSED_TEXTURE_PATH) 287 288 else: 289 for vertex in self.vertices[:-1]: 290 vertex.set_base_texture(VERTEX_CLOSED_TEXTURE_PATH) 291 292 last_vertex = self.vertices[-1] 293 last_vertex.set_base_texture(VERTEX_OPEN_TEXTURE_PATH) 294 295 def _add_vertex(self, x, y): 296 vertex_order = len(self.vertices) 297 vertex = PolygonVertex(name='Vertex%d' % vertex_order, parent=self, get_mouse_position=self.drawing_area.get_mouse_position, polygon=self, order=vertex_order, x=x, y=y, texturePath=VERTEX_OPEN_TEXTURE_PATH, draggingTexturePath=VERTEX_DRAGGING_TEXTURE_PATH, opacity=1.0 if self.show_vertices else 0.0) 298 vertex.on_left_clicked.connect(self.on_vertex_left_clicked) 299 if IS_RIGHT_CLICK_DELETE_ENABLED: 300 vertex.on_right_clicked.connect(self.on_vertex_right_clicked) 301 vertex.on_drag_started.connect(self.on_vertex_drag_started) 302 vertex.on_drag_ended.connect(self.on_vertex_drag_ended) 303 self.vertices.append(vertex) 304 self._set_active_vertex(vertex) 305 self._redraw_vertices() 306 307 def _remove_vertex(self, index): 308 vertex_to_remove = self.vertices[index] 309 was_active_vertex = self.active_vertex == vertex_to_remove 310 vertex_to_remove.Close() 311 del self.vertices[index] 312 for order, vertex in enumerate(self.vertices): 313 vertex.order = order 314 315 if was_active_vertex: 316 if len(self.vertices): 317 self._set_active_vertex(self.vertices[-1]) 318 else: 319 self._set_active_vertex(None) 320 self._redraw_vertices() 321 322 def _add_segment(self, x, y): 323 self.perimeter.add_vertex(x, y) 324 self.fill.add_vertex(x, y) 325 self.fill_bg.add_vertex(x, y) 326 self.perimeter_inner.add_vertex(x, y) 327 328 def _remove_segment(self, index): 329 self.perimeter.remove_vertex(index) 330 self.fill.remove_vertex(index) 331 self.fill_bg.remove_vertex(index) 332 self.perimeter_inner.remove_vertex(index) 333 334 def autocomplete_polygon(self): 335 self._complete_polygon() 336 self._set_active_vertex(None) 337 self._remove_wip_segment() 338 self.on_completed(self) 339 340 def _complete_polygon(self): 341 for vertex in self.vertices: 342 vertex.set_base_cursor(cursors.CURSOR_RESIZER_COLLAPSED) 343 vertex.set_dragging_cursor(cursors.CURSOR_RESIZER_EXPANDED) 344 vertex.reset_cursor() 345 346 self._redraw_vertices() 347 self.perimeter.isLoop = True 348 self.perimeter_inner.isLoop = True 349 self.fill.complete() 350 self.fill_bg.complete() 351 self.is_complete = True 352 353 def _set_active_vertex(self, vertex): 354 if self.active_vertex == vertex: 355 return 356 self.active_vertex = vertex 357 self._remove_wip_segment() 358 if self.active_vertex: 359 self._add_wip_segment() 360 361 def _move(self, start_x, start_y, end_x, end_y): 362 x_diff = end_x - start_x 363 y_diff = end_y - start_y 364 for vertex in self.vertices: 365 vertex.update_position(x=vertex.x + x_diff, y=vertex.y + y_diff) 366 367 self._redraw_polygon() 368 369 def _delete(self): 370 self._start_edit() 371 self.Close() 372 373 def _start_edit(self): 374 self.on_edit_started(self) 375 self._update_last_edit_time() 376 377 def _update_last_edit_time(self): 378 self.time_of_last_edit = GetWallclockTime() 379 380 def on_left_clicked(self, *args): 381 self._start_edit() 382 383 def on_right_clicked(self, *args): 384 self._delete() 385 386 def on_drag_started(self, *args): 387 self._start_edit() 388 self._remove_wip_segment() 389 self._start_drag_updates() 390 391 def on_drag_ended(self, *args): 392 self._end_drag_updates() 393 self._add_wip_segment() 394 395 def on_vertex_left_clicked(self, vertex): 396 self._start_edit() 397 if len(self.vertices) > 2 and vertex.order == 0: 398 self.autocomplete_polygon() 399 400 def on_vertex_right_clicked(self, vertex): 401 self._start_edit() 402 is_polygon_delete = len(self.vertices) <= 1 403 self.remove_point(vertex.order) 404 if is_polygon_delete: 405 self.Close() 406 407 def on_vertex_drag_started(self, vertex): 408 self._start_edit() 409 self._remove_wip_segment() 410 self._start_vertex_drag_updates(vertex) 411 412 def on_vertex_drag_ended(self, *args): 413 self._end_vertex_drag_updates() 414 self._add_wip_segment() 415 416 def _start_drag_updates(self): 417 self._end_drag_updates() 418 self.is_moving = True 419 self.drag_mouse_position = self.drawing_area.get_mouse_position() 420 self.drag_updates_thread = AutoTimer(DRAG_UPDATE_TIMER_MSEC, self._update_while_dragging) 421 self._remove_x_button() 422 423 def _end_drag_updates(self): 424 if self.drag_updates_thread: 425 self.drag_updates_thread.KillTimer() 426 self.is_moving = False 427 self.drag_mouse_position = None 428 self.drag_updates_thread = None 429 self._update_last_edit_time() 430 self._add_x_button() 431 432 def _update_drag_mouse_position(self): 433 start_x, start_y = self.drag_mouse_position 434 end_x, end_y = self.drawing_area.get_mouse_position() 435 x_diff = end_x - start_x 436 y_diff = end_y - start_y 437 for vertex in self.vertices: 438 vertex_end_x = vertex.x + x_diff 439 vertex_end_y = vertex.y + y_diff 440 if not self.drawing_area.is_position_in_area(vertex_end_x, vertex_end_y): 441 return False 442 443 self.drag_mouse_position = (end_x, end_y) 444 return True 445 446 def _update_while_dragging(self): 447 start_x, start_y = self.drag_mouse_position 448 if not self._update_drag_mouse_position(): 449 return 450 end_x, end_y = self.drag_mouse_position 451 self._move(start_x, start_y, end_x, end_y) 452 453 def _start_vertex_drag_updates(self, vertex): 454 self._end_vertex_drag_updates() 455 self.is_vertex_moving = True 456 self.original_vertex_mouse_position = self.drawing_area.get_mouse_position() 457 self.vertex_drag_updates_thread = AutoTimer(DRAG_UPDATE_TIMER_MSEC, self._update_vertex_while_dragging, vertex) 458 self._remove_x_button() 459 460 def _end_vertex_drag_updates(self): 461 if self.vertex_drag_updates_thread: 462 self.vertex_drag_updates_thread.KillTimer() 463 self.is_vertex_moving = False 464 self.original_vertex_mouse_position = None 465 self.vertex_drag_updates_thread = None 466 self._update_last_edit_time() 467 self._add_x_button() 468 469 def _update_vertex_while_dragging(self, vertex): 470 x, y = self.drawing_area.get_mouse_position() 471 vertex.update_position(x, y) 472 should_abort_move = self.drawing_area.is_there_another_vertex_in_range(self, vertex) 473 if should_abort_move: 474 x, y = self.original_vertex_mouse_position 475 vertex.update_position(x, y) 476 self.perimeter.update_vertex(vertex.order, x, y) 477 self.fill.update_vertex(vertex.order, x, y) 478 self.fill_bg.update_vertex(vertex.order, x, y) 479 self.perimeter_inner.update_vertex(vertex.order, x, y) 480 if should_abort_move: 481 self._end_vertex_drag_updates() 482 483 def move_cursor(self): 484 self._update_wip_segment() 485 486 def _update_wip_segment(self): 487 if self.wip_segment: 488 x, y = self.drawing_area.get_mouse_position() 489 self.wip_segment.update_vertex(1, x, y) 490 491 def _add_wip_segment(self): 492 if self.is_complete: 493 return 494 self._remove_wip_segment() 495 self.wip_segment = PolygonSegment(name='wip_segment', parent=self.drawing_area, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, color=self.perimeter_color, lineWidth=self.perimeter_width) 496 self.wip_segment.on_removed.connect(self.on_wip_segment_removed) 497 self.on_wip_segment_added() 498 last_vertex = self.vertices[-1] 499 self.wip_segment.add_vertex(last_vertex.x, last_vertex.y) 500 x, y = self.drawing_area.get_mouse_position() 501 self.wip_segment.add_vertex(x, y) 502 503 def _remove_wip_segment(self): 504 if self.wip_segment: 505 self.wip_segment.Close() 506 self.wip_segment = None 507 508 def _add_x_button(self): 509 if not self.drawing_area.IsClickable() or not self.is_selected or not self.is_complete or self.is_moving or self.is_vertex_moving: 510 return 511 x, y = self._get_top_right() 512 self.drawing_area.add_x_button(self._delete, x, y) 513 514 def _remove_x_button(self): 515 self.drawing_area.remove_x_button()