extends Node
## GATE — set_curve_point RE-DERIVES HANDLES (#129 deferred bug; docs §14.2 note).
## Writing only `p` left a point's stored handles where they were, so an auto / vector
## point moved that way bent its spans. The handles of auto / vector points (the moved one
## and its neighbours) are re-derived in the same gesture; aligned / free keep theirs.
## P1 auto: after moving point 1, every point's handles = auto_handles of the new points
## P2 ... in ONE gesture
## P3 a FREE point keeps its stored handles
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_curve_point_handles.tscn
var fails := 0
var checks := 0
var map: Node3D
func _ok(what: String, cond: bool, note: Variant = "") -> void:
checks += 1
if not cond:
fails += 1
print(" %-64s %s %s" % [what, "OK" if cond else "**FAIL**", str(note)])
func _ready() -> void:
var gs: Node = get_tree().root.get_node_or_null("/root/GameState")
gs.route("world:level:*", 1, "peer")
var keet: Node = get_tree().root.get_node_or_null("/root/KeetService")
if keet != null:
keet.current_member_id = "gate-author"
get_tree().root.get_node("/root/GameMode").set_mode("build")
map = (load("res://scenes/maps/map_dev_test.tscn") as PackedScene).instantiate()
add_child(map)
for i in 6:
await get_tree().process_frame
_run()
get_tree().root.get_node("/root/GameMode").set_mode("play")
if checks < 3:
print(" **FAIL** only %d checks ran -- setup died before the assertions" % checks)
fails += 1
print("\nGATE curve_point_handles: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
get_tree().quit(0 if fails == 0 else 1)
func _same(a: Array, b: Array) -> bool:
for i in a.size():
for k: String in ["h_in", "h_out"]:
if not ((a[i] as Dictionary)[k] as Vector3).is_equal_approx((b[i] as Dictionary)[k]):
return false
return true
func _run() -> void:
var persist: Node = map.get("_persist")
var O := Vector3(90.13, 1.0, 90.29)
var c: String = map.create_curve([CurveMath.point(O), CurveMath.point(O + Vector3(3.1, 0, 0)),
CurveMath.point(O + Vector3(6.3, 0, 0)), CurveMath.point(O + Vector3(9.2, 0, 1.1))], false, "H")
var g0: int = (persist.gestures(true) as Array).size()
map.set_curve_point(c, 1, {"p": O + Vector3(3.1, 0, 2.4)})
var pts: Array = map.curve_states()[c]["points"]
var want: Array = CurveMath.auto_handles(pts, false)
_ok("P1 auto points' handles are re-derived from the new points", _same(pts, want),
[pts[1]["h_out"], want[1]["h_out"]])
_ok("P2 ... in ONE gesture", (persist.gestures(true) as Array).size() == g0 + 1)
# A FREE point keeps its handles.
var f: String = map.create_curve([CurveMath.point(O + Vector3(0, 0, 9)), CurveMath.point(O + Vector3(3, 0, 9)),
CurveMath.point(O + Vector3(6, 0, 9))], false, "F")
var q1: Dictionary = (map.curve_states()[f]["points"] as Array)[1].duplicate()
q1["handle"] = "free"
q1["h_out"] = Vector3(0.0, 0.7, 0.0)
map.set_curve_point(f, 1, q1)
map.set_curve_point(f, 1, {"p": O + Vector3(3, 0, 10.5)})
var hf: Vector3 = (map.curve_states()[f]["points"] as Array)[1]["h_out"]
_ok("P3 a free point keeps its stored handle", hf.is_equal_approx(Vector3(0.0, 0.7, 0.0)), hf)