# text_tail_arrange_test —— §8.8 seam ⑩:ArrangeTextTail item 尾标去重叠 + isIn 1:1。 # boxes_overlap(isIn 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_overlap(isIn,: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 = 121;ys[1] 不变。 # 再看 i=1:此时第 1 条已在 y=121(x∈[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;与 2(100)此时已不撞(121 的框 y∈[119,137])。 # i=1:与 2(100)撞 -> 121;再与已排好的 0(121)撞 -> 121 + 21 = 142。 # i=2:0 在 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_owner(SetItemTextTailOwner, PythonTextTail.cpp:706-737)------ # 无 owner(owner_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 -> 退化") # 有 owner:yEnd += 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")