SideFX Houdini customization package
git clone https://git.lucas.co/hou-control.git
otls-audit: record the float/vector consolidation
The status block gains a consolidation note with the old-to-new mapping
and commits, the current file, definition and help counts, and the
versioning rule. Section 2 drops the Labs dummy that lived in
developer_vector_direct, section 3 notes which failing nodes were
replaced, section 4 loses the rows for removed types and trims the
analysis row, section 5 loses the bypassed round wrangle, section 6
updates the bare Developer and help counts, section 7 notes the
float_ names are gone, and the appendix rows for the ten consolidated
or rewritten developer nodes were re-run today the same way.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
otls-audit.md | 64 +++++++++++++++++++++++++++++++----------------------------
1 file changed, 34 insertions(+), 30 deletions(-)
diff --git a/otls-audit.md b/otls-audit.md
index ed92d86..3f7c705 100644
--- a/otls-audit.md
+++ b/otls-audit.md
@@ -3,6 +3,21 @@
Audit of every HDA in `otls/`, run 2026-09-14 against Houdini 22.0.429 (Indie) with `hython`. Every library was loaded, every definition was instantiated with its contents unlocked, and every SOP was cooked once with a 10x10 polygon grid on each required input. The audit script and raw JSON are not in the repo; the method and its limits are at the end.
> **Status after the audit.** The 20 hollow files were deleted (`d3d8cb3`). The dummy children in section 2 were replaced with their current types in 29 assets (`aef363e`, `28dfa29`); the five `l_s_galante::im_cull` uses now point at a rebuilt `im_cull_pieces` asset; what remains are the SideFX Labs nodes and the `lucas::dev::` children in `im_bevel` and `im_relax`. The four VEX compile failures in section 3 are fixed. Sections 4 to 7 are untouched.
+>
+> **Consolidation (2026-09-16).** Every `developer_float_*` / `developer_vector_*` pair is now one type-agnostic node that reads the attribute's size at cook time, and the vector-only nodes were merged by function. Breaking changes to an HDA now get a new minor version; a type that is removed outright is simply deleted. `otls/` holds 194 files and 194 definitions (186 SOP, 6 COP, 2 Object); 10 definitions carry help.
+>
+> | old | new | commit |
+> |---|---|---|
+> | `developer_float_diffuse 1.1` | `developer_diffuse 1.0` | `b29b966` |
+> | `developer_float_initialize`, `developer_vector_initialize` | `developer_attribute_initialize 1.0` (one tab per attribute) | `10edab3` |
+> | `developer_float_concentrate`, `developer_vector_concentrate` | `developer_concentrate 1.0` | `8b1f499` |
+> | `developer_float_migrate 1.1`, `developer_vector_migrate 1.1` | `developer_migrate 1.2` (push, not average) | `e492c1c`, `67a644d` |
+> | `developer_float_bleed` | `developer_bleed 1.0` | `46fde3d` |
+> | `developer_vector_conform`, `developer_vector_direct`, `developer_vector_unify` | `developer_align 1.0` (a list of targets) | `95abc44` |
+> | `developer_vector_from_scalar` | `developer_gradient 1.0` | `95abc44` |
+> | `developer_vector_rotate` | `developer_rotate 1.0` | `95abc44` |
+>
+> Alongside: `developer_attribute_composite` gained vector and further scalar operations (`ffd6c75`), `developer_analysis` writes its statistics into the `<attr>_info` dictionary that `developer_attribute_initialize` creates, whose range is one vector2 key (`8e87ec2`, `43ba011`). No hip in the repo used any removed type; hips elsewhere that did will load those nodes as empty placeholders.
| | count |
|---|---|
@@ -96,7 +111,6 @@ Affected nodes and the dummy children inside them:
| `im_bevel::1.0` | `/mycurve1` (lucas::dev::mycurve::1.0), `/morphorotatevec1` (lucas::dev::morphorotatevec::1.0) |
| `im_split::1.0` | `/im_delete1` (l_s_galante::im_delete::1.1), `/im_delete2` (l_s_galante::im_delete::1.1) |
| `developer_surface_remesh::1.0` | `/subnet1/im_ramp_color1` (im_ramp_color::1.0) |
-| `developer_vector_direct::1.0` | `/attribute_normalize_vector1` (labs::attribute_normalize_vector::1.0) |
| `gem_mold_root::1.0` | `/im_delete4` (im_delete::1.0) |
| `gem_mold_structure::1.0` | `/im_orient1` (im_orient::1.0) |
| `gem_partition::1.0` | `/IMMMinvertmesh1` (lsg::dev::IMMMinvertmesh::1.0) |
@@ -125,6 +139,8 @@ Four `developer_*` nodes fail regardless of input because a VOP inside them does
- `developer_vector_direct::1.0`: `multiplier/attribvop1`: No matching function for vector chs(string). Candidates are: string chs(string), string chs(string; float)
- `developer_vector_from_scalar::1.0`: `near_pts/attribvop1`: Reference to undefined variable: P
+Since then `developer_float_migrate`, `developer_vector_direct` and `developer_vector_from_scalar` have been replaced by `developer_migrate`, `developer_align` and `developer_gradient`, and `developer_attribute_composite` was rewritten; all four cook.
+
`im_pose` and `im_sample` also fail, but only because they need a second input the test did not supply. Eight GEM and IM nodes warn that a for-each block has no pieces, which is expected on a plain grid. Nothing else errored or warned.
## 4. Parameters nothing reads
@@ -133,28 +149,20 @@ For each top-level parameter the audit looked for a channel reference from any c
| node | unreferenced parameters |
|---|---|
-| `developer_analysis::1.0` | `enable_round`, `round_places`, `enable_prev` |
+| `developer_analysis::1.0` | `enable_prev` (`enable_round` and `round_places` are read since `8e87ec2`) |
| `developer_attribute_clip::1.0` | `range` |
| `developer_attribute_combine::1.0` | `input_0`, `type_0`, `weight_0`, `x_0`, `y_0`, `z_0`, `input_1`, `type_1`, `weight_1`, `x_1`, `y_1`, `z_1` |
-| `developer_attribute_composite::1.0` | `output_attr` |
| `developer_attribute_constant::1.0` | `class` |
| `developer_attribute_remap::1.0` | `class`, `type` |
| `developer_charge::1.0` | `release`, `amount` |
| `developer_embryo::1.0` | `source` |
| `developer_expire::1.0` | `attr` |
-| `developer_float_concentrate::1.0` | `global_intensity`, `global_cutoff`, `connectivity_depth` |
-| `developer_float_diffuse::1.1` | `connect_depth` |
-| `developer_float_migrate::1.1` | `connect_depth`, `infl`, `multiplier`, `multi_infl` |
| `developer_lead::1.0` | `amt` |
| `developer_normalize::1.0` | `type`, `method` |
| `developer_select::1.0` | `rnd_float_coverage`, `rnd_float_coverage_seed`, `rnd_float_variation`, `rnd_float_variation_seed`, `rnd_float_multiplier`, `newparameter` |
| `developer_surface_detangle::1.0` | `developer_iteration_ct` |
| `developer_surface_remesh::1.0` | `transfer_method` |
| `developer_surface_suture::1.0` | `distthresh`, `fusionthresh` |
-| `developer_vector_conform::1.0` | `weight_ramp` |
-| `developer_vector_direct::1.0` | `bias_vec_type`, `bias_vec_const`, `bias_vec_attr`, `bias_vec_infl`, `bias_scalar_unity`, `bias_scalar_infl`, `unity_multi_infl`, `unity_multi_attr` |
-| `developer_vector_migrate::1.1` | `input_reference`, `infl`, `multi`, `multi_infl`, `simbi_enable`, `simbi_amt`, `simbi_tol`, `simbi_weight` |
-| `developer_vector_unify::1.0` | `bias_radius`, `biasdisteff`, `biasm_ulti_attr` |
| `developer_weight::1.0` | `attr`, `multiplier` |
| `gem_build_area::1.0` | `machine`, `dimensions` |
| `gem_mold_solver::1.0` | `thickness`, `frame` |
@@ -217,11 +225,12 @@ For each top-level parameter the audit looked for a channel reference from any c
The two Object assets (`gem_build_area`, `im_cam`) are excluded: their unreferenced parameters are the standard object transform and render pages.
+Rows for the types removed by the consolidation are gone; their replacements read every parameter they expose.
+
## 5. Bypassed children
Bypassed nodes inside a locked asset are usually experiments that were never removed:
-- `developer_analysis::1.0`: `/round`
- `developer_metamax::1.0`: `/timeshift1`
- `developer_surface_remesh::1.0`: `/subnet1/tridivide1`
- `developer_volume_miasma::1.0`: `/isooffset1`
@@ -246,8 +255,8 @@ Bypassed nodes inside a locked asset are usually experiments that were never rem
- `im_grid` and `gem_build_area` each exist in two categories (COP and SOP; Object and SOP). That is legal, just easy to confuse.
- **Typo**: `im_distrubution`.
- **Labels**: `im_cam` is labelled `im_cam` and `im_blaster` is `IM Blaster` with two spaces. Every other label matches its type name.
-- **Tab menu**: `im_vdb_visualize_intersections` has no submenu, so it lands at the top level of the tab menu. Everything else sorts into `Immutable Methods/*`, `Developer/*` or `GEM`; two nodes sit in bare `Immutable Methods` and 20 in bare `Developer`.
-- **Help**: only `gem_orient` has embedded help. The other 197 have none.
+- **Tab menu**: `im_vdb_visualize_intersections` has no submenu, so it lands at the top level of the tab menu. Everything else sorts into `Immutable Methods/*`, `Developer/*` or `GEM`; two nodes sit in bare `Immutable Methods` and 27 in bare `Developer` (the consolidated nodes sit there rather than in `Developer/Float` or `Developer/Vector`).
+- **Help**: at the audit only `gem_orient` had embedded help. Ten definitions have it now: `gem_orient`, `im_cull_pieces` and the eight consolidated or rewritten `developer_*` nodes (`align`, `attribute_composite`, `attribute_initialize`, `bleed`, `concentrate`, `gradient`, `migrate`, `rotate`). The other 184 have none.
- **Icons**: 8 nodes carry their own SVG (`im_bounds`, `im_box`, `im_count`, `im_cull`, `im_grid`, `im_line 1.1`, `im_skeletonize`, `im_wave`); the rest use the generic subnet icon.
- **Hardcoded paths**: `im_multistain` reads two preset JSON files from `/Users/lucas/src/hou-immutable-methods/presets/`, which does not exist on this machine. Separately, 29 nodes carry fallback library paths to the old repos listed in section 2; those are informational and only consulted when a type is missing.
- **Event scripts**: all 23 `PythonModule` sections and the three Python `OnCreated`/`OnUpdated` handlers compile. `developer_solver` and `gem_mold_solver` have HScript `OnCreated` handlers, correctly flagged as non-Python.
@@ -255,13 +264,13 @@ Bypassed nodes inside a locked asset are usually experiments that were never rem
## 7. developer.md is out of date
-The operator list in `developer.md` uses **Scalar** names (`Scalar Diffuse`, `Scalar Migrate`, …) while the nodes on disk are `developer_float_*`, and it still lists `Subdivide`, `Remesh`, `Detangle`, `Combine`, `Constant` under their pre-rename names. 34 of its 55 items match no node, and 30 of the 51 `developer_*` nodes on disk are not in the document, including the whole `developer_attribute_*` family, `developer_time_analysis`, `developer_charge` and `developer_release`.
+The operator list in `developer.md` uses **Scalar** names (`Scalar Diffuse`, `Scalar Migrate`, …) while the nodes on disk were `developer_float_*` (since merged into type-agnostic `developer_*` nodes, see the status block), and it still lists `Subdivide`, `Remesh`, `Detangle`, `Combine`, `Constant` under their pre-rename names. 34 of its 55 items match no node, and 30 of the 51 `developer_*` nodes on disk at the time (47 now) are not in the document, including the whole `developer_attribute_*` family, `developer_time_analysis`, `developer_charge` and `developer_release`.
## Method and limits
- Loaded with `hou.hda.definitionsInFile` and `installFile`, then instantiated in `/obj`, `/img` and a scratch geo. Locked HDAs in `hython` load their contents lazily, so each instance was unlocked with `allowEditingOfContents()` before inspection. Nothing was saved.
- Dummy children were identified by `type().definition().libraryFilePath() == "Embedded"`.
-- Cooking used a 10x10 grid on each required input at frame 1. Nodes built for the SOP solver were cooked standalone, so an error there means the VEX does not compile, not that the node misbehaves in a sim. Nothing was cooked on real input data.
+- Cooking used a 10x10 grid on each required input at frame 1. The appendix rows for the consolidated nodes were re-run on 2026-09-16 the same way. Nodes built for the SOP solver were cooked standalone, so an error there means the VEX does not compile, not that the node misbehaves in a sim. Nothing was cooked on real input data.
- The parameter-usage check is textual and channel-reference based. It does not follow `hidewhen`, menu scripts, or Python that builds a parameter name at runtime.
- Viewer states, handles, and anything needing `hou.ui` were not exercised.
@@ -269,32 +278,35 @@ The operator list in `developer.md` uses **Scalar** names (`Scalar Diffuse`, `Sc
| type | ver | category | submenu | inputs | parms | unread | dummies | bypassed | cook |
|---|---|---|---|---|---|---|---|---|---|
-| `developer_analysis` | 1.0 | Sop | Developer | 1-2 | 13 | 3 | 0 | 1 | ok |
+| `developer_align` | 1.0 | Sop | Developer | 1-1 | 11 | 0 | 0 | 0 | ok |
+| `developer_analysis` | 1.0 | Sop | Developer | 1-2 | 13 | 1 | 0 | 0 | ok |
| `developer_attribute_clip` | 1.0 | Sop | Developer/Attribute | 0-1 | 1 | 1 | 0 | 0 | ok |
| `developer_attribute_combine` | 1.0 | Sop | Developer/Attribute | 1-2 | 18 | 12 | 0 | 0 | ok |
-| `developer_attribute_composite` | 1.0 | Sop | Developer/Attribute | 1-2 | 5 | 1 | 0 | 0 | failed |
+| `developer_attribute_composite` | 1.0 | Sop | Developer/Attribute | 1-2 | 5 | 0 | 0 | 0 | ok |
| `developer_attribute_constant` | 1.0 | Sop | Developer/Attribute | 0-1 | 5 | 1 | 0 | 0 | ok |
+| `developer_attribute_initialize` | 1.0 | Sop | Developer/Attribute | 0-1 | 15 | 0 | 0 | 0 | ok |
| `developer_attribute_promote` | 1.0 | Sop | Developer/Attribute | 1-1 | 4 | 0 | 0 | 0 | ok |
| `developer_attribute_remap` | 1.0 | Sop | Developer/Attribute | 0-1 | 9 | 2 | 0 | 0 | ok |
+| `developer_bleed` | 1.0 | Sop | Developer | 0-1 | 2 | 0 | 0 | 0 | ok |
| `developer_charge` | 1.0 | Sop | Developer | 0-1 | 2 | 2 | 0 | 0 | ok |
+| `developer_concentrate` | 1.0 | Sop | Developer | 1-1 | 9 | 0 | 0 | 0 | ok |
| `developer_core` | 1.0 | Sop | Developer/Volume | 0-1 | 0 | 0 | 0 | 0 | ok |
| `developer_cull` | 1.0 | Sop | Developer | 1-1 | 2 | 0 | 0 | 0 | ok |
| `developer_develop` | 1.0 | Sop | Developer | 1-1 | 4 | 0 | 0 | 0 | ok |
+| `developer_diffuse` | 1.0 | Sop | Developer | 1-1 | 8 | 0 | 0 | 0 | ok |
| `developer_embryo` | 1.0 | Sop | Developer | 0-2 | 14 | 1 | 0 | 0 | ok |
| `developer_expire` | 1.0 | Sop | Developer | 1-1 | 1 | 1 | 0 | 0 | ok |
-| `developer_float_bleed` | 1.0 | Sop | Developer/Float | 0-1 | 2 | 0 | 0 | 0 | ok |
-| `developer_float_concentrate` | 1.0 | Sop | Developer/Float | 1-1 | 8 | 3 | 0 | 0 | ok |
-| `developer_float_diffuse` | 1.1 | Sop | Developer/Float | 1-1 | 9 | 1 | 0 | 0 | ok |
-| `developer_float_initialize` | 1.0 | Sop | Developer/Float | 0-1 | 6 | 0 | 0 | 0 | ok |
-| `developer_float_migrate` | 1.1 | Sop | Developer/Float | 1-1 | 13 | 4 | 0 | 0 | failed |
+| `developer_gradient` | 1.0 | Sop | Developer | 1-1 | 8 | 0 | 0 | 0 | ok |
| `developer_id` | 1.0 | Sop | Developer | 1-1 | 1 | 0 | 0 | 0 | ok |
| `developer_lead` | 1.0 | Sop | Developer | 1-1 | 3 | 1 | 0 | 0 | ok |
| `developer_measure` | 1.0 | Sop | Developer | 1-1 | 36 | 0 | 0 | 0 | ok |
| `developer_metamax` | 1.0 | Sop | Developer | 1-1 | 3 | 0 | 0 | 1 | ok |
+| `developer_migrate` | 1.2 | Sop | Developer | 1-1 | 13 | 0 | 0 | 0 | ok |
| `developer_normalize` | 1.0 | Sop | Developer | 1-1 | 4 | 2 | 0 | 0 | ok |
| `developer_panel` | 1.0 | Sop | Developer | 0-1 | 3 | 0 | 0 | 0 | ok |
| `developer_region_center` | 1.0 | Sop | Developer | 1-1 | 3 | 0 | 0 | 0 | ok |
| `developer_release` | 1.0 | Sop | Developer | 0-1 | 0 | 0 | 0 | 0 | ok |
+| `developer_rotate` | 1.0 | Sop | Developer | 0-1 | 8 | 0 | 0 | 0 | ok |
| `developer_select` | 1.0 | Sop | Developer | 1-1 | 31 | 6 | 0 | 0 | ok |
| `developer_solver` | 1.0 | Sop | Developer | 0-1 | 8 | 0 | 0 | 0 | ok |
| `developer_submute_begin` | 1.0 | Sop | Developer | 1-1 | 2 | 0 | 0 | 0 | ok |
@@ -310,14 +322,6 @@ The operator list in `developer.md` uses **Scalar** names (`Scalar Diffuse`, `Sc
| `developer_time_analysis` | 1.0 | Sop | Developer/Time | 1-1 | 2 | 0 | 0 | 0 | ok |
| `developer_time_ramp` | 1.0 | Sop | Developer/Time | 0-0 | 3 | 0 | 0 | 0 | ok |
| `developer_time_switch` | 1.0 | Sop | Developer/Time | 1-2 | 4 | 0 | 0 | 0 | ok |
-| `developer_vector_concentrate` | 1.0 | Sop | Developer/Vector | 1-1 | 8 | 0 | 0 | 0 | ok |
-| `developer_vector_conform` | 1.0 | Sop | Developer/Vector | 0-1 | 6 | 1 | 0 | 0 | ok |
-| `developer_vector_direct` | 1.0 | Sop | Developer/Vector | 1-1 | 12 | 8 | 1 | 0 | failed |
-| `developer_vector_from_scalar` | 1.0 | Sop | Developer/Vector | 1-1 | 6 | 0 | 0 | 0 | failed |
-| `developer_vector_initialize` | 1.0 | Sop | Developer/Vector | 0-1 | 8 | 0 | 0 | 0 | ok |
-| `developer_vector_migrate` | 1.1 | Sop | Developer/Vector | 1-1 | 14 | 8 | 0 | 0 | ok |
-| `developer_vector_rotate` | 1.0 | Sop | Developer/Vector | 0-1 | 5 | 0 | 0 | 0 | ok |
-| `developer_vector_unify` | 1.0 | Sop | Developer/Vector | 1-1 | 17 | 3 | 0 | 0 | ok |
| `developer_vitality` | 1.0 | Sop | Developer | 1-2 | 1 | 0 | 0 | 0 | ok |
| `developer_volume_miasma` | 1.0 | Sop | Developer/Volume | 0-1 | 0 | 0 | 0 | 1 | ok |
| `developer_weight` | 1.0 | Sop | Developer | 0-1 | 2 | 2 | 0 | 0 | ok |