Complete remaining client gap features

This commit is contained in:
shenlei
2026-09-04 13:25:05 +09:00
parent 13ccb8d02d
commit 9a4a044da5
47 changed files with 6667 additions and 140 deletions
+152
View File
@@ -0,0 +1,152 @@
# text_tail_arrange_test —— §8.8 seam ⑩:ArrangeTextTail item 尾标去重叠 + isIn 1:1。
# boxes_overlapisIn AABB,含边界)、box_* 四边(-w/2-2 / w/2+2 / -2 / h)、
# arrange_item_tails(单条不动 / 两条不叠不动 / 两条叠 -> 后者顶到前者框底下 /
# 级联下推 / 病态全同位收敛在 MAX_ITERS 内)、
# item_box_with_owner(增量 125:无 owner 退化 / 有 owner -> h += ownerH + 4、
# owner 更宽时 w = ownerW - 2 使 box_end_x == ownerW/2 + 1)。
# godot --headless --path project --script text_tail_arrange_test.gd
# 退出码 0 = 全过。
extends SceneTree
const TextTailArrange = preload("res://text_tail_arrange.gd")
var _fail := 0
func _ck(c: bool, m: String) -> void:
if not c:
_fail += 1
printerr("FAIL: " + m)
func _feq(a: float, b: float, m: String) -> void:
_ck(absf(a - b) < 0.0001, "%s (got %f want %f)" % [m, a, b])
func _init() -> void:
_run()
if _fail == 0:
print("PASS: text_tail_arrange_test (§8.8 seam ⑩ ArrangeTextTail item 尾标去重叠 + isIn 1:1)")
quit(0)
else:
printerr("%d check(s) failed" % _fail)
quit(1)
func _run() -> void:
# --- 0. 常量 -------------------------------------------------------------
_feq(TextTailArrange.PUSH_GAP, 5.0, "PUSH_GAP = yTemp = 5:164")
_feq(TextTailArrange.MAX_ITERS, 20.0, "MAX_ITERS = LimitCount 上限 20:176")
# --- 1. 框四边(PythonTextTail.cpp:810-813----------------------------
_feq(TextTailArrange.box_start_x(40.0), -40.0 / 2.0 - 2.0, "xStart = -w/2 - 2 = -22")
_feq(TextTailArrange.box_end_x(40.0), 40.0 / 2.0 + 2.0, "xEnd = w/2 + 2 = 22")
_feq(TextTailArrange.box_start_y(), -2.0, "yStart = -2")
_feq(TextTailArrange.box_end_y(16.0), 16.0, "yEnd = h = 16")
# --- 2. boxes_overlapisIn:489-507-------------------------------
var a := {"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0} # x∈[78,122] y∈[98,116]
var b_hit := {"x": 110.0, "y": 105.0, "w": 40.0, "h": 16.0} # x∈[88,132] y∈[103,121]
_ck(TextTailArrange.boxes_overlap(a, b_hit), "x、y 都交叠 -> 相交")
_ck(TextTailArrange.boxes_overlap(b_hit, a), "isIn 对称")
# 边界相接:inclusive <= / >= -> 视为相交
var b_edge := {"x": 100.0 + 44.0, "y": 100.0, "w": 40.0, "h": 16.0} # x1T = 144-22 = 122 == x2S
_ck(TextTailArrange.boxes_overlap(a, b_edge), "边界相接(x2S == x1T)含边界 -> 相交")
# x 完全分离
var b_farx := {"x": 100.0 + 45.0, "y": 100.0, "w": 40.0, "h": 16.0} # x1T = 145-22 = 123 > 122
_ck(not TextTailArrange.boxes_overlap(a, b_farx), "x 分离 -> 不相交")
# y 完全分离
var b_fary := {"x": 100.0, "y": 100.0 + 40.0, "w": 40.0, "h": 16.0} # y1T = 140-2 = 138 > 116
_ck(not TextTailArrange.boxes_overlap(a, b_fary), "y 分离 -> 不相交")
# --- 3. arrange_item_tails ---------------------------------------------
# 单条:不动
var one := [{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0}]
_feq(TextTailArrange.arrange_item_tails(one)[0], 100.0, "单条 -> y 不变")
# 两条不叠:都不动
var two_far := [
{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0},
{"x": 100.0, "y": 300.0, "w": 40.0, "h": 16.0},
]
var r_far := TextTailArrange.arrange_item_tails(two_far)
_feq(r_far[0], 100.0, "不叠 -> 第 1 条不变")
_feq(r_far[1], 300.0, "不叠 -> 第 2 条不变")
# 两条叠:外层先处理 i=0 —— 与 j=1 相交,把「自己」(第 1 条)顶到第 2 条框底下。
# ys[0] = ys[1] + yEnd(第2条=16) + PUSH_GAP(5) = 100 + 16 + 5 = 121ys[1] 不变。
# 再看 i=1:此时第 1 条已在 y=121x∈[78,122] y∈[119,137]),第 2 条 y=100
# y∈[98,116])——115? 119 > 116 -> 不再相交,第 2 条不动。
var two_hit := [
{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0},
{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0},
]
var r_hit := TextTailArrange.arrange_item_tails(two_hit)
_feq(r_hit[0], 100.0 + 16.0 + 5.0, "两条同位 -> 第 1 条被顶到 121")
_feq(r_hit[1], 100.0, "两条同位 -> 第 2 条留原位")
_ck(not TextTailArrange.boxes_overlap(
{"x": 100.0, "y": r_hit[0], "w": 40.0, "h": 16.0},
{"x": 100.0, "y": r_hit[1], "w": 40.0, "h": 16.0}),
"排布后两框不再相交")
# 三条同位。逐条各自移动自己(参考端每个 pInsert 只改自己的 y):
# i=0:与 1 撞 -> ys[0] = 100 + 16 + 5 = 121;与 2100)此时已不撞(121 的框 y∈[119,137])。
# i=1:与 2100)撞 -> 121;再与已排好的 0121)撞 -> 121 + 21 = 142。
# i=20 在 121、1 在 142,都在 100 上方(y 更大 = 屏幕更下),100 的槽已空出 -> 第 3 条不动。
# 结果 [121, 142, 100]:三框互不相交(100→[98,116]、121→[119,137]、142→[140,158])。
var three := [
{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0},
{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0},
{"x": 100.0, "y": 100.0, "w": 40.0, "h": 16.0},
]
var r3 := TextTailArrange.arrange_item_tails(three)
_feq(r3[0], 121.0, "三条同位 -> 第 1 条 121")
_feq(r3[1], 142.0, "三条同位 -> 第 2 条 142")
_feq(r3[2], 100.0, "三条同位 -> 第 3 条留原位(前两条已让开)")
# 两两不再相交
for p in [[0, 1], [0, 2], [1, 2]]:
_ck(not TextTailArrange.boxes_overlap(
{"x": 100.0, "y": r3[p[0]], "w": 40.0, "h": 16.0},
{"x": 100.0, "y": r3[p[1]], "w": 40.0, "h": 16.0}),
"三条排布后 %d/%d 不相交" % p)
# 病态:很多条全同位 —— 单条被下推次数封顶 MAX_ITERS,函数必然返回(不死循环)。
var many: Array = []
for k in 40:
many.append({"x": 0.0, "y": 0.0, "w": 30.0, "h": 12.0})
var rm := TextTailArrange.arrange_item_tails(many)
_ck(rm.size() == 40, "40 条全同位 -> 正常返回 40 个 y(未死循环)")
# 第 1 条只需 1 次下推就让开其余全部(它们都还在 y=0):0 + 12 + 5 = 17。
_feq(rm[0], 17.0, "第 1 条一次下推到 17")
# 靠后的条目下推次数封顶 MAX_ITERS:每次至多 +12 + 5= 17 -> <= 340。
for k in 40:
_ck(rm[k] <= (12.0 + 5.0) * TextTailArrange.MAX_ITERS + 0.001,
"%d 条下推量不超过 MAX_ITERS * (h + gap) = 340" % (k + 1))
# --- 4. 列表顺序即 m_ItemTextTailList 顺序:靠前的先让位(各自移动自己)-------
var ordered := [
{"x": 50.0, "y": 200.0, "w": 20.0, "h": 10.0},
{"x": 50.0, "y": 203.0, "w": 20.0, "h": 10.0},
]
var ro := TextTailArrange.arrange_item_tails(ordered)
_ck(ro[0] > ro[1], "靠前的条目被顶到靠后条目下方(参考端各 pInsert 移动自己)")
_feq(ro[1], 203.0, "靠后的条目保持原位")
# --- 5. item_box_with_ownerSetItemTextTailOwner, PythonTextTail.cpp:706-737------
# 无 ownerowner_w / owner_h <= 0-> 退化为纯名字框
var b0 := TextTailArrange.item_box_with_owner(40.0, 16.0)
_feq(b0["w"], 40.0, "无 owner -> w = name_w")
_feq(b0["h"], 16.0, "无 owner -> h = name_h")
_feq(TextTailArrange.item_box_with_owner(40.0, 16.0, 0.0, 12.0)["h"], 16.0, "owner_w<=0 -> 退化")
_feq(TextTailArrange.item_box_with_owner(40.0, 16.0, 30.0, 0.0)["h"], 16.0, "owner_h<=0 -> 退化")
# 有 owneryEnd += ownerH + 4:735
var bo := TextTailArrange.item_box_with_owner(40.0, 16.0, 30.0, 12.0)
_feq(bo["h"], 16.0 + 12.0 + 4.0, "有 owner -> h = name_h + owner_h + 4 = 32")
_feq(bo["w"], 40.0, "owner 比名字窄 -> w 仍 = name_w")
_feq(TextTailArrange.box_end_x(bo["w"]), 40.0 / 2.0 + 2.0, "owner 窄 -> box_end_x(w) == nameW/2 + 2")
# owner 比名字宽 -> box_end_x 收进 fMAX(nameW/2+2, ownerW/2+1):737-> w = owner_w - 2
var bw := TextTailArrange.item_box_with_owner(20.0, 16.0, 50.0, 12.0)
_feq(bw["w"], 50.0 - 2.0, "owner 更宽 -> w = owner_w - 2 = 48")
_feq(TextTailArrange.box_end_x(bw["w"]), 50.0 / 2.0 + 1.0, "-> box_end_x(w) == ownerW/2 + 1(参考 :737 fMAX")
# 顶到带 owner 的框底下时 push = yEnd + PUSH_GAP
var stacked := [
{"x": 0.0, "y": 0.0, "w": 20.0, "h": 16.0},
{"x": 0.0, "y": 0.0, "w": bo["w"], "h": bo["h"]},
]
_feq(TextTailArrange.arrange_item_tails(stacked)[0], bo["h"] + TextTailArrange.PUSH_GAP,
"靠前条被顶到带 owner 框底下 = (name_h + owner_h + 4) + PUSH_GAP = 37")