| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- extends Sprite2D
- signal value_change(index: int, position: Vector2, rotation: float, scale: Vector2, skew: float)
- @onready var target: Sprite2D
- @export var zoom = 1.0
- var target_index = -1
- var is_dragging = false
- var cursor_sprite_frames: SpriteFrames
- var local_direction = 0
- var cursor_direction = 0
- var action = ""
- var transform_start_local_direction = 0
- var transform_start_cursor_direction = 0
- var transform_start_global_position: Vector2
- var transform_start_position: Vector2
- var transform_start_rotation: float
- var transform_start_scale: Vector2
- var _corners = [
- Vector2(1.0, 0.5), Vector2(1.0, 0.0), Vector2(0.5, 0.0), Vector2(0.0, 0.0),
- Vector2(0.0, 0.5), Vector2(0.0, 1.0), Vector2(0.5, 1.0), Vector2(1.0, 1.0)
- ]
- var editable = true
- func _ready() -> void:
- set_target(null, -1)
- cursor_sprite_frames = load("res://Resources/cursor_sprite_frames.tres")
- func set_target(_sprite: Sprite2D, _index: int, _editable: bool = false):
- target = _sprite
- target_index = _index
- editable = _editable
- if target == null:
- visible = false
- return
-
- visible = true
- var image = Image.create(target.texture.get_width() + 512, target.texture.get_height() + 512, false, Image.FORMAT_RGBA8)
- texture = ImageTexture.create_from_image(image)
-
- position = target.position
- rotation = target.rotation
- scale = target.scale
- skew = target.skew
-
- func grow_rect(rect: Rect2, amount: float, _scale: Vector2) -> Rect2:
- var amount_x = clampf(amount / abs(_scale.x), 1.0, 9999.9) if _scale.x != 0.0 else 1.0
- var amount_y = clampf(amount / abs(_scale.y), 1.0, 9999.9) if _scale.y != 0.0 else 1.0
- return rect.grow_individual(amount_x, amount_y, amount_x, amount_y)
- func _process(_delta: float) -> void:
- if target == null:
- return
- update_shader_params()
-
- func _input(event):
- if target == null or editable == false:
- return
-
- if event is InputEventMouseButton:
- if event.button_index == MOUSE_BUTTON_LEFT:
- if event.pressed and action != "":
- transform_start_global_position = get_global_mouse_position()
- transform_start_local_direction = local_direction
- transform_start_cursor_direction = cursor_direction
- transform_start_position = target.position
- transform_start_rotation = target.rotation
- transform_start_scale = target.scale
- is_dragging = true
- get_viewport().set_input_as_handled()
- else:
- is_dragging = false
-
- elif event is InputEventKey:
- if event.pressed and not event.echo:
- if Input.is_key_pressed(KEY_ENTER) or Input.is_key_pressed(KEY_KP_ENTER):
- is_dragging = false
- set_target(null, -1)
- Input.set_custom_mouse_cursor(null)
- return
-
- var _offset = Vector2.ZERO;
- if Input.is_key_pressed(KEY_LEFT): _offset.x = -1
- if Input.is_key_pressed(KEY_RIGHT): _offset.x = 1
- if Input.is_key_pressed(KEY_UP): _offset.y = -1
- if Input.is_key_pressed(KEY_DOWN): _offset.y = 1
- if _offset == Vector2.ZERO: return
- if event.ctrl_pressed:
- _offset *= 10;
-
- target.position += _offset;
- position = target.position
- value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
- get_viewport().set_input_as_handled()
-
- elif event is InputEventMouseMotion and is_dragging:
- if action == "Move":
- var _offset = get_global_mouse_position() - transform_start_global_position;
-
- if event.ctrl_pressed:
- if abs(_offset.x) < abs(_offset.y):
- _offset.x = 0
- else:
- _offset.y = 0
-
- target.position = transform_start_position + _offset;
- position = target.position
- value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
- get_viewport().set_input_as_handled()
-
- elif action == "Rotation":
- var vector_OA = (transform_start_global_position - transform_start_position).normalized()
- var vector_OB = (get_global_mouse_position() - transform_start_position).normalized()
- var angle_rad = -vector_OA.angle_to(vector_OB)
- if angle_rad < 0: angle_rad += 2 * PI
-
- if event.ctrl_pressed:
- angle_rad = deg_to_rad(round(rad_to_deg(angle_rad) / 15.0) * 15.0)
-
- target.rotation = transform_start_rotation - angle_rad
- rotation = target.rotation
- value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
- get_viewport().set_input_as_handled()
-
- elif action == "Scale":
- # 将起始点和当前点转换到Sprite的局部坐标系
- var start_local = target.to_local(transform_start_global_position)
- var current_local = target.to_local(get_global_mouse_position())
- # 在局部坐标系中计算缩放
- var scale_delta = Vector2.ONE
- var start_vec = start_local - target.offset # 从中心点到起始点的向量
- var current_vec = current_local - target.offset # 从中心点到当前点的向量
- # 避免除零错误
- if start_vec.x != 0: scale_delta.x = current_vec.x / start_vec.x
- if start_vec.y != 0: scale_delta.y = current_vec.y / start_vec.y
- # 根据拖拽方向锁定轴
- if transform_start_local_direction == 0 or transform_start_local_direction == 4: # 左右
- scale_delta.y = 1.0
- elif transform_start_local_direction == 2 or transform_start_local_direction == 6: # 上下
- scale_delta.x = 1.0
- # 等比缩放
- if event.ctrl_pressed:
- var uniform_scale = (scale_delta.x + scale_delta.y) / 2.0
- scale_delta = Vector2(uniform_scale, uniform_scale)
- # 应用缩放
- target.scale = transform_start_scale * scale_delta
- scale = target.scale
- value_change.emit(target_index, target.position, target.rotation, target.scale, target.skew)
- get_viewport().set_input_as_handled()
- #func _draw() -> void:
- #var _scale = target.scale * zoom
- #var r0 = target.get_rect()
- #var r1 = grow_rect(r0, -15, _scale) # move
- #var r2 = grow_rect(r0, 30, _scale) # scale
- #var r3 = grow_rect(r0, 80, _scale) # rotation
- #draw_rect(r0, Color.WHITE, false)
- #draw_rect(r1, Color.WHITE, false)
- #draw_rect(r2, Color.WHITE, false)
- #draw_rect(r3, Color.WHITE, false)
- #
- #var texture_size = target.texture.get_size()
- #var s = Vector2(1.0, texture_size.y / texture_size.x) if texture_size.x < texture_size.y else Vector2(texture_size.x / texture_size.y, 1.0)
- #var t = Transform2D(0.0, s, 0.0, Vector2.ZERO)
- #var scale_local_mouse_pos = t.affine_inverse() * get_local_mouse_position()
- #
- #var PI16 = PI / 16;
- #for i in range(32):
- #var p = (Vector2.RIGHT * 3000.0).rotated(i * PI16)
- #var point = t * p
- #draw_line(Vector2.ZERO, point, Color.WHITE, 2.0)
-
- func update_free_transform(update_cursor: bool=true):
- if update_cursor == false or editable == false or target == null:
- action = ""
- local_direction = -1
- Input.set_custom_mouse_cursor(null)
- return
-
- var local_mouse_position = get_local_mouse_position()
- var PI8 = PI / 8;
- var texture_size = target.texture.get_size()
- var scale_factor = Vector2(1.0, texture_size.y / texture_size.x) if texture_size.x < texture_size.y else Vector2(texture_size.x / texture_size.y, 1.0)
- var scale_local_mouse_pos = local_mouse_position / scale_factor
- var angle = fposmod(PI / 16 - scale_local_mouse_pos.angle(), TAU)
-
- local_direction = -1
- for i in range(9):
- if (i * 2) * PI8 <= angle and angle < (1 + i * 2) * PI8:
- local_direction = i % 8
- break
- #print(rad_to_deg(angle), " ", local_direction, " ")
-
- var target_rect = target.get_rect()
- if local_direction != -1:
- var rect = Rect2(-target_rect.size / 2, target_rect.size)
- var trans = target.transform
- trans.origin = Vector2.ZERO
- cursor_direction = find_point_mapping(rect, trans, local_direction)
- if is_dragging == false:
- action = ""
- var _scale = target.scale * zoom
- var local_mouse_pos = local_mouse_position
-
- if grow_rect(target_rect, -15, _scale).has_point(local_mouse_pos):
- action = "Move"
- elif local_direction != -1:
- if grow_rect(target_rect, 30, _scale).has_point(local_mouse_pos):
- action = "Scale"
- elif grow_rect(target_rect, 80, _scale).has_point(local_mouse_pos):
- action = "Rotation"
- if action == "":
- Input.set_custom_mouse_cursor(null)
- elif action == "Move":
- var tex = cursor_sprite_frames.get_frame_texture(action, 0)
- Input.set_custom_mouse_cursor(tex, Input.CursorShape.CURSOR_ARROW, Vector2(tex.get_width() / 2.0, tex.get_height() / 2.0))
- elif action == "Rotation":
- var curson_name = "%s_%d" % [action, cursor_direction]
- var tex = cursor_sprite_frames.get_frame_texture(curson_name, 0)
- Input.set_custom_mouse_cursor(tex, Input.CursorShape.CURSOR_ARROW, Vector2(tex.get_width() / 2.0, tex.get_height() / 2.0))
- elif action == "Scale":
- if is_dragging and transform_start_local_direction in [0, 2, 4, 6]:
- return
-
- var curson_name = "%s_%d" % [action, cursor_direction]
- var tex = cursor_sprite_frames.get_frame_texture(curson_name, 0)
- Input.set_custom_mouse_cursor(tex, Input.CursorShape.CURSOR_ARROW, Vector2(tex.get_width() / 2.0, tex.get_height() / 2.0))
- else:
- Input.set_custom_mouse_cursor(null)
- action = ""
-
- update_shader_params()
-
- func update_shader_params():
- var rect = target.get_rect()
- var points_array = [
- rect.position, # 左上角
- rect.position + Vector2(rect.size.x, 0), # 右上角
- rect.position + rect.size, # 右下角
- rect.position + Vector2(0, rect.size.y) # 左下角
- ]
- for i in range(4):
- points_array[i] = target.transform * points_array[i]
-
- material.set_shader_parameter("points", points_array)
- material.set_shader_parameter("editable", editable)
-
- func find_point_mapping(rect: Rect2, trans: Transform2D, direction: int) -> int:
- var points = []
- for corner in _corners:
- points.append(rect.position + rect.size * corner)
-
- var min_index = -1
- var min_angle = TAU
- for i in points.size():
- var angle = abs((trans * points[i]).angle())
- if angle < min_angle:
- min_angle = angle
- min_index = i
- if(trans.get_scale().y < 0):
- return (min_index - direction + 8) % 8
- else:
- return (direction - min_index + 8) % 8
-
|