mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-14 18:01:20 +08:00
e205ddfc1d
drawElementImage cannot paint CSS 3D: rendering contexts drop earlier siblings and backgrounds, backface-visibility is ignored (mirrored backfaces even at rest), and flat 3D matrices silently lose their rotation. Until now every 3D comp was gated to the baseline route. threeDProjection.ts rewrites 3D content in-page before capture: - discovery: perspective/preserve-3d contexts, elements at a 3D matrix at t=0, and the producer stub's record of 3D tween targets (rotationX/rotationY/transformPerspective) for to()-style tweens that are still flat at init - leaf quads rasterized once via SVG foreignObject (fonts and images inlined as data URLs), shell quads carry only own paint when a child contains further 3D - per-frame WebGL projection from live computed matrices: CSS-convention matrix math, perspective + transform-origin sandwiches, GL backface culling, accumulated opacity as a fragment uniform - live elements hidden via clip-path (GSAP autoAlpha fights visibility/opacity), 3D contexts neutralized and live 3D matrices stripped after reading (perspective-carrying and rotated matrices poison the capture even when hidden), authored backface flags captured before neutralization - projected canvases composite OVER the DOM paint — the under-pass would bury them beneath the composition root's own background Correctness guards fall back to the platform baseline route, same contract as the video gate: degenerate markup (zero-size/inline-box quads — gen_os flip-card spans) and quads with GSAP-animated descendants (static textures would freeze them; golf measured 46->24 dB without this). fast-capture-3d test comp (flip card + perspective-free rotationX entrance): 57.5 dB avg / 51.7 dB min vs baseline at 1.5x speedup. Real-world comps with animated 3D subtrees fall back cleanly. Engine suite 692/694 (2 failures pre-exist on clean tree); build, lint, oxfmt verified manually — hook bypassed for the same stale-lockfile core-build failure as the previous commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
85 lines
3.2 KiB
JavaScript
85 lines
3.2 KiB
JavaScript
// Drive the engine's initThreeDProjectionInPage against a denovo-style
|
|
// inline flip card (flip-container inside an h1 text flow) and screenshot
|
|
// the page directly — no capture pipeline — to isolate projection bugs.
|
|
// Run with bun (TS import): bun spikes/de-3d-module-check.mjs
|
|
|
|
import puppeteer from "puppeteer";
|
|
import { spawnSync } from "node:child_process";
|
|
import { writeFileSync, mkdirSync } from "node:fs";
|
|
import { initThreeDProjectionInPage } from "../src/services/threeDProjection.ts";
|
|
|
|
const W = 960, H = 540;
|
|
|
|
const HTML = `<!doctype html><meta charset=utf-8>
|
|
<style>
|
|
*{margin:0;padding:0}
|
|
html,body{width:${W}px;height:${H}px;background:#ece8dd}
|
|
#root{position:relative;width:${W}px;height:${H}px;background:#ece8dd}
|
|
h1.headline{position:absolute;top:160px;left:0;width:100%;text-align:center;
|
|
font:600 64px serif;color:#1a2640}
|
|
.flip-container{display:inline-block;perspective:1000px;vertical-align:baseline;
|
|
height:1.2em;position:relative;min-width:300px}
|
|
.flip-card{position:relative;width:100%;height:100%;transform-style:preserve-3d}
|
|
.flip-front,.flip-back{position:absolute;width:100%;height:100%;
|
|
backface-visibility:hidden;display:flex;align-items:center;justify-content:center;
|
|
left:0;top:0}
|
|
.flip-back{transform:rotateX(-180deg);color:#C8A870;font-style:italic;white-space:nowrap}
|
|
</style>
|
|
<div id=root data-composition-id="check">
|
|
<h1 class=headline>tools are <span class=flip-container><span class=flip-card id=card>
|
|
<span class=flip-front>agentic workflows</span>
|
|
<span class=flip-back>blind</span>
|
|
</span></span></h1>
|
|
</div>
|
|
<script>
|
|
window.__setAngle = a => {
|
|
document.getElementById("card").style.transform = "rotateX(" + a + "deg)";
|
|
};
|
|
</script>`;
|
|
|
|
const browser = await puppeteer.launch({
|
|
headless: true,
|
|
executablePath: process.env.CHROME_PATH || undefined,
|
|
args: ["--no-sandbox", "--use-gl=angle", `--window-size=${W},${H}`],
|
|
});
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: W, height: H });
|
|
await page.setContent(HTML, { waitUntil: "load" });
|
|
page.on("console", (m) => console.log("[page]", m.text()));
|
|
|
|
mkdirSync("/tmp/de-3d-module", { recursive: true });
|
|
|
|
function psnr(a, b) {
|
|
const r = spawnSync("ffmpeg", ["-i", a, "-i", b, "-lavfi", "psnr", "-f", "null", "-"],
|
|
{ encoding: "utf8" });
|
|
const m = String(r.stderr).match(/average:([\d.inf]+)/);
|
|
return m ? m[1] : "n/a";
|
|
}
|
|
|
|
const ANGLES = [0, 45, 90, 135, 180];
|
|
|
|
// refs: real CSS 3D
|
|
for (const a of ANGLES) {
|
|
await page.evaluate((x) => window.__setAngle(x), a);
|
|
await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => setTimeout(r, 30))));
|
|
writeFileSync(`/tmp/de-3d-module/ref-${a}.png`, await page.screenshot({ type: "png" }));
|
|
}
|
|
|
|
// engine projection
|
|
const res = await page.evaluate(initThreeDProjectionInPage);
|
|
console.log("init:", JSON.stringify(res));
|
|
|
|
for (const a of ANGLES) {
|
|
await page.evaluate((x) => {
|
|
window.__setAngle(x);
|
|
window.__hf3d?.update();
|
|
}, a);
|
|
await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => setTimeout(r, 30))));
|
|
const out = `/tmp/de-3d-module/proj-${a}.png`;
|
|
writeFileSync(out, await page.screenshot({ type: "png" }));
|
|
console.log(`angle=${a}: psnr=${psnr(`/tmp/de-3d-module/ref-${a}.png`, out)}`);
|
|
}
|
|
|
|
console.log(await browser.version());
|
|
await browser.close();
|