forked from mikael-lovqvist/websperiments
Strip pipeline/palette/depth/relax UI; lock to paint mode
- Remove Pipeline stage, Palette, Mesh depth, Relaxation UI sections - Remove Build button; mesh always builds as relaxed Goldberg depth-5 - Hardcode get_params() — no DOM reads for build parameters - Remove current_stage; use_relaxed is always true - Tool buttons no longer toggle paint off when re-clicking active tool Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -90,48 +90,6 @@
|
|||||||
<h3>Sphere Playground</h3>
|
<h3>Sphere Playground</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3>Pipeline stage</h3>
|
|
||||||
<div class="stage-grid" style="margin-top:6px">
|
|
||||||
<button class="sb" data-stage="ico">Icosahedron</button>
|
|
||||||
<button class="sb" data-stage="subdiv">Subdivided</button>
|
|
||||||
<button class="sb" data-stage="goldberg">Goldberg</button>
|
|
||||||
<button class="sb active" data-stage="relaxed">Relaxed</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3>Palette</h3>
|
|
||||||
<div id="palette-grid" class="stage-grid" style="margin-top:6px">
|
|
||||||
<!-- populated by JS -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3>Mesh</h3>
|
|
||||||
<div class="param" style="margin-top:6px">
|
|
||||||
<label>Depth</label>
|
|
||||||
<input type="range" id="depth" min="1" max="5" value="5">
|
|
||||||
<span class="val" id="depth-val">5</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3>Relaxation</h3>
|
|
||||||
<div class="param" style="margin-top:6px">
|
|
||||||
<label>Iterations</label>
|
|
||||||
<input type="number" id="iters" value="500" min="0" step="50">
|
|
||||||
</div>
|
|
||||||
<div class="param">
|
|
||||||
<label>α edge</label>
|
|
||||||
<input type="number" id="alpha-edge" value="0" min="0" step="0.01">
|
|
||||||
</div>
|
|
||||||
<div class="param">
|
|
||||||
<label>α centroid</label>
|
|
||||||
<input type="number" id="alpha-centroid" value="0.04" min="0" step="0.001">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3>Paint</h3>
|
<h3>Paint</h3>
|
||||||
<div style="display:flex; gap:4px; margin-top:6px">
|
<div style="display:flex; gap:4px; margin-top:6px">
|
||||||
@@ -252,7 +210,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="spin-btn" style="background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:7px; cursor:pointer; font-family:monospace; font-size:13px; width:100%">↻ Auto-spin</button>
|
<button id="spin-btn" style="background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:7px; cursor:pointer; font-family:monospace; font-size:13px; width:100%">↻ Auto-spin</button>
|
||||||
<button id="build-btn">▶ Build</button>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div id="status">Ready</div>
|
<div id="status">Ready</div>
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { mat4_mul, mat4_perspective, mat4_translate_z, quat_identity, quat_from_
|
|||||||
import { create_program, make_buffer } from './gl_utils.mjs';
|
import { create_program, make_buffer } from './gl_utils.mjs';
|
||||||
import { PAINT_BG, PAINT_BG_PBR, PAINT_BG_NOISE, Paint_State } from './paint_state.mjs';
|
import { PAINT_BG, PAINT_BG_PBR, PAINT_BG_NOISE, Paint_State } from './paint_state.mjs';
|
||||||
import { Spin_State } from './spin_state.mjs';
|
import { Spin_State } from './spin_state.mjs';
|
||||||
import { shape_distortion, build_triangle_geo, build_goldberg_geo } from './render_geo.mjs';
|
import { shape_distortion, build_goldberg_geo } from './render_geo.mjs';
|
||||||
import { build_goldberg_adjacency } from './topology.mjs';
|
import { build_goldberg_adjacency } from './topology.mjs';
|
||||||
import { Undo_State } from './undo_state.mjs';
|
import { Undo_State } from './undo_state.mjs';
|
||||||
import { Env_State } from './env_state.mjs';
|
import { Env_State } from './env_state.mjs';
|
||||||
import { PALETTES, DEFAULT_PALETTE } from './palettes.mjs';
|
import { DEFAULT_PALETTE } from './palettes.mjs';
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Load shaders
|
// Load shaders
|
||||||
@@ -32,7 +32,6 @@ const paint = new Paint_State();
|
|||||||
const undo_state = new Undo_State();
|
const undo_state = new Undo_State();
|
||||||
const env = new Env_State();
|
const env = new Env_State();
|
||||||
let current_geo = null;
|
let current_geo = null;
|
||||||
let current_stage = 'relaxed';
|
|
||||||
let current_palette = DEFAULT_PALETTE;
|
let current_palette = DEFAULT_PALETTE;
|
||||||
let building = false;
|
let building = false;
|
||||||
let active_brush_side = 'left';
|
let active_brush_side = 'left';
|
||||||
@@ -42,12 +41,7 @@ let paint_dirty = false;
|
|||||||
let debounce_timer = null;
|
let debounce_timer = null;
|
||||||
|
|
||||||
function get_params() {
|
function get_params() {
|
||||||
return {
|
return { depth: 5, iters: 500, alpha_edge: 0, alpha_centroid: 0.04 };
|
||||||
depth: parseInt(document.getElementById('depth').value, 10),
|
|
||||||
iters: parseInt(document.getElementById('iters').value, 10),
|
|
||||||
alpha_edge: parseFloat(document.getElementById('alpha-edge').value),
|
|
||||||
alpha_centroid: parseFloat(document.getElementById('alpha-centroid').value),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +100,7 @@ function upload_geo() {
|
|||||||
function rebuild_face_colors() {
|
function rebuild_face_colors() {
|
||||||
if (!cache.goldberg || !face_col_buf) { return; }
|
if (!cache.goldberg || !face_col_buf) { return; }
|
||||||
const goldberg = cache.goldberg;
|
const goldberg = cache.goldberg;
|
||||||
const use_relaxed = current_stage === 'relaxed';
|
const use_relaxed = true;
|
||||||
const fcol = [], fpbr = [], fnoise = [];
|
const fcol = [], fpbr = [], fnoise = [];
|
||||||
for (let fi = 0; fi < goldberg.faces.length; fi++) {
|
for (let fi = 0; fi < goldberg.faces.length; fi++) {
|
||||||
const face = goldberg.faces[fi];
|
const face = goldberg.faces[fi];
|
||||||
@@ -202,8 +196,6 @@ function apply_history(side) {
|
|||||||
async function build() {
|
async function build() {
|
||||||
if (building) { return; }
|
if (building) { return; }
|
||||||
building = true;
|
building = true;
|
||||||
const btn = document.getElementById('build-btn');
|
|
||||||
btn.disabled = true;
|
|
||||||
|
|
||||||
const p = get_params();
|
const p = get_params();
|
||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
@@ -217,14 +209,6 @@ async function build() {
|
|||||||
cache.poly = null;
|
cache.poly = null;
|
||||||
cache.goldberg = null;
|
cache.goldberg = null;
|
||||||
}
|
}
|
||||||
if (current_stage === 'ico') {
|
|
||||||
current_geo = build_triangle_geo(cache.ico, current_palette);
|
|
||||||
upload_geo();
|
|
||||||
cache.depth = p.depth;
|
|
||||||
set_stats(current_geo, 'Icosahedron', Date.now() - t0);
|
|
||||||
set_status('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth_changed || !cache.poly) {
|
if (depth_changed || !cache.poly) {
|
||||||
let poly = cache.ico;
|
let poly = cache.ico;
|
||||||
@@ -235,14 +219,6 @@ async function build() {
|
|||||||
cache.poly = poly;
|
cache.poly = poly;
|
||||||
cache.goldberg = null;
|
cache.goldberg = null;
|
||||||
}
|
}
|
||||||
if (current_stage === 'subdiv') {
|
|
||||||
current_geo = build_triangle_geo(cache.poly, current_palette);
|
|
||||||
upload_geo();
|
|
||||||
cache.depth = p.depth;
|
|
||||||
set_stats(current_geo, `Subdivided ×${p.depth}`, Date.now() - t0);
|
|
||||||
set_status('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth_changed || !cache.goldberg) {
|
if (depth_changed || !cache.goldberg) {
|
||||||
set_status('Building Goldberg dual…'); await yield_ui(30);
|
set_status('Building Goldberg dual…'); await yield_ui(30);
|
||||||
@@ -250,16 +226,8 @@ async function build() {
|
|||||||
cache.adj = null;
|
cache.adj = null;
|
||||||
undo_state.invalidate();
|
undo_state.invalidate();
|
||||||
}
|
}
|
||||||
if (current_stage === 'goldberg') {
|
|
||||||
current_geo = build_goldberg_geo(cache.goldberg, false, paint, current_palette);
|
|
||||||
upload_geo();
|
|
||||||
cache.depth = p.depth;
|
|
||||||
set_stats(current_geo, 'Goldberg (no relax)', Date.now() - t0);
|
|
||||||
set_status('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_status(`Relaxing (${p.iters} iters, edge=${p.alpha_edge}, centroid=${p.alpha_centroid})…`);
|
set_status(`Relaxing (${p.iters} iters)…`);
|
||||||
await yield_ui(50);
|
await yield_ui(50);
|
||||||
cache.goldberg.relax_sphere(p.iters, p.alpha_edge, p.alpha_centroid);
|
cache.goldberg.relax_sphere(p.iters, p.alpha_edge, p.alpha_centroid);
|
||||||
current_geo = build_goldberg_geo(cache.goldberg, true, paint, current_palette);
|
current_geo = build_goldberg_geo(cache.goldberg, true, paint, current_palette);
|
||||||
@@ -270,7 +238,6 @@ async function build() {
|
|||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
building = false;
|
building = false;
|
||||||
btn.disabled = false;
|
|
||||||
snapshot_now();
|
snapshot_now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,7 +269,7 @@ function pick_face(px, py) {
|
|||||||
const hx = ro[0]+t*rd[0], hy = ro[1]+t*rd[1], hz = ro[2]+t*rd[2];
|
const hx = ro[0]+t*rd[0], hy = ro[1]+t*rd[1], hz = ro[2]+t*rd[2];
|
||||||
|
|
||||||
const goldberg = cache.goldberg;
|
const goldberg = cache.goldberg;
|
||||||
const use_relaxed = current_stage === 'relaxed';
|
const use_relaxed = true;
|
||||||
let best_fi = -1, best_dot = -Infinity;
|
let best_fi = -1, best_dot = -Infinity;
|
||||||
for (let fi = 0; fi < goldberg.faces.length; fi++) {
|
for (let fi = 0; fi < goldberg.faces.length; fi++) {
|
||||||
const face = goldberg.faces[fi];
|
const face = goldberg.faces[fi];
|
||||||
@@ -409,7 +376,7 @@ function faces_at_radius(center_fi, radius) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function save_paint() {
|
function save_paint() {
|
||||||
const depth = parseInt(document.getElementById('depth').value);
|
const depth = 5;
|
||||||
const faces = [];
|
const faces = [];
|
||||||
for (const [fi, c] of paint.face_colors) {
|
for (const [fi, c] of paint.face_colors) {
|
||||||
const h = v => Math.round(v*255).toString(16).padStart(2, '0');
|
const h = v => Math.round(v*255).toString(16).padStart(2, '0');
|
||||||
@@ -432,10 +399,7 @@ function load_paint(file) {
|
|||||||
reader.onload = e => {
|
reader.onload = e => {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(e.target.result);
|
const data = JSON.parse(e.target.result);
|
||||||
const depth = parseInt(document.getElementById('depth').value);
|
if (data.depth !== 5) {
|
||||||
if (data.depth !== depth) {
|
|
||||||
document.getElementById('depth').value = data.depth;
|
|
||||||
document.getElementById('depth-val').textContent = data.depth;
|
|
||||||
cache.poly = null; cache.goldberg = null; cache.adj = null;
|
cache.poly = null; cache.goldberg = null; cache.adj = null;
|
||||||
}
|
}
|
||||||
paint.face_colors.clear();
|
paint.face_colors.clear();
|
||||||
@@ -730,50 +694,6 @@ function render_frame(ts) {
|
|||||||
// UI wiring
|
// UI wiring
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Palette buttons (built dynamically from PALETTES array).
|
|
||||||
{
|
|
||||||
const grid = document.getElementById('palette-grid');
|
|
||||||
for (const p of PALETTES) {
|
|
||||||
const btn = document.createElement('button');
|
|
||||||
btn.className = 'sb' + (p === DEFAULT_PALETTE ? ' active' : '');
|
|
||||||
btn.textContent = p.name;
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
current_palette = p;
|
|
||||||
grid.querySelectorAll('.sb').forEach(b => b.classList.remove('active'));
|
|
||||||
btn.classList.add('active');
|
|
||||||
rebuild_face_colors();
|
|
||||||
// Triangle stages need a full rebuild since positions are shared.
|
|
||||||
if (current_stage === 'ico' || current_stage === 'subdiv') { build(); }
|
|
||||||
});
|
|
||||||
grid.appendChild(btn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll('.sb').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
document.querySelectorAll('.sb').forEach(b => b.classList.remove('active'));
|
|
||||||
btn.classList.add('active');
|
|
||||||
current_stage = btn.dataset.stage;
|
|
||||||
build();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('depth').addEventListener('input', () => {
|
|
||||||
const d = parseInt(document.getElementById('depth').value);
|
|
||||||
document.getElementById('depth-val').textContent = d;
|
|
||||||
document.getElementById('iters').value = d * 8;
|
|
||||||
cache.poly = null;
|
|
||||||
cache.goldberg = null;
|
|
||||||
cache.adj = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('build-btn').addEventListener('click', () => {
|
|
||||||
if (current_stage === 'relaxed') {
|
|
||||||
cache.goldberg = null;
|
|
||||||
cache.adj = null;
|
|
||||||
}
|
|
||||||
build();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('spin-btn').addEventListener('click', () => {
|
document.getElementById('spin-btn').addEventListener('click', () => {
|
||||||
@@ -802,12 +722,8 @@ const update_paint_buttons = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const make_tool_handler = tool => () => {
|
const make_tool_handler = tool => () => {
|
||||||
if (!paint.enabled || paint.tool !== tool) {
|
paint.enabled = true;
|
||||||
paint.enabled = true;
|
paint.tool = tool;
|
||||||
paint.tool = tool;
|
|
||||||
} else {
|
|
||||||
paint.enabled = false;
|
|
||||||
}
|
|
||||||
update_paint_buttons();
|
update_paint_buttons();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user