← Commit history

adom-fields 0.8.2: Energy view turns to a low front view so the fields between the layers show; --camera front

John Lauer ·03ea2a69ac · ·parent 1e379b0
7 files changed +30−27
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-fields"-version = "0.8.1"+version = "0.8.2" edition = "2021" license = "MIT" description = "Adom Fields: where the current and the heat go on your board. Current density, copper heat flow and board temperature from the board file itself, in 3D, for the AI to drive and the human to explore."
docs/release-0.8.2.mdadded+4
@@ -0,0 +1,4 @@+# adom-fields 0.8.2++- **The Energy view shows where the energy actually runs.** On entering it, the camera turns slowly to a low front view, close to edge-on, so the energy streaming between the copper layers is visible instead of hidden under the top copper. It holds there, then eases back to the chosen view. A camera command or another view during the turn cancels the return.+- `ui view --camera front` gives the same low front view anytime, and `adom-fields tour` uses it on its Energy step.
package.json+1−1
@@ -1,7 +1,7 @@ {   "slug": "adom-fields",   "type": "app",-  "version": "0.8.1",+  "version": "0.8.2",   "title": "Adom Fields",   "brief": "Adom Fields: where the current and the heat go on your board.",   "summary": "Current density on the copper, heat flow through the pours and the vias, and the temperature over the board, solved from the board file itself and shown on the board in 3D. The AI does the analysis and drives the view; the human orbits, zooms, hovers for the number under the cursor, and reads the nets by what they carry and the issues by what to fix. A step in Adom's AI Flow: the walkthrough goes into the video.",
page.json+8−23
@@ -1,7 +1,7 @@ {   "slug": "adom-fields",   "type": "app",-  "version": "0.8.1",+  "version": "0.8.2",   "title": "Adom Fields",   "brief": "Adom Fields: where the current and the heat go on your board.",   "summary": "Current density on the copper, heat flow through the pours and the vias, and the temperature over the board, solved from the board file itself and shown on the board in 3D. The AI does the analysis and drives the view; the human orbits, zooms, hovers for the number under the cursor, and reads the nets by what they carry and the issues by what to fix. A step in Adom's AI Flow: the walkthrough goes into the video.",@@ -17,10 +17,7 @@     "vias",     "3d",     "analysis",-    "eda",-    "current flow animation",-    "voltage",-    "river"+    "eda"   ],   "status": "live",   "discovery_pitch": "Solve where the current and the heat go on a KiCad board and show it on the board in 3D: current density per net, heat flow per layer with the vias, temperature over the board, the nets by what they carry, the issues with the fix. One command to solve, one to serve, one to drive it on camera.",@@ -40,10 +37,11 @@     "fields",     "flow of current",     "river view",-    "voltage drop in 3d",-    "ground bounce",+    "energy flow on a board",+    "poynting vector",+    "speed of light on a pcb",+    "electron drift speed",     "exploded layer stack",-    "current through vias",     "explain the view out loud",     "narrate the fields",     "energy cycle",@@ -85,20 +83,7 @@     "anti-resonance",     "input filter stability",     "middlebrook",-    "ngspice pdn",-    "signals view",-    "copper capacitance",-    "switch node capacitance",-    "plane capacitance",-    "differential pair skew",-    "six layer board",-    "receiver frequency plan",-    "lo leakage",-    "tone in band",-    "rf crosstalk",-    "return path over supply plane",-    "antenna ground loop",-    "stage feed bypass"+    "ngspice pdn"   ],   "sample_prompts": [     {@@ -139,4 +124,4 @@     "name": "John Lauer",     "email": "[email protected]"   }-}\ No newline at end of file+}
src/main.rs+1−1
@@ -28,7 +28,7 @@ enum Cmd {     Parts { #[arg(long)] board: String, #[arg(long, default_value = "fields")] fields: PathBuf, #[arg(long = "models-dir")] models_dir: Vec<PathBuf> },     /// Serve the app for a solved board (or for a board and spec, solving first): the 3D board with the fields on it, the nets by relevance, the issues, and the live channel the AI drives     Serve { #[arg(long, default_value = "fields")] fields: PathBuf, #[arg(long, default_value_t = 8874)] port: u16, #[arg(long)] board: Option<String>, #[arg(long)] spec: Option<String> },-    /// Drive the running app: `ui view --quantity current|heat|temperature|signals|flow|river [--explode 0..1] [--speed 1] [--net X] [--layer F.Cu] [--camera top|iso|bottom|fit|zoom --at x,y] [--parts auto|ghost|solid|off]`, `ui toast "..."`, `ui select --net X`, `ui issue --index 0`+    /// Drive the running app: `ui view --quantity current|heat|temperature|signals|flow|river [--explode 0..1] [--speed 1] [--net X] [--layer F.Cu] [--camera top|iso|front|bottom|fit|zoom --at x,y] [--parts auto|ghost|solid|off]`, `ui toast "..."`, `ui select --net X`, `ui issue --index 0`     Ui { #[command(subcommand)] what: UiCmd, #[arg(long, default_value_t = 8874)] port: u16 },     /// A scripted walkthrough over the live channel: the nets by relevance, each field, the issues; for the AI's video step     Tour { #[arg(long, default_value_t = 8874)] port: u16, /// milliseconds to hold each beat while the board drifts (camera moves take up to 18 s since 0.5); gentle on purpose, the flow speeds the clip up ten times
src/serve.rs+1−1
@@ -234,7 +234,7 @@ pub fn tour(port: u16, hold_ms: u64) {         toast("The layers pulled apart: the current drops through the vias"); step("/ui/view", json!({"explode": 1}), settle); steps += 1;         toast("The river: every net at its voltage, the current running downhill"); step("/ui/view", json!({"quantity": "river", "explode": 0, "camera": "iso"}), settle); steps += 1;         toast("The loads fall to ground and the sources lift it back up"); step("/ui/view", json!({"explode": 1}), settle); steps += 1;-        toast("Energy: it travels in the fields between the layers, while the electrons crawl"); step("/ui/view", json!({"quantity": "energy", "explode": 0.6, "camera": "iso"}), settle); steps += 1;+        toast("Energy: it travels in the fields between the layers, while the electrons crawl"); step("/ui/view", json!({"quantity": "energy", "explode": 0.6, "camera": "front"}), settle * 2); steps += 1;         toast("Light: how fast a change crosses the board, and when it reaches each part"); step("/ui/view", json!({"quantity": "light", "explode": 0, "camera": "top"}), settle); steps += 1;         toast("The energy cycle: input caps, the inductor's magnetic field, the output, one switching period"); step("/ui/view", json!({"quantity": "cycle", "camera": "iso"}), settle); steps += 1;         toast("Charge: the board as capacitors"); step("/ui/view", json!({"quantity": "charge", "explode": 0.6, "camera": "iso"}), settle); steps += 1;
src/ui/app.js+14
@@ -350,10 +350,19 @@     const s = flipping ? 2.6 : 9.0;   // the turn-over is quick, everything else is slow     if (which === 'top') goto(NORTH, 0.02, R, home, s);     else if (which === 'bottom') goto(NORTH, Math.PI - 0.02, R, home, s);+    // low and from the front: the layers edge-on, so what runs between them (the Energy view's fields) is in sight+    else if (which === 'front') goto(NORTH, 1.32, sphere ? Rs : R * 1.08, sphere ? sphere.c : home, s);     else if (which === 'iso') { if (sphere && sphere.r > Math.hypot(board.widthMm, board.heightMm, board.thicknessMm) / 2 * 1.02) goto(NORTH - 0.55, 0.95, Rs, sphere.c, s); else goto(NORTH - 0.55, 0.95, R * 1.08, home, s); }     else if (sphere) goto(camera.alpha, camera.beta, Rs, sphere.c, s);     else { tweens = []; tweening = 0; V.goHome(); }   }+  const energy = { tok: 0 };+  function energyTour() {+    const tok = ++energy.tok;+    fitCamera('front');+    // the turn takes 9 s x SLOW; hold the front view about as long again, then return+    setTimeout(() => { if (tok === energy.tok && state.view.quantity === 'energy') fitCamera(state.view.camera === 'zoom' ? 'iso' : (state.view.camera || 'iso')); }, 9000 * SLOW + 12000);+  }   function zoomTo(x, y, fromBelow) {     // gentle: about half the board in frame, from near where the camera already is, over four seconds     const t = boardSpace(x, y, 0);@@ -514,6 +523,11 @@     if (state.view.quantity !== narr.view) { const had = narr.view !== null; narr.view = state.view.quantity; stopNarration(); const q = narr.view; setTimeout(() => { if (state.view.quantity === q) maybeExplain(q); }, had ? 900 : 2500); }     const changed = (v.changed || []);     const moveCamera = first || changed.includes('camera') || changed.includes('focus') || (changed.includes('explode') && state.view.camera !== 'zoom');+    // the Energy view's fields run between the copper layers, so from above the top copper hides them: turn to the front,+    // nearly edge-on, hold there, then settle back on the chosen view (John, 2026-10-19). A camera command or another view+    // during the tour cancels the return.+    if ((first || changed.includes('quantity')) && state.view.quantity === 'energy' && state.view.camera !== 'zoom' && !changed.includes('camera')) { energyTour(); return; }+    energy.tok++;     if (!moveCamera) return;     if (state.view.camera === 'zoom' && state.view.focus) { zoomTo(state.view.focus[0], state.view.focus[1], (state.view.layer || 'F.Cu') === 'B.Cu'); }     else if (state.view.camera) fitCamera(state.view.camera);