Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)

This commit is contained in:
WpyQwq
2026-09-19 13:04:56 +08:00
commit 3f422218a5
1098 changed files with 39410 additions and 0 deletions
+164
View File
@@ -0,0 +1,164 @@
<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8" /><title>先抬起后波浪检查</title>
<style>
body {
background: #eae5e1;
color: #303329;
font: 13px monospace;
}
#scene {
width: 960px;
height: 540px;
}
pre {
white-space: pre-wrap;
}
</style>
<div id="scene"></div>
<pre id="result">正在检查动画先后关系…</pre>
<script type="module">
import { ArchiveScene } from "/src/scene.ts";
import { selectionWave } from "/src/motion.ts";
import { columnFiles } from "/src/data.ts";
import { wrap, sameCell } from "/src/archive-loop.ts";
const s = new ArchiveScene(document.querySelector("#scene"), selectionWave, true);
await s.load();
const draw = s.composer.render.bind(s.composer);
s.composer.render = () => {};
let time = 0,
index = 0,
latestPulse = -1;
let trackedHeights = null;
const errors = [],
emissions = [];
const expect = (v, m) => {
if (!v) errors.push(m);
};
const step = (count = 1) => {
for (let f = 0; f < count; f++) {
s.update((time += 1 / 60));
trackedHeights?.push(s.model.position.y);
const pulse = s.pulses.at(-1);
if (pulse && pulse.time !== latestPulse) {
latestPulse = pulse.time;
const old = s.outgoing.filter(
(o) =>
o.cell.lane === s.selectedCell.lane &&
Math.abs(o.cell.row - s.selectedCell.row) <= 4,
);
const margin = old.length
? Math.min(
...old.map((o) => s.model.position.y - o.group.position.y),
)
: null;
emissions.push({
time,
lift: s.lift.value,
margin,
cell: { ...s.selectedCell },
});
expect(s.lift.value >= 0.35, "Wave starts before the new file rises");
expect(
margin === null || margin > 0.015 - 1e-8,
"Wave starts while the previous file is higher",
);
expect(
sameCell(pulse, s.selectedCell),
"An obsolete selection emits a delayed wave",
);
}
}
};
function move(direction) {
const files = columnFiles(s.selectedCell.lane % 5);
index = files[wrap(files.indexOf(index) + direction, files.length)];
s.select(index, { axis: "row", direction });
}
s.setMode("archive");
s.select(0);
step(300);
const initial = emissions.length;
const start = time;
move(1);
trackedHeights = [];
step(12);
expect(
emissions.length === initial,
"Wave starts simultaneously with lift",
);
step(120);
expect(
emissions.length === initial + 1,
"Selection fails to emit one wave",
);
const firstDelay = emissions.at(-1).time - start;
let peak = -Infinity,
maxDrop = 0;
for (const height of trackedHeights) {
peak = Math.max(peak, height);
maxDrop = Math.max(maxDrop, peak - height);
}
trackedHeights = null;
expect(
maxDrop < 0.005,
"The selected file jumps and falls after its initial rise",
);
const rapidStart = emissions.length;
for (let i = 0; i < 12; i++) {
move(1);
step(6);
}
expect(
emissions.length === rapidStart,
"Abandoned rapid selections emitted queued waves",
);
step(180);
expect(
emissions.length === rapidStart + 1,
"Last rapid selection did not emit exactly one wave",
);
const oldCell = { ...s.selectedCell },
oldIndex = index;
move(1);
step(6);
s.select(oldIndex, { cell: oldCell });
index = oldIndex;
step(180);
expect(s.pendingPulse === null, "Rapid reversal never settled");
s.setMode("detail");
step(300);
expect(s.canInspect, "Detail inspection lost clearance");
s.rotation = s.targetRotation = 0.8;
step();
s.setMode("archive");
move(1);
const rotatedStart = emissions.length;
step(20);
expect(
emissions.length === rotatedStart,
"Wave starts before rotated old card aligns",
);
step(300);
expect(
emissions.length === rotatedStart + 1,
"Wave missing after detail return",
);
s.setReduced(true);
move(1);
const reducedStart = emissions.length;
step(180);
expect(emissions.length === reducedStart, "Reduced motion emits a wave");
s.setReduced(false);
move(1);
s.setMode("hidden");
step(120);
expect(s.pendingPulse === null, "Replay leaves a pending wave");
document.querySelector("#result").textContent = JSON.stringify(
{ passed: !errors.length, errors, firstDelay, maxDrop, emissions },
null,
2,
);
draw();
</script>
</html>