extends LevelOperator
## DROP TO THE GROUND (#120 L3, docs/node-tree-and-stacks.md §14.3): Cablerator's Drop the
## Cable on the selected cables. Every point but an open cable's two ends falls straight
## down and rests on what it hits; spans longer than `step` get points first, so the cable
## follows the ground. The preview draws the dropped curves; Enter writes ONE curve.set per
## cable (map.drop_cables, one gesture). Adjust Last re-runs it with another step.
var step := 1.0 # metres between added points (0 = none added)
var _targets: Array = [] # the cables' dots
var _frozen := false # Adjust Last: act on the stored cables, not the selection
func op_name() -> String:
return "drop to ground"
## The mouse drags the focused value (§17.7 Q2): this tool works on the selection.
func drags_value() -> bool:
return true
func gesture_label() -> String:
return "Drop to ground"
func preview_color() -> Color:
return Color(1.0, 0.85, 0.3, 0.9) # the curve view's amber
func setup_modal(m: ModalControls) -> void:
m.confirm_label = "drop"
m.table([["", "cables", func() -> String: return "%d" % _targets.size()]])
m.bind("step", "add points every", func() -> float: return step,
func(v: float) -> void:
step = maxf(0.0, v)
update_preview(),
0.25, " m", 0.05)
func status() -> String:
return modal().status_text()
func on_activate() -> bool:
if not _frozen:
_targets = map.selected_cables() if map != null and map.has_method("selected_cables") else []
if _targets.is_empty():
print("[drop] select a cable first")
return false
update_preview()
return true
func on_deactivate() -> void:
clear_preview()
func handle_key(event: InputEvent) -> bool:
return modal().handle_key(event)
## The dropped curves, as lines (nothing is written until confirm).
func update_preview() -> void:
clear_preview()
var lines := PackedVector3Array()
for d: String in _targets:
var pts: Array = map.dropped_points(d, step)
var curves: Dictionary = map.curve_states()
var cd := String((map._persist.shape_state(d)["params"] as Dictionary).get("curve", ""))
var closed := bool(curves.get(cd, {}).get("closed", false))
if pts.size() < 2:
continue
var fs := CurveMath.sample(pts, closed, 64)
for i in range(1, fs.size()):
lines.append(fs[i - 1]["p"])
lines.append(fs[i]["p"])
draw_lines(lines)
func confirm() -> bool:
var n: int = map.drop_cables(_targets, step)
if n == 0:
return false
ModalControls.summary("Dropped %d cable%s%s" % [n, "" if n == 1 else "s",
", a point every %.2f m" % step if step > 0.0 else ""])
return true
func tool_settings() -> Array:
return [{"id": "step", "label": "Add points every", "type": "float", "value": step, "min": 0.0,
"max": 20.0, "step": 0.25, "tip": "Spans longer than this get points first, so the cable follows the ground (0 = none)."}]
func set_tool_setting(sid: String, value: Variant) -> bool:
if sid != "step":
return false
step = maxf(0.0, float(value))
if not _frozen:
update_preview()
return true
func adjust_ids() -> Array:
return ["step"]
func adjust_snapshot() -> Dictionary:
return {"targets": _targets.duplicate(), "step": step}
func adjust_restore(snap: Dictionary) -> void:
_targets = (snap.get("targets", []) as Array).duplicate()
step = float(snap.get("step", step))
_frozen = true
func adjust_end() -> void:
_frozen = false
clear_preview()