A sample's file and window pair by its own index, not by sorting the file names as text
motion_100.bvh sorted before motion_11.bvh, so past 100 samples a clip was exported over another sample's window. The index is read from the file name and orders the samples and names the clips. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
16e585d0b9
commit
d35b9e2c31
2 changed files with 51 additions and 4 deletions
|
|
@ -1504,6 +1504,49 @@ test("constraints given twice are refused before anything is written or spawned"
|
|||
}
|
||||
});
|
||||
|
||||
test("past 100 samples each clip is exported over the window of its own sample", async () => {
|
||||
const daemon = await startDaemon();
|
||||
try {
|
||||
const answer = await generate(daemon, exportRequest({ clipName: "m", loop: true, numSamples: 101, exportTimeoutMs: 60000 }));
|
||||
assert.equal(answer.status, 200);
|
||||
const starts = exportStarts(daemon);
|
||||
assert.equal(answer.body.export.clips.length, 101);
|
||||
answer.body.export.clips.forEach((clip, i) => {
|
||||
const sample = Number(/motion_(\d+)\.fbx$/.exec(clip.file)[1]);
|
||||
const argv = starts[i].argv.slice(starts[i].argv.indexOf("--") + 1);
|
||||
assert.equal(argv[2], `m_${String(sample).padStart(2, "0")}`, `${clip.file} is named for its sample`);
|
||||
assert.deepEqual(argv.slice(3), ["--frames", String(4 + sample), String(224 + sample)], `${clip.file} gets its own sample's window`);
|
||||
});
|
||||
assert.ok(answer.body.export.clips.some((clip) => clip.file.endsWith("motion_100.fbx")));
|
||||
} finally {
|
||||
await daemon.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("constraints given twice are refused before anything is written or spawned", async () => {
|
||||
const daemon = await startDaemon();
|
||||
try {
|
||||
const inline = [{ type: "root2d" }];
|
||||
for (const bad of [
|
||||
{ constraints: inline, constraints_path: "/canary/a.json" },
|
||||
{ constraints: inline, constraintsPath: "/canary/a.json" },
|
||||
{ constraints: "/canary/a.json", constraints_path: "/canary/b.json" },
|
||||
{ constraints_path: "/canary/a.json", constraintsPath: "/canary/b.json" },
|
||||
]) {
|
||||
const answer = await generate(daemon, exportRequest(bad));
|
||||
assert.equal(answer.status, 400, JSON.stringify(bad));
|
||||
assert.deepEqual(answer.body, { ok: false, error: "Invalid constraints request." });
|
||||
assert.ok(!answer.raw.includes("canary"), "the refusal repeated the input");
|
||||
}
|
||||
assert.equal(existsSync(path.join(daemon.stateRoot, "artifacts")), false, "no artifact directory was made");
|
||||
assert.deepEqual(jobFiles(daemon), []);
|
||||
assert.deepEqual(daemon.spawns(), [], "no worker was started");
|
||||
assert.equal((await generate(daemon, exportRequest({ constraints_path: "/some/where.json" }))).status, 200, "one input is fine");
|
||||
} finally {
|
||||
await daemon.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("loop is a boolean and needs fbx, refused before anything is written or spawned", async () => {
|
||||
const daemon = await startDaemon();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -650,8 +650,8 @@ async function sweepWork() {
|
|||
|
||||
// Every BVH the worker listed becomes an FBX, or the job fails. A listed BVH must be a regular file inside the job's
|
||||
// artifact directory, and that is decided when its export begins (exportClip), never earlier. Sample N of several is
|
||||
// <clipName>_NN. With loops (the worker's answer, an array when the request asked for a loop, else undefined), sample k
|
||||
// is exported over loops[k], and a job whose answer has no usable window for a sample fails.
|
||||
// <clipName>_NN, N being its own index. With loops (the worker's answer, an array when the request asked for a loop,
|
||||
// else undefined), sample k is exported over loops[k], and a job whose answer has no usable window for a sample fails.
|
||||
async function exportListed(listed, artifactDir, clipName, timeoutMs, loops = undefined) {
|
||||
const bvhs = [];
|
||||
for (const file of Array.isArray(listed) ? listed : []) {
|
||||
|
|
@ -659,9 +659,13 @@ async function exportListed(listed, artifactDir, clipName, timeoutMs, loops = un
|
|||
bvhs.push(path.resolve(artifactDir, file));
|
||||
}
|
||||
if (bvhs.length === 0) return { clips: [], error: "fbx-export-failed (no bvh)" };
|
||||
bvhs.sort();
|
||||
// A sample's own index is in its file name (motion_NN.bvh, more digits past 99), so the order and the window come
|
||||
// from that number and never from sorting the names as text.
|
||||
const sampleOf = (bvh) => Number(/_(\d+)\.bvh$/.exec(bvh)?.[1] ?? 0);
|
||||
bvhs.sort((a, b) => sampleOf(a) - sampleOf(b) || (a < b ? -1 : a > b ? 1 : 0));
|
||||
const clips = [];
|
||||
for (const [index, bvh] of bvhs.entries()) {
|
||||
for (const bvh of bvhs) {
|
||||
const index = sampleOf(bvh);
|
||||
const name = bvhs.length === 1 ? clipName : `${clipName}_${String(index).padStart(2, "0")}`;
|
||||
const window = loops === undefined ? null : loops[index];
|
||||
if (window !== null && !(Number.isInteger(window?.first) && Number.isInteger(window?.last) && window.first >= 0 && window.last > window.first)) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue