treat biological actuators as normal budget-competing mass, fix explore-panel visibility

Biological actuators (Human Muscle, Animal Traction) were special-cased
out of the mass optimizer entirely: a fixed 70kg reference used only in
the power formula, excluded from the platform's mass budget and from
the explore-panel sliders. That made "bigger operator" or "more
operators" inexpressible, and required a power_mass/denom_offset
parameter pair throughout the physics code solely to keep this one
case's numerator mass separate from its budget mass.

Operator mass is now a normal, budget-competing, structurally-carried
variable sized by the same joint optimizer as any mechanical actuator,
with BIOLOGICAL_OPERATOR_MASS_KG reinterpreted as a floor (at least one
real operator) rather than a fixed value -- the explore slider now
reads as "how many/how large are the operators." Since every remaining
case set power_mass == actuator_mass and denom_offset == 0.0 anyway,
those parameters were entirely vestigial once biological's special
case was gone, so _raw_physics_from_masses drops them.

Also: the explore section was gated on `explore_result is not none`,
so combos with no free mass to explore (radiation-pressure sails, or
previously biological) showed nothing at all instead of the existing
explanatory message. Gated on `scores` instead, so the section always
renders and the message inside `_explore_result.html` is reachable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 19:17:59 -05:00
parent d871635779
commit 6cdd308583
3 changed files with 71 additions and 61 deletions

View File

@@ -1,7 +1,8 @@
{% if explore_result is none %}
<p class="empty">No free mass allocation to explore for this combination — its
actuator's mass isn't a design choice (a physiological or footprint-derived
quantity), or the platform has no declared mass ceiling to bound the sliders.</p>
actuator's mass isn't a design choice (a footprint-derived quantity, like a
radiation-pressure sail), or the platform has no declared mass ceiling to
bound the sliders.</p>
{% else %}
{% set r = explore_result %}
<div class="optimize-summary">

View File

@@ -129,7 +129,7 @@
</div>
{% endif %}
{% if explore_result is not none %}
{% if scores %}
<h2>Explore: Scale the Build</h2>
<p class="subtitle">
Purely exploratory — nothing here is saved. Drag a slider to pick a
@@ -141,6 +141,7 @@
or merely functional one.
</p>
<div class="card">
{% if explore_result is not none %}
{% set r = explore_result %}
<form id="explore-form"
hx-post="{{ url_for('results.explore', domain_name=domain.name, combo_id=combo.id) }}"
@@ -154,7 +155,7 @@
<output id="out_platform_mass">{{ "%.1f"|format(r.platform_mass) }}kg</output>
</div>
<div class="weight-slider-row">
<label for="actuator_mass">actuator (motor size)</label>
<label for="actuator_mass">actuator (motor size, or operator count/size for muscle power)</label>
<input type="range" min="{{ r.actuator_min }}" max="{{ r.actuator_slider_max }}" step="0.1"
id="actuator_mass" name="actuator_mass" value="{{ r.actuator_mass }}"
oninput="document.getElementById('out_actuator_mass').textContent = (+this.value).toFixed(1) + 'kg'">
@@ -168,6 +169,7 @@
<output id="out_storage_mass">{{ "%.1f"|format(r.storage_mass) }}kg</output>
</div>
</form>
{% endif %}
<div id="explore-result">
{% include "results/_explore_result.html" %}
</div>