| … | … | @@ -0,0 +1,135 @@ |
| 1 | extends Node |
| 2 | ## PROBE (not a gate) — fold cost of the L3 cable extras (#120, docs/node-tree-and-stacks.md |
| 3 | ## §14.8 step 7, "measured, not asserted"), the same method as _probe_mod_fold_time: a FULL |
| 4 | ## refold (the brush half forced), median of 3, and the live brush count, after each stage: |
| 5 | ## base the map, no cables |
| 6 | ## cable + one straight-ish 10 m cable, the defaults (round 8, max_len 1 m) |
| 7 | ## strands that cable with 7 strands |
| 8 | ## bundle + a 20-cable bundle between two boxes 10 m apart (slack 1 m: they hang and bend) |
| 9 | ## bundle7 the bundle's cables with 7 strands each |
| 10 | ## A cable's curve edit re-folds IN FULL (§11.2: a followed curve), so each line is also what |
| 11 | ## one curve edit costs in that room. |
| 12 | ## |
| 13 | ## RUN: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . res://scenes/test/_probe_cable_fold_time.tscn |
| 14 | |
| 15 | const BundleCables := preload("res://scenes/maps/curves/bundle_cables.gd") |
| 16 | const ParamShapes := preload("res://scenes/maps/shapes/param/param_shapes.gd") |
| 17 | const ShapeFold := preload("res://scenes/maps/shapes/param/shape_fold.gd") |
| 18 | |
| 19 | var persist: Node |
| 20 | var level: Node |
| 21 | var map: Node3D |
| 22 | |
| 23 | |
| 24 | func _ready() -> void: |
| 25 | var gs: Node = get_tree().root.get_node_or_null("/root/GameState") |
| 26 | gs.route("world:level:*", 1, "peer") |
| 27 | var keet: Node = get_tree().root.get_node_or_null("/root/KeetService") |
| 28 | if keet != null: |
| 29 | keet.current_member_id = "probe" |
| 30 | get_tree().root.get_node("/root/GameMode").set_mode("build") |
| 31 | map = (load("res://scenes/maps/map_dev_test.tscn") as PackedScene).instantiate() |
| 32 | add_child(map) |
| 33 | for i in 6: |
| 34 | await get_tree().process_frame |
| 35 | persist = map.get("_persist") |
| 36 | level = map.get("level") |
| 37 | var rows: Array = [] |
| 38 | rows.append(["base", _time(), _n()]) |
| 39 | # ---- one 10 m cable ---- |
| 40 | var c := map.add_cable([CurveMath.point(Vector3(300, 2, 300), Vector3.ZERO), CurveMath.point(Vector3(305, 3, 302), Vector3.ZERO), |
| 41 | CurveMath.point(Vector3(310, 2, 300), Vector3.ZERO)], false, {}) as String |
| 42 | rows.append(["cable", _time(), _n()]) |
| 43 | _set_params(c, {"strands": 7}) |
| 44 | rows.append(["strands 7", _time(), _n()]) |
| 45 | # ---- a 20-cable bundle: the facing sides of two boxes 10 m apart ---- |
| 46 | var A := {"pts": PackedVector3Array([Vector3(400, 0, 300), Vector3(400, 0, 302), Vector3(400, 2, 302), Vector3(400, 2, 300)]), "n": Vector3.RIGHT} |
| 47 | var B := {"pts": PackedVector3Array([Vector3(410, 0, 300), Vector3(410, 2, 300), Vector3(410, 2, 302), Vector3(410, 0, 302)]), "n": Vector3.LEFT} |
| 48 | var dots: Array = map.cable_bundle([A, B], {"count": 20, "random": true, "seed": 1, "tension": 1.0, |
| 49 | "jitter": 0.3, "slack": 1.0}, {"radius": 0.03}) |
| 50 | rows.append(["bundle 20", _time(), _n()]) |
| 51 | _split(dots) |
| 52 | persist.begin_gesture("strands") |
| 53 | for d: String in dots: |
| 54 | persist.record("shape.set", {"target": d, "params": {"strands": 7}}) |
| 55 | persist.end_gesture() |
| 56 | rows.append(["bundle 20 x 7 strands", _time(), _n()]) |
| 57 | var prev := 0 |
| 58 | for r: Array in rows: |
| 59 | print("PROBE %-24s full refold %8.1f ms %6d live brushes (+%d)" % [r[0], r[1], r[2], int(r[2]) - prev]) |
| 60 | prev = int(r[2]) |
| 61 | print("PROBE cables in the bundle: %d" % dots.size()) |
| 62 | get_tree().quit(0) |
| 63 | |
| 64 | |
| 65 | func _set_params(dot: String, p: Dictionary) -> void: |
| 66 | persist.begin_gesture("probe") |
| 67 | persist.record("shape.set", {"target": dot, "params": p}) |
| 68 | persist.end_gesture() |
| 69 | |
| 70 | |
| 71 | ## WHERE THE BUNDLE'S FOLD TIME GOES, the generator alone (no level), median of 3: |
| 72 | ## world_pieces for the 20 cables; its fine CurveMath.sample alone; and DCLevel.hull_of alone |
| 73 | ## on the same rings (the hulls world_pieces makes, re-hulled from their corners). |
| 74 | func _split(dots: Array) -> void: |
| 75 | var t: Object = ParamShapes.get_type("cable") |
| 76 | var curves: Dictionary = persist.curve_states() |
| 77 | var jobs: Array = [] |
| 78 | for d: String in dots: |
| 79 | var p: Dictionary = t.resolve(persist.shape_state(d)["params"]) |
| 80 | jobs.append([curves[String(p["curve"])], p]) |
| 81 | var wp: Array = [] |
| 82 | var smp: Array = [] |
| 83 | var hl: Array = [] |
| 84 | var pieces: Array = [] |
| 85 | for rep in 3: |
| 86 | var t0 := Time.get_ticks_usec() |
| 87 | pieces = [] |
| 88 | for j: Array in jobs: |
| 89 | pieces.append_array(t.world_pieces(j[0], j[1])) |
| 90 | wp.append(float(Time.get_ticks_usec() - t0) / 1000.0) |
| 91 | t0 = Time.get_ticks_usec() |
| 92 | for j2: Array in jobs: |
| 93 | var cv: Dictionary = j2[0] |
| 94 | var L := CurveMath.length(cv["points"], bool(cv["closed"])) |
| 95 | CurveMath.sample(cv["points"], bool(cv["closed"]), clampi(ceili(L / 0.02) + 1, 16, 4096)) |
| 96 | smp.append(float(Time.get_ticks_usec() - t0) / 1000.0) |
| 97 | var pts_all: Array = [] |
| 98 | for pc: Dictionary in pieces: |
| 99 | pts_all.append(FaceKit.corners(pc["planes"])) |
| 100 | t0 = Time.get_ticks_usec() |
| 101 | for cs: PackedVector3Array in pts_all: |
| 102 | DCLevel.hull_of(cs) |
| 103 | hl.append(float(Time.get_ticks_usec() - t0) / 1000.0) |
| 104 | wp.sort() |
| 105 | smp.sort() |
| 106 | hl.sort() |
| 107 | print("PROBE split (bundle 20, %d pieces): world_pieces %.1f ms | its fine sampling %.1f ms | hull_of alone %.1f ms" |
| 108 | % [pieces.size(), wp[1], smp[1], hl[1]]) |
| 109 | # ShapeFold.expand on the room's shape records: the generator + resolve_states + the |
| 110 | # brush.create records. What a full refold spends beyond this is the level's side. |
| 111 | var shape_recs: Array = (persist.records(false) as Array).filter(func(r: Dictionary) -> bool: return ShapeFold.handles(String(r["kind"]))) |
| 112 | var ex: Array = [] |
| 113 | for rep2 in 3: |
| 114 | var t1 := Time.get_ticks_usec() |
| 115 | ShapeFold.expand(shape_recs, {}, curves) |
| 116 | ex.append(float(Time.get_ticks_usec() - t1) / 1000.0) |
| 117 | ex.sort() |
| 118 | print("PROBE split: ShapeFold.expand (whole room) %.1f ms" % ex[1]) |
| 119 | |
| 120 | |
| 121 | func _n() -> int: |
| 122 | return (level.get_live_brush_ids() as PackedInt32Array).size() |
| 123 | |
| 124 | |
| 125 | ## A full refold, median of 3 (as _probe_mod_fold_time). |
| 126 | func _time() -> float: |
| 127 | var ts: Array = [] |
| 128 | for i in 3: |
| 129 | persist.set("_brush_ver_folded", -1) # force the brush half |
| 130 | persist.set("_ix_tail_ok", false) |
| 131 | var t0 := Time.get_ticks_usec() |
| 132 | persist.refold() |
| 133 | ts.append(float(Time.get_ticks_usec() - t0) / 1000.0) |
| 134 | ts.sort() |
| 135 | return ts[1] |