97 lines
2.5 KiB
GDScript
97 lines
2.5 KiB
GDScript
@tool
|
||
extends TextureRect
|
||
|
||
signal shaven(progress: float)
|
||
|
||
@export var area_size := Vector2(40, 20):
|
||
set(val):
|
||
area_size = val
|
||
custom_minimum_size = val
|
||
if is_node_ready():
|
||
_init_bit_mask()
|
||
@export var debug_reload := false:
|
||
set(val):
|
||
debug_reload = false
|
||
if is_node_ready():
|
||
_init_bit_mask()
|
||
|
||
# points inside radius 5 circle
|
||
var brush_points := generate_brush_points(5)
|
||
var bit_mask: BitMap
|
||
|
||
func _ready() -> void:
|
||
# init bit mask
|
||
_init_bit_mask()
|
||
|
||
|
||
# a solid circle brush, center = (0, 0)
|
||
func generate_brush_points(radius: int) -> PackedVector2Array:
|
||
var points := PackedVector2Array()
|
||
# Check each pixel in square area
|
||
for x in range(-radius, radius + 1):
|
||
for y in range(-radius, radius + 1):
|
||
# If point is within circle radius
|
||
if x * x + y * y <= radius * radius:
|
||
points.append(Vector2(x, y))
|
||
return points
|
||
|
||
|
||
func _init_bit_mask() -> void:
|
||
if not bit_mask:
|
||
bit_mask = BitMap.new()
|
||
bit_mask.create(area_size)
|
||
texture = ImageTexture.create_from_image(bit_mask.convert_to_image())
|
||
|
||
|
||
func _shave(mouse_pos: Vector2) -> void:
|
||
if not mouse_pressing:
|
||
return
|
||
var updated = false
|
||
for point in brush_points:
|
||
var pos = mouse_pos + point
|
||
if pos.x < 0 or pos.x >= area_size.x or pos.y < 0 or pos.y >= area_size.y:
|
||
continue
|
||
bit_mask.set_bitv(pos, true)
|
||
updated = true
|
||
texture.update(bit_mask.convert_to_image())
|
||
if updated:
|
||
_send_signal()
|
||
|
||
|
||
func _send_signal() -> void:
|
||
var total = area_size.x * area_size.y
|
||
var true_bit_count = bit_mask.get_true_bit_count()
|
||
var progress = float(true_bit_count) / float(total)
|
||
shaven.emit(progress)
|
||
|
||
|
||
var mouse_pressing = false
|
||
|
||
|
||
# 可以刮出区域
|
||
func _gui_input(event: InputEvent) -> void:
|
||
# 通过鼠标点击拖动,刮开区域,set_bit 一个 brush_size 的圆形区域为 true
|
||
# 每次更新后,bit_mask 会更新到 texture 上
|
||
# 通过 shaven 信号,通知外部,刮开了多少区域
|
||
if event is InputEventMouseButton:
|
||
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||
# 先设置 mouse_pressing 为 true
|
||
mouse_pressing = true
|
||
# 刮开区域
|
||
_shave(event.position)
|
||
accept_event()
|
||
elif event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
|
||
mouse_pressing = false
|
||
accept_event()
|
||
elif event is InputEventMouseMotion and mouse_pressing:
|
||
_shave(event.position)
|
||
accept_event()
|
||
# elif event is InputEventScreenTouch:
|
||
# if event.pressed:
|
||
# mouse_pressing = true
|
||
# _shave(event.position)
|
||
# accept_event()
|
||
# elif not event.pressed:
|
||
# mouse_pressing = false
|
||
# accept_event()
|