extends SelectionTool var _rect := Rect2(0, 0, 0, 0) var _square := false # Mouse Click + Shift var _expand_from_center := false # Mouse Click + Ctrl var _displace_origin = false # Mouse Click + Alt func _input(event: InputEvent) -> void: if !_move and !_rect.has_no_area(): if event.is_action_pressed("shape_perfect"): _square = true elif event.is_action_released("shape_perfect"): _square = false if event.is_action_pressed("shape_center"): _expand_from_center = true elif event.is_action_released("shape_center"): _expand_from_center = false if event.is_action_pressed("shape_displace"): _displace_origin = true elif event.is_action_released("shape_displace"): _displace_origin = false func draw_move(position: Vector2) -> void: if selection_node.arrow_key_move: return position = snap_position(position) .draw_move(position) if !_move: if _displace_origin: _start_pos += position - _offset _rect = _get_result_rect(_start_pos, position) _set_cursor_text(_rect) _offset = position func draw_end(position: Vector2) -> void: if selection_node.arrow_key_move: return position = snap_position(position) .draw_end(position) _rect = Rect2(0, 0, 0, 0) _square = false _expand_from_center = false _displace_origin = false func draw_preview() -> void: if !_move && !_rect.has_no_area(): var canvas: Node2D = Global.canvas.previews var position := canvas.position var scale := canvas.scale var temp_rect = _rect if Global.mirror_view: position.x = position.x + Global.current_project.size.x temp_rect.position.x = Global.current_project.size.x - temp_rect.position.x scale.x = -1 var border := _get_shape_points_filled(temp_rect.size) var indicator := _fill_bitmap_with_points(border, temp_rect.size) canvas.draw_set_transform(temp_rect.position, canvas.rotation, scale) for line in _create_polylines(indicator): canvas.draw_polyline(PoolVector2Array(line), Color.black) canvas.draw_set_transform(canvas.position, canvas.rotation, canvas.scale) func apply_selection(_position: Vector2) -> void: .apply_selection(_position) var project: Project = Global.current_project if !_add and !_subtract and !_intersect: Global.canvas.selection.clear_selection() if _rect.size == Vector2.ZERO and Global.current_project.has_selection: Global.canvas.selection.commit_undo("Select", undo_data) if _rect.size != Vector2.ZERO: var selection_map_copy := SelectionMap.new() selection_map_copy.copy_from(project.selection_map) set_ellipse(selection_map_copy, _rect.position) # Handle mirroring if Tools.horizontal_mirror: var mirror_x_rect := _rect mirror_x_rect.position.x = ( Global.current_project.x_symmetry_point - _rect.position.x + 1 ) mirror_x_rect.end.x = Global.current_project.x_symmetry_point - _rect.end.x + 1 set_ellipse(selection_map_copy, mirror_x_rect.abs().position) if Tools.vertical_mirror: var mirror_xy_rect := mirror_x_rect mirror_xy_rect.position.y = ( Global.current_project.y_symmetry_point - _rect.position.y + 1 ) mirror_xy_rect.end.y = Global.current_project.y_symmetry_point - _rect.end.y + 1 set_ellipse(selection_map_copy, mirror_xy_rect.abs().position) if Tools.vertical_mirror: var mirror_y_rect := _rect mirror_y_rect.position.y = ( Global.current_project.y_symmetry_point - _rect.position.y + 1 ) mirror_y_rect.end.y = Global.current_project.y_symmetry_point - _rect.end.y + 1 set_ellipse(selection_map_copy, mirror_y_rect.abs().position) project.selection_map = selection_map_copy Global.canvas.selection.big_bounding_rectangle = project.selection_map.get_used_rect() Global.canvas.selection.commit_undo("Select", undo_data) func set_ellipse(selection_map: SelectionMap, position: Vector2) -> void: var project: Project = Global.current_project var bitmap_size: Vector2 = selection_map.get_size() if _intersect: selection_map.clear() var points := _get_shape_points_filled(_rect.size) for p in points: var pos: Vector2 = position + p if pos.x < 0 or pos.y < 0 or pos.x >= bitmap_size.x or pos.y >= bitmap_size.y: continue if _intersect: if project.selection_map.is_pixel_selected(pos): selection_map.select_pixel(pos, true) else: selection_map.select_pixel(pos, !_subtract) # Given an origin point and destination point, returns a rect representing # where the shape will be drawn and what is its size func _get_result_rect(origin: Vector2, dest: Vector2) -> Rect2: var rect := Rect2(Vector2.ZERO, Vector2.ZERO) # Center the rect on the mouse if _expand_from_center: var new_size := (dest - origin).floor() # Make rect 1:1 while centering it on the mouse if _square: var square_size := max(abs(new_size.x), abs(new_size.y)) new_size = Vector2(square_size, square_size) origin -= new_size dest = origin + 2 * new_size # Make rect 1:1 while not trying to center it if _square: var square_size := min(abs(origin.x - dest.x), abs(origin.y - dest.y)) rect.position.x = origin.x if origin.x < dest.x else origin.x - square_size rect.position.y = origin.y if origin.y < dest.y else origin.y - square_size rect.size = Vector2(square_size, square_size) # Get the rect without any modifications else: rect.position = Vector2(min(origin.x, dest.x), min(origin.y, dest.y)) rect.size = (origin - dest).abs() rect.size += Vector2.ONE return rect func _get_shape_points_filled(size: Vector2) -> PoolVector2Array: var border := _get_ellipse_points(Vector2.ZERO, size) var filling := [] var bitmap := _fill_bitmap_with_points(border, size) for x in range(1, ceil(size.x / 2)): var fill := false var prev_is_true := false for y in range(0, ceil(size.y / 2)): var top_l_p := Vector2(x, y) var bit := bitmap.get_bit(top_l_p) if bit and not fill: prev_is_true = true continue if not bit and (fill or prev_is_true): filling.append(top_l_p) filling.append(Vector2(x, size.y - y - 1)) filling.append(Vector2(size.x - x - 1, y)) filling.append(Vector2(size.x - x - 1, size.y - y - 1)) if prev_is_true: fill = true prev_is_true = false elif bit and fill: break return PoolVector2Array(border + filling) # Algorithm based on http://members.chello.at/easyfilter/bresenham.html func _get_ellipse_points(pos: Vector2, size: Vector2) -> Array: var array := [] var x0 := int(pos.x) var x1 := pos.x + int(size.x - 1) var y0 := int(pos.y) var y1 := int(pos.y) + int(size.y - 1) var a := int(abs(x1 - x0)) var b := int(abs(y1 - x0)) var b1 := b & 1 var dx := 4 * (1 - a) * b * b var dy := 4 * (b1 + 1) * a * a var err := dx + dy + b1 * a * a var e2 := 0 if x0 > x1: x0 = x1 x1 += a if y0 > y1: y0 = y1 # warning-ignore:integer_division y0 += (b + 1) / 2 y1 = y0 - b1 a *= 8 * a b1 = 8 * b * b while x0 <= x1: var v1 := Vector2(x1, y0) var v2 := Vector2(x0, y0) var v3 := Vector2(x0, y1) var v4 := Vector2(x1, y1) array.append(v1) array.append(v2) array.append(v3) array.append(v4) e2 = 2 * err if e2 <= dy: y0 += 1 y1 -= 1 dy += a err += dy if e2 >= dx || 2 * err > dy: x0 += 1 x1 -= 1 dx += b1 err += dx while y0 - y1 < b: var v1 := Vector2(x0 - 1, y0) var v2 := Vector2(x1 + 1, y0) var v3 := Vector2(x0 - 1, y1) var v4 := Vector2(x1 + 1, y1) array.append(v1) array.append(v2) array.append(v3) array.append(v4) y0 += 1 y1 -= 1 return array