100 lines
3.0 KiB
GDScript
100 lines
3.0 KiB
GDScript
@tool
|
|
extends Resource
|
|
class_name SS2D_NormalRange
|
|
|
|
## This class will determine if the normal of a vector falls within the specifed angle ranges.
|
|
##
|
|
## - if begin and end are equal, any angle is considered to be within range [br]
|
|
## - 360.0 and 0.0 degrees are considered equivilent [br]
|
|
|
|
@export_range (0, 360, 1) var begin: float = 0.0 : set = set_begin
|
|
@export_range (0, 360, 1) var distance: float = 0.0 : set = set_distance
|
|
|
|
# This is a hack to support the custom editor, needed a property
|
|
# to exist to lock the TextureProgressBar to. Makes it flow better
|
|
# in the Inspector.
|
|
@export var edgeRendering: Vector2
|
|
|
|
|
|
func set_distance(f: float) -> void:
|
|
distance = f
|
|
emit_changed()
|
|
|
|
|
|
func set_begin(f: float) -> void:
|
|
begin = f
|
|
emit_changed()
|
|
|
|
|
|
func _to_string() -> String:
|
|
return "NormalRange: %s - %s" % [begin, begin + distance]
|
|
|
|
|
|
static func get_angle_from_vector(vec: Vector2) -> float:
|
|
var normal: Vector2 = vec.normalized()
|
|
# With respect to the X-axis
|
|
# This is how Vector2.angle() is calculated, best to keep it consistent
|
|
var comparison_vector := Vector2(1, 0)
|
|
|
|
var ab: Vector2 = normal
|
|
var bc: Vector2 = comparison_vector
|
|
var dot_prod: float = ab.dot(bc)
|
|
var determinant: float = (ab.x * bc.y) - (ab.y * bc.x)
|
|
var angle: float = atan2(determinant, dot_prod)
|
|
|
|
# This angle has a range of 360 degrees
|
|
# Is between 180 and - 180
|
|
var deg: float = rad_to_deg(angle)
|
|
|
|
# Get range between 0.0 and 360.0
|
|
if deg < 0:
|
|
deg = 360.0 + deg
|
|
return deg
|
|
|
|
|
|
# Get in range between 0.0 and 360.0.
|
|
static func _get_positive_angle_deg(degrees: float) -> float:
|
|
while degrees < 0:
|
|
degrees += 360
|
|
return fmod(degrees, 360.0)
|
|
|
|
# Get in range between -360.0 and 360.0
|
|
static func _get_signed_angle_deg(degrees: float) -> float:
|
|
var new_degrees: float = degrees
|
|
while absf(new_degrees) > 360.0:
|
|
new_degrees += (360.0 * signf(degrees) * -1.0)
|
|
return new_degrees
|
|
|
|
|
|
# Saving a scene with this resource requires a parameter-less init method
|
|
func _init(_begin: float = 0.0, _distance: float = 0.0) -> void:
|
|
_begin = SS2D_NormalRange._get_signed_angle_deg(_begin)
|
|
_distance = SS2D_NormalRange._get_signed_angle_deg(_distance)
|
|
|
|
begin = _begin
|
|
distance = _distance
|
|
|
|
|
|
func is_in_range(vec: Vector2) -> bool:
|
|
# A Distance of 0 or 360 is the entire circle
|
|
if distance == 0 or SS2D_NormalRange._get_positive_angle_deg(distance) == 360.0:
|
|
return true
|
|
|
|
var begin_positive: float = SS2D_NormalRange._get_positive_angle_deg(begin)
|
|
var end_positive: float = SS2D_NormalRange._get_positive_angle_deg(begin + distance)
|
|
# If positive, counter clockwise direction
|
|
# If negative, clockwise direction
|
|
var direction: float = signf(distance)
|
|
var angle: float = SS2D_NormalRange.get_angle_from_vector(vec)
|
|
|
|
# Swap begin and end if direction is negative
|
|
if direction == -1:
|
|
var t: float = begin_positive
|
|
begin_positive = end_positive
|
|
end_positive = t
|
|
|
|
if begin_positive < end_positive:
|
|
return ((angle >= begin_positive) and (angle <= end_positive))
|
|
else:
|
|
return ((angle >= begin_positive) or (angle <= end_positive))
|