- Replace fragmented green/blue/orange accent scheme with unified hacker orange (#ff6a00) across all CSS, JS canvas drawing, and param palettes - Redesign designs.html: monospace font, clickable cards, grid background, remove A/B/C labels; copy to index.html as landing page - Add JetBrains Mono font across all views for consistent techy aesthetic - Sharpen border-radius (pills 20px→6px, back buttons→6px) for angular feel - Restructure RL buttons: key numbers inside buttons (larger, white), teal-green for thumbs-up, dark red-orange for thumbs-down - Fix RL button z-index (25→45) so they render above the bottom sheet - Add Xavier-scaled weight randomization (spread parameter)
133 lines
4.4 KiB
JavaScript
133 lines
4.4 KiB
JavaScript
// Parameter bar display
|
|
// Shows output parameters as horizontal bars, draggable in examples mode
|
|
|
|
const DEFAULT_PARAM_NAMES = ['Flow', 'Scale', 'Speed', 'Hue', 'Spread', 'Size', 'Trail', 'Turb', 'Attract', 'Radius', 'DispRate', 'DispAmt', 'Lifetime', 'Respawn', 'Advection', 'Inertia', 'Drag', 'Repulse', 'RepCnt', 'RepRate'];
|
|
const DEFAULT_PARAM_COLORS = ['#ff6a00', '#00ccff', '#ff6600', '#ff00cc', '#ffcc00', '#88ff00', '#0088ff', '#ff3366', '#9bff5f', '#59d3ff', '#ff8f3f', '#a0b7ff', '#f4ff7a', '#ffa8db', '#7dffc8', '#ffd166', '#8ad4ff', '#ff5f5f', '#ffc15f', '#ff8a3d'];
|
|
|
|
export class ParamDisplay {
|
|
constructor(container, numParams = 20) {
|
|
this.container = container;
|
|
this.numParams = numParams;
|
|
this.values = new Array(numParams).fill(0.5);
|
|
this.draggable = false;
|
|
this.onChange = null;
|
|
this.activeBar = -1;
|
|
this.paramNames = [...DEFAULT_PARAM_NAMES];
|
|
this.paramColors = [...DEFAULT_PARAM_COLORS];
|
|
this.build();
|
|
}
|
|
|
|
build() {
|
|
this.container.innerHTML = '';
|
|
this.bars = [];
|
|
|
|
for (let i = 0; i < this.numParams; i++) {
|
|
const row = document.createElement('div');
|
|
row.className = 'param-row';
|
|
|
|
const label = document.createElement('span');
|
|
label.className = 'param-label';
|
|
label.textContent = this.paramNames[i] || `p${i}`;
|
|
|
|
const track = document.createElement('div');
|
|
track.className = 'param-track';
|
|
track.dataset.index = i;
|
|
|
|
const fill = document.createElement('div');
|
|
fill.className = 'param-fill';
|
|
fill.style.background = this.paramColors[i] || '#888';
|
|
fill.style.width = '50%';
|
|
|
|
const val = document.createElement('span');
|
|
val.className = 'param-value';
|
|
val.textContent = '0.50';
|
|
|
|
track.appendChild(fill);
|
|
row.appendChild(label);
|
|
row.appendChild(track);
|
|
row.appendChild(val);
|
|
this.container.appendChild(row);
|
|
|
|
this.bars.push({ fill, val, track });
|
|
}
|
|
|
|
// Touch/mouse drag events on the container
|
|
const onStart = (e) => {
|
|
if (!this.draggable) return;
|
|
const target = e.target.closest('.param-track');
|
|
if (!target) return;
|
|
e.preventDefault();
|
|
this.activeBar = parseInt(target.dataset.index);
|
|
this._updateFromEvent(e);
|
|
};
|
|
|
|
const onMove = (e) => {
|
|
if (this.activeBar < 0 || !this.draggable) return;
|
|
e.preventDefault();
|
|
this._updateFromEvent(e);
|
|
};
|
|
|
|
const onEnd = () => {
|
|
this.activeBar = -1;
|
|
};
|
|
|
|
this.container.addEventListener('mousedown', onStart);
|
|
window.addEventListener('mousemove', onMove);
|
|
window.addEventListener('mouseup', onEnd);
|
|
this.container.addEventListener('touchstart', onStart, { passive: false });
|
|
this.container.addEventListener('touchmove', onMove, { passive: false });
|
|
this.container.addEventListener('touchend', onEnd);
|
|
}
|
|
|
|
_updateFromEvent(e) {
|
|
const i = this.activeBar;
|
|
if (i < 0) return;
|
|
const track = this.bars[i].track;
|
|
const rect = track.getBoundingClientRect();
|
|
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
|
const value = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
this.values[i] = value;
|
|
this._renderBar(i);
|
|
if (this.onChange) this.onChange(i, value, this.values);
|
|
}
|
|
|
|
update(values) {
|
|
for (let i = 0; i < this.numParams && i < values.length; i++) {
|
|
this.values[i] = values[i];
|
|
this._renderBar(i);
|
|
}
|
|
}
|
|
|
|
_renderBar(i) {
|
|
const v = this.values[i];
|
|
this.bars[i].fill.style.width = (v * 100) + '%';
|
|
this.bars[i].val.textContent = v.toFixed(2);
|
|
}
|
|
|
|
setDraggable(draggable) {
|
|
this.draggable = draggable;
|
|
this.container.classList.toggle('draggable', draggable);
|
|
}
|
|
|
|
setNamesAndColors(names, colors) {
|
|
this.paramNames = names || DEFAULT_PARAM_NAMES;
|
|
this.paramColors = colors || DEFAULT_PARAM_COLORS;
|
|
// Update existing bars in-place
|
|
for (let i = 0; i < this.numParams; i++) {
|
|
if (!this.bars[i]) continue;
|
|
const row = this.bars[i].fill.closest('.param-row');
|
|
const label = row?.querySelector('.param-label');
|
|
if (label) label.textContent = this.paramNames[i] || `p${i}`;
|
|
this.bars[i].fill.style.background = this.paramColors[i] || '#888';
|
|
}
|
|
}
|
|
|
|
rebuild(numParams, names, colors) {
|
|
this.numParams = numParams;
|
|
this.values = new Array(numParams).fill(0.5);
|
|
this.paramNames = names || DEFAULT_PARAM_NAMES;
|
|
this.paramColors = colors || DEFAULT_PARAM_COLORS;
|
|
this.activeBar = -1;
|
|
this.build();
|
|
}
|
|
}
|