OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / maps / shapes / param / param_shapes.gd

80 lines · gd ref:
extends RefCounted
## The parametric shape TYPES, by id (docs/parametric-shapes-design.md). The id is
## what a shape.create record names, so it is durable: never rename one.

static var _types: Dictionary = {}


## IN TAB ORDER — the create tool cycles ids() as listed (§13: it makes only these).
## box / cylinder / cone / sphere are the baked generators in the local frame
## (param_basic.gd); the arch is the tunnel's round profile.
static func _ensure() -> void:
	if not _types.is_empty():
		return
	var basic := preload("res://scenes/maps/shapes/param/param_basic.gd")
	var list: Array = [basic.new("box", BoxShape.new()),
		preload("res://scenes/maps/shapes/param/param_stairs.gd").new(),
		preload("res://scenes/maps/shapes/param/param_spiral_stairs.gd").new(),
		preload("res://scenes/maps/shapes/param/param_tunnel.gd").new(),
		basic.new("cylinder", CylinderShape.new()),
		basic.new("cone", ConeShape.new()),
		basic.new("sphere", SphereShape.new())]
	for t: Object in list:
		_types[t.id()] = t
	# Generated FROM a brush (Make parametric), never drawn with the create tool, so it is
	# known to the fold but not in ids() (#120 F2).
	_types["hull"] = preload("res://scenes/maps/shapes/param/param_hull.gd").new()
	# Generated ON a curve (Add → Cable, #120 L3), not drawn as a box: also not in ids().
	_types["cable"] = preload("res://scenes/maps/shapes/param/param_cable.gd").new()
	# Drawn by BoxCutter (#120 L2 §13.1), not by the create tool: also not in ids().
	_types["prism"] = preload("res://scenes/maps/shapes/param/param_prism.gd").new()


static func get_type(type_id: String) -> Object:
	_ensure()
	return _types.get(type_id, null)


## The create tool's types, in tab order — every registered type but those that only
## come from a brush (hull), a curve (cable) or the BoxCutter (prism).
static func ids() -> Array:
	_ensure()
	return _types.keys().filter(func(k: String) -> bool: return k != "hull" and k != "cable" and k != "prism")


## A type's params as ToolSettingsBar rows ("p.<id>"), valued from `values` (resolved).
## The bar draws an enum from `choices` + an INDEX; a descriptor names `options` + a
## string — converted here and back in value_of, so the create tool and the Shape panel
## cannot disagree about it.
static func rows(t: Object, values: Dictionary) -> Array:
	var out: Array = []
	for d: Dictionary in (t.params() as Array):
		if bool(d.get("hidden", false)):
			continue                          # data, not a knob (the hull's planes)
		var row: Dictionary = d.duplicate()
		row["id"] = "p." + String(d["id"])
		var v: Variant = values.get(String(d["id"]), d["default"])
		if String(d.get("type")) == "enum":
			var opts: Array = d.get("options", [])
			row["choices"] = opts.map(func(o: Variant) -> String: return String(o).capitalize())
			row["value"] = maxi(0, opts.find(String(v)))
		else:
			row["value"] = v
		out.append(row)
	return out


## A bar value for param `pid` -> the record's value (an enum index -> its option), or
## null for an unknown param.
static func value_of(t: Object, pid: String, v: Variant) -> Variant:
	for d: Dictionary in (t.params() as Array):
		if String(d["id"]) != pid:
			continue
		if String(d.get("type")) == "enum":
			var opts: Array = d.get("options", [])
			if v is int or v is float:
				return String(opts[clampi(int(v), 0, opts.size() - 1)]) if not opts.is_empty() else ""
			return String(v)
		return t.resolve({pid: v})[pid]
	return null