340 lines
24 KiB
JavaScript
340 lines
24 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import factory from "../../web/app/src/vendor/blender/web_engine.js";
|
|
|
|
const root = new URL("../../", import.meta.url);
|
|
const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root));
|
|
const fixture = fs.readFileSync(new URL("tests/files/web/nonmesh_scene.blend", root));
|
|
|
|
function output(engine, handle, fn, owned = false) {
|
|
const dataOut = engine._malloc(4);
|
|
const lengthOut = engine._malloc(4);
|
|
try {
|
|
assert.equal(fn(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
const pointer = engine.HEAPU32[dataOut >>> 2];
|
|
const length = engine.HEAPU32[lengthOut >>> 2];
|
|
const bytes = engine.HEAPU8.slice(pointer, pointer + length);
|
|
if (owned) engine._web_engine_free_buffer(pointer);
|
|
return bytes;
|
|
}
|
|
finally {
|
|
engine._free(dataOut);
|
|
engine._free(lengthOut);
|
|
}
|
|
}
|
|
|
|
function open(engine, handle, bytes) {
|
|
const pointer = engine._malloc(bytes.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(bytes, pointer);
|
|
assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0,
|
|
engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
}
|
|
finally { engine._free(pointer); }
|
|
}
|
|
|
|
function command(engine, handle, payload) {
|
|
const bytes = new TextEncoder().encode(JSON.stringify(payload));
|
|
const pointer = engine._malloc(bytes.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(bytes, pointer);
|
|
assert.equal(engine._web_engine_apply_command(handle, pointer, bytes.byteLength), 0,
|
|
`${payload.type}: ${engine.UTF8ToString(engine._web_engine_last_error_message())}`);
|
|
}
|
|
finally { engine._free(pointer); }
|
|
}
|
|
|
|
function reject(engine, handle, payload, code) {
|
|
const bytes = new TextEncoder().encode(JSON.stringify(payload));
|
|
const pointer = engine._malloc(bytes.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(bytes, pointer);
|
|
assert.notEqual(engine._web_engine_apply_command(handle, pointer, bytes.byteLength), 0, `${payload.type} unexpectedly succeeded`);
|
|
assert.match(engine.UTF8ToString(engine._web_engine_last_error_message()), new RegExp(code));
|
|
}
|
|
finally { engine._free(pointer); }
|
|
}
|
|
|
|
function snapshot(engine, handle) {
|
|
return JSON.parse(new TextDecoder().decode(output(engine, handle, engine._web_engine_get_scene_snapshot)));
|
|
}
|
|
|
|
function save(engine, handle) {
|
|
return output(engine, handle, engine._web_engine_save_blend, true);
|
|
}
|
|
|
|
function depsgraph(engine, handle) {
|
|
return JSON.parse(new TextDecoder().decode(output(engine, handle, engine._web_engine_evaluate_depsgraph)));
|
|
}
|
|
|
|
function close(actual, expected, label) {
|
|
assert.ok(Math.abs(actual - expected) < 1e-5, `${label}: ${actual} != ${expected}`);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary: wasmBinary.slice() });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fixture);
|
|
const before = snapshot(engine, handle);
|
|
const curve = before.nonMeshData.find((data) => data.type === "CURVE");
|
|
const surface = before.nonMeshData.find((data) => data.type === "SURFACE");
|
|
const font = before.nonMeshData.find((data) => data.type === "FONT");
|
|
const metaball = before.nonMeshData.find((data) => data.type === "METABALL");
|
|
const volume = before.nonMeshData.find((data) => data.type === "VOLUME");
|
|
assert.ok(curve?.controlPoints?.length && curve.splineOffsets?.length);
|
|
assert.ok(surface?.controlPoints?.length && surface.splineOffsets?.length);
|
|
assert.deepEqual(surface.splineDimensions, [{ u: 4, v: 4, orderU: 4, orderV: 4 }]);
|
|
assert.equal(surface.pointWeights?.length, 16);
|
|
assert.ok(font?.text && metaball?.elements?.length);
|
|
assert.ok(volume?.sourcePath && volume?.volumeProperties);
|
|
assert.equal(before.vfonts?.length, 2);
|
|
assert.ok(before.vfonts.every((resource) => resource.packed && resource.id.startsWith("vfont:")));
|
|
assert.ok(font.fontLinks);
|
|
assert.deepEqual(curve.splineTypes, ["POLY", "BEZIER"]);
|
|
assert.equal(curve.cyclicU?.length, curve.splineCount);
|
|
assert.equal(curve.handleTypes?.length, 6);
|
|
assert.equal(curve.handlePoints?.length, 18);
|
|
assert.deepEqual(curve.handlePointIndices, [4, 5, 6]);
|
|
|
|
const volumeProperties = {
|
|
displayDensity: 1.75,
|
|
interpolation: "NEAREST",
|
|
stepSize: 0.125,
|
|
velocityGrid: "velocity",
|
|
velocityScale: 1.5,
|
|
};
|
|
const volumeSourcePath = "//assets/volumes/generated-smoke.vdb";
|
|
reject(engine, handle, { type: "setVolumeProperties", dataId: volume.id, ...volumeProperties, sourcePath: "../outside.vdb" }, "NON_MESH_PROPERTY_INVALID");
|
|
command(engine, handle, { type: "setVolumeProperties", dataId: volume.id, ...volumeProperties, sourcePath: volumeSourcePath });
|
|
let changedVolume = snapshot(engine, handle).nonMeshData.find((data) => data.id === volume.id);
|
|
assert.equal(changedVolume.sourcePath, volumeSourcePath);
|
|
assert.deepEqual(changedVolume.volumeProperties, volumeProperties);
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
changedVolume = snapshot(engine, handle).nonMeshData.find((data) => data.id === volume.id);
|
|
assert.equal(changedVolume.sourcePath, volume.sourcePath);
|
|
assert.deepEqual(changedVolume.volumeProperties, volume.volumeProperties);
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
changedVolume = snapshot(engine, handle).nonMeshData.find((data) => data.id === volume.id);
|
|
assert.deepEqual(changedVolume.volumeProperties, volumeProperties);
|
|
|
|
command(engine, handle, { type: "renameId", id: curve.id, name: "WebCurveRenamed" });
|
|
const renamedCurve = snapshot(engine, handle).nonMeshData.find((data) => data.type === "CURVE" && data.name === "WebCurveRenamed");
|
|
assert.equal(renamedCurve?.id, "curve:WebCurveRenamed");
|
|
const curveId = renamedCurve.id;
|
|
|
|
command(engine, handle, { type: "createCurve", curveType: "CURVE", name: "CreatedWebCurve", controlPoints: [-1, 0, 0, 0, 1, 0, 1, 0, 0], cyclic: true, resolution: 8 });
|
|
const createdCurve = snapshot(engine, handle).nonMeshData.find((data) => data.name === "CreatedWebCurve");
|
|
assert.equal(createdCurve?.type, "CURVE");
|
|
assert.equal(createdCurve?.controlPoints?.length, 9);
|
|
assert.deepEqual(createdCurve?.cyclicU, [true]);
|
|
command(engine, handle, { type: "setCurveTopology", dataId: createdCurve.id, splineTypes: ["NURBS"], cyclicU: [true], cyclicV: [false] });
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id)?.splineTypes, ["NURBS"]);
|
|
command(engine, handle, { type: "setCurveTopology", dataId: createdCurve.id, splineTypes: ["POLY"], cyclicU: [true], cyclicV: [false] });
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id)?.splineTypes, ["POLY"]);
|
|
command(engine, handle, { type: "setCurveTopology", dataId: createdCurve.id, splineTypes: ["BEZIER"], cyclicU: [true], cyclicV: [false] });
|
|
const bezierCreatedCurve = snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id);
|
|
assert.deepEqual(bezierCreatedCurve?.splineTypes, ["BEZIER"]);
|
|
assert.equal(bezierCreatedCurve?.handleTypes?.length, 6);
|
|
command(engine, handle, { type: "setCurveTopology", dataId: createdCurve.id, splineTypes: ["NURBS"], cyclicU: [true], cyclicV: [false] });
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id)?.splineTypes, ["NURBS"]);
|
|
|
|
const threeSplineTypes = ["POLY", "BEZIER", "NURBS"];
|
|
const threeSplineOffsets = [0, 3, 6, 10];
|
|
const threeSplinePoints = [
|
|
-2, 0, 0, -1.5, 0.5, 0, -1, 0, 0,
|
|
-0.5, 0, 0, 0, 0.75, 0, 0.5, 0, 0,
|
|
1, 0, 0, 1.5, 0.5, 0, 2, 0.5, 0, 2.5, 0, 0,
|
|
];
|
|
const threeSplineWeights = Array(10).fill(1);
|
|
threeSplineWeights[8] = 0.8;
|
|
const threeSplineHandles = [
|
|
-0.75, 0, 0, -0.25, 0, 0,
|
|
-0.25, 0.75, 0, 0.25, 0.75, 0,
|
|
0.25, 0, 0, 0.75, 0, 0,
|
|
];
|
|
const threeSplineCommand = {
|
|
type: "setCurveSplines", dataId: createdCurve.id, splineTypes: threeSplineTypes,
|
|
splineOffsets: threeSplineOffsets, ordersU: [0, 0, 4], controlPoints: threeSplinePoints,
|
|
pointWeights: threeSplineWeights, cyclicU: [false, true, false],
|
|
handleTypes: [0, 0, 1, 1, 2, 2], handlePoints: threeSplineHandles,
|
|
};
|
|
command(engine, handle, threeSplineCommand);
|
|
let transactedCurve = snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id);
|
|
assert.deepEqual(transactedCurve.splineTypes, threeSplineTypes);
|
|
assert.deepEqual(transactedCurve.splineOffsets, threeSplineOffsets);
|
|
assert.deepEqual(transactedCurve.cyclicU, [false, true, false]);
|
|
assert.equal(transactedCurve.pointCount, 10);
|
|
assert.equal(transactedCurve.handlePoints.length, 18);
|
|
close(transactedCurve.pointWeights[8], 0.8, "multi-spline NURBS weight");
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id).splineTypes, ["NURBS"]);
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id).splineTypes, threeSplineTypes);
|
|
|
|
const invalidSplineCommand = new TextEncoder().encode(JSON.stringify({ ...threeSplineCommand, splineOffsets: [1, 4, 7, 11] }));
|
|
const invalidSplinePointer = engine._malloc(invalidSplineCommand.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(invalidSplineCommand, invalidSplinePointer);
|
|
assert.notEqual(engine._web_engine_apply_command(handle, invalidSplinePointer, invalidSplineCommand.byteLength), 0);
|
|
assert.match(engine.UTF8ToString(engine._web_engine_last_error_message()), /NON_MESH_PROPERTY_INVALID/);
|
|
}
|
|
finally { engine._free(invalidSplinePointer); }
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id).splineOffsets, threeSplineOffsets);
|
|
|
|
const twoSplineHandlePoints = threeSplineHandles.slice();
|
|
twoSplineHandlePoints[1] -= 0.2;
|
|
command(engine, handle, {
|
|
type: "setCurveSplines", dataId: createdCurve.id, splineTypes: ["BEZIER", "NURBS"],
|
|
splineOffsets: [0, 3, 7], ordersU: [0, 4], controlPoints: threeSplinePoints.slice(9),
|
|
pointWeights: threeSplineWeights.slice(3), cyclicU: [false, true],
|
|
handleTypes: [0, 0, 0, 0, 0, 0], handlePoints: twoSplineHandlePoints,
|
|
});
|
|
transactedCurve = snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id);
|
|
assert.deepEqual(transactedCurve.splineTypes, ["BEZIER", "NURBS"]);
|
|
assert.deepEqual(transactedCurve.splineOffsets, [0, 3, 7]);
|
|
assert.deepEqual(transactedCurve.cyclicU, [false, true]);
|
|
close(transactedCurve.handlePoints[1], twoSplineHandlePoints[1], "bulk handle transaction");
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id).splineTypes, threeSplineTypes);
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === createdCurve.id).splineTypes, ["BEZIER", "NURBS"]);
|
|
|
|
const invalidSurfaceCommand = new TextEncoder().encode(JSON.stringify({ type: "setCurveTopology", dataId: surface.id, splineTypes: ["POLY"], cyclicU: [false], cyclicV: [false] }));
|
|
const invalidSurfacePointer = engine._malloc(invalidSurfaceCommand.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(invalidSurfaceCommand, invalidSurfacePointer);
|
|
assert.notEqual(engine._web_engine_apply_command(handle, invalidSurfacePointer, invalidSurfaceCommand.byteLength), 0);
|
|
assert.match(engine.UTF8ToString(engine._web_engine_last_error_message()), /NON_MESH_TOPOLOGY_EDIT_UNSUPPORTED/);
|
|
}
|
|
finally { engine._free(invalidSurfacePointer); }
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === surface.id)?.splineTypes, ["NURBS"]);
|
|
|
|
const curvePoints = curve.controlPoints.slice();
|
|
curvePoints[0] += 0.25;
|
|
command(engine, handle, { type: "setCurveControlPoints", dataId: curveId, controlPoints: curvePoints, splineOffsets: curve.splineOffsets, resolution: (curve.resolution ?? 12) + 1 });
|
|
const surfaceDimensions = [{ u: 5, v: 4, orderU: 4, orderV: 4 }];
|
|
const surfacePoints = [];
|
|
const surfaceWeights = [];
|
|
for (let v = 0; v < 4; v++) for (let u = 0; u < 5; u++) {
|
|
surfacePoints.push(-1 + u * 0.5, v / 3, u === 2 && (v === 1 || v === 2) ? 0.45 : 0);
|
|
surfaceWeights.push(u === 2 ? 0.85 : 1);
|
|
}
|
|
command(engine, handle, { type: "setSurfaceTopology", dataId: surface.id, splineDimensions: surfaceDimensions, controlPoints: surfacePoints, pointWeights: surfaceWeights, cyclicU: [false], cyclicV: [false] });
|
|
let changedSurface = snapshot(engine, handle).nonMeshData.find((data) => data.id === surface.id);
|
|
assert.deepEqual(changedSurface.splineDimensions, surfaceDimensions);
|
|
assert.equal(changedSurface.pointCount, 20);
|
|
close(changedSurface.pointWeights[2], 0.85, "surface rational weight");
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === surface.id).splineDimensions, surface.splineDimensions);
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
changedSurface = snapshot(engine, handle).nonMeshData.find((data) => data.id === surface.id);
|
|
assert.deepEqual(changedSurface.splineDimensions, surfaceDimensions);
|
|
const curveCyclic = curve.cyclicU.map((value, index) => index === 0 ? !value : value);
|
|
const curveHandlePoints = curve.handlePoints.slice();
|
|
curveHandlePoints[0] += 0.25;
|
|
curveHandlePoints[15] -= 0.2;
|
|
reject(engine, handle, { type: "setCurveTopology", baseRevision: snapshot(engine, handle).revision - 1, dataId: curveId, splineTypes: curve.splineTypes, cyclicU: curveCyclic, cyclicV: curve.cyclicV, handleTypes: curve.handleTypes, handlePoints: curveHandlePoints }, "REVISION_CONFLICT");
|
|
command(engine, handle, { type: "setCurveTopology", baseRevision: snapshot(engine, handle).revision, dataId: curveId, splineTypes: curve.splineTypes, cyclicU: curveCyclic, cyclicV: curve.cyclicV, handleTypes: curve.handleTypes, handlePoints: curveHandlePoints });
|
|
const preciseHandlePosition = curveHandlePoints.slice(12, 15);
|
|
preciseHandlePosition[2] += 0.125;
|
|
command(engine, handle, { type: "setCurveHandle", dataId: curveId, pointIndex: curve.handlePointIndices[2], side: "LEFT", position: preciseHandlePosition });
|
|
close(snapshot(engine, handle).nonMeshData.find((data) => data.id === curveId).handlePoints[14], preciseHandlePosition[2], "precise handle command");
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
close(snapshot(engine, handle).nonMeshData.find((data) => data.id === curveId).handlePoints[14], curveHandlePoints[14], "precise handle undo");
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
close(snapshot(engine, handle).nonMeshData.find((data) => data.id === curveId).handlePoints[14], preciseHandlePosition[2], "precise handle redo");
|
|
curveHandlePoints.splice(12, 3, ...preciseHandlePosition);
|
|
const metaballElements = metaball.elements.map((element, index) => ({ ...element, radius: index === 0 ? element.radius + 0.1 : element.radius }));
|
|
command(engine, handle, { type: "setMetaballElements", dataId: metaball.id, elements: metaballElements });
|
|
command(engine, handle, { type: "setFontBody", dataId: font.id, body: "N-015 roundtrip" });
|
|
command(engine, handle, { type: "setFontProperties", dataId: font.id, properties: { alignment: "RIGHT", alignY: "CENTER", extrude: 0.08, bevelDepth: 0.015, bevelResolution: 3, offset: 0.01, spacing: 1.2, lineDistance: 1.4, wordSpace: 1.1, shear: 0.2, fontSize: 1.25, offsetX: 0.15, offsetY: -0.1 } });
|
|
const fontLinks = { regular: font.fontLinks.bold, bold: font.fontLinks.regular, italic: font.fontLinks.boldItalic, boldItalic: font.fontLinks.italic };
|
|
command(engine, handle, { type: "setFontLinks", dataId: font.id, links: fontLinks });
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === font.id).fontLinks, fontLinks);
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === font.id).fontLinks, font.fontLinks);
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
assert.deepEqual(snapshot(engine, handle).nonMeshData.find((data) => data.id === font.id).fontLinks, fontLinks);
|
|
const fontCharacters = Array.from("N-015 roundtrip", (_, index) => ({ kern: index === 1 ? 0.25 : 0, materialIndex: index % 2, styleFlags: index === 0 ? 0x3 : index === 2 ? 0x14 : 0 }));
|
|
const fontTextBoxes = [
|
|
{ x: 0, y: 0, width: 3.5, height: 1.25 },
|
|
{ x: 4, y: -0.5, width: 2.25, height: 1.5 },
|
|
];
|
|
command(engine, handle, { type: "setFontAdvanced", dataId: font.id, characters: fontCharacters, textBoxes: fontTextBoxes, activeTextBox: 1 });
|
|
|
|
const changed = snapshot(engine, handle);
|
|
close(changed.nonMeshData.find((data) => data.id === curveId).controlPoints[0], curvePoints[0], "curve point");
|
|
assert.deepEqual(changed.nonMeshData.find((data) => data.id === surface.id).splineDimensions, surfaceDimensions);
|
|
close(changed.nonMeshData.find((data) => data.id === surface.id).controlPoints[8], surfacePoints[8], "surface topology point");
|
|
close(changed.nonMeshData.find((data) => data.id === metaball.id).elements[0].radius, metaballElements[0].radius, "metaball radius");
|
|
assert.equal(changed.nonMeshData.find((data) => data.id === font.id).text, "N-015 roundtrip");
|
|
assert.deepEqual(changed.nonMeshData.find((data) => data.id === curveId).cyclicU, curveCyclic);
|
|
close(changed.nonMeshData.find((data) => data.id === curveId).handlePoints[0], curveHandlePoints[0], "curve left handle");
|
|
close(changed.nonMeshData.find((data) => data.id === curveId).handlePoints[15], curveHandlePoints[15], "curve right handle");
|
|
assert.equal(changed.nonMeshData.find((data) => data.id === curveId).handleTypes[4], 0);
|
|
assert.equal(changed.nonMeshData.find((data) => data.id === font.id).fontProperties.alignment, "RIGHT");
|
|
assert.equal(changed.nonMeshData.find((data) => data.id === font.id).fontProperties.bevelResolution, 3);
|
|
assert.deepEqual(changed.nonMeshData.find((data) => data.id === font.id).fontCharacters, fontCharacters);
|
|
assert.deepEqual(changed.nonMeshData.find((data) => data.id === font.id).fontTextBoxes, fontTextBoxes);
|
|
assert.equal(changed.nonMeshData.find((data) => data.id === font.id).activeFontTextBox, 1);
|
|
assert.deepEqual(changed.nonMeshData.find((data) => data.id === font.id).fontLinks, fontLinks);
|
|
close(changed.nonMeshData.find((data) => data.id === font.id).fontProperties.spacing, 1.2, "font spacing");
|
|
close(changed.nonMeshData.find((data) => data.id === font.id).fontProperties.fontSize, 1.25, "font size");
|
|
assert.equal(engine._web_engine_undo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
const undoneProperties = snapshot(engine, handle).nonMeshData.find((data) => data.id === font.id);
|
|
assert.equal(undoneProperties.text, "N-015 roundtrip");
|
|
assert.equal(undoneProperties.fontProperties.alignment, "RIGHT");
|
|
assert.notDeepEqual(undoneProperties.fontTextBoxes, fontTextBoxes);
|
|
assert.equal(engine._web_engine_redo(handle), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
const redoneProperties = snapshot(engine, handle).nonMeshData.find((data) => data.id === font.id);
|
|
assert.equal(redoneProperties.text, "N-015 roundtrip");
|
|
assert.equal(redoneProperties.fontProperties.alignment, "RIGHT");
|
|
assert.deepEqual(redoneProperties.fontCharacters, fontCharacters);
|
|
assert.deepEqual(redoneProperties.fontTextBoxes, fontTextBoxes);
|
|
|
|
const evaluated = depsgraph(engine, handle);
|
|
const evaluatedTypes = new Set((evaluated.nonMeshGeometries ?? []).filter((geometry) => geometry.status === "EVALUATED").map((geometry) => geometry.sourceType));
|
|
for (const type of ["CURVE", "SURFACE", "FONT", "METABALL"]) assert.ok(evaluatedTypes.has(type), `missing evaluated ${type}`);
|
|
const evaluatedSurface = evaluated.nonMeshGeometries.find((geometry) => geometry.sourceDataId === surface.id && geometry.status === "EVALUATED");
|
|
assert.ok(evaluatedSurface?.vertexCount > 256 && evaluatedSurface?.triangleCount > 450, "5x4 Surface topology did not change evaluated geometry");
|
|
for (const geometry of evaluated.nonMeshGeometries ?? []) if (geometry.status === "EVALUATED") {
|
|
assert.ok(geometry.vertexCount > 0, `${geometry.sourceType} has no evaluated vertices`);
|
|
assert.equal(geometry.positions.length, geometry.vertexCount * 3);
|
|
assert.equal(geometry.indices.length, geometry.triangleCount * 3);
|
|
}
|
|
|
|
const saved = save(engine, handle);
|
|
engine._web_engine_destroy(handle);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const roundtripped = snapshot(engine, reopened);
|
|
const reopenedVolume = roundtripped.nonMeshData.find((data) => data.id === volume.id);
|
|
assert.equal(reopenedVolume.sourcePath, volumeSourcePath);
|
|
assert.deepEqual(reopenedVolume.volumeProperties, volumeProperties);
|
|
close(roundtripped.nonMeshData.find((data) => data.id === curveId).controlPoints[0], curvePoints[0], "reopened curve point");
|
|
assert.deepEqual(roundtripped.nonMeshData.find((data) => data.id === surface.id).splineDimensions, surfaceDimensions);
|
|
assert.equal(roundtripped.nonMeshData.find((data) => data.id === surface.id).pointCount, 20);
|
|
close(roundtripped.nonMeshData.find((data) => data.id === surface.id).pointWeights[2], 0.85, "reopened surface weight");
|
|
close(roundtripped.nonMeshData.find((data) => data.id === metaball.id).elements[0].radius, metaballElements[0].radius, "reopened metaball radius");
|
|
assert.equal(roundtripped.nonMeshData.find((data) => data.id === font.id).text, "N-015 roundtrip");
|
|
assert.deepEqual(roundtripped.nonMeshData.find((data) => data.id === curveId).cyclicU, curveCyclic);
|
|
close(roundtripped.nonMeshData.find((data) => data.id === curveId).handlePoints[0], curveHandlePoints[0], "reopened curve left handle");
|
|
close(roundtripped.nonMeshData.find((data) => data.id === curveId).handlePoints[15], curveHandlePoints[15], "reopened curve right handle");
|
|
assert.equal(roundtripped.nonMeshData.find((data) => data.id === curveId).handleTypes[4], 0);
|
|
assert.equal(roundtripped.nonMeshData.find((data) => data.id === font.id).fontProperties.alignment, "RIGHT");
|
|
close(roundtripped.nonMeshData.find((data) => data.id === font.id).fontProperties.spacing, 1.2, "reopened font spacing");
|
|
close(roundtripped.nonMeshData.find((data) => data.id === font.id).fontProperties.offsetY, -0.1, "reopened font offset y");
|
|
assert.deepEqual(roundtripped.nonMeshData.find((data) => data.id === font.id).fontCharacters, fontCharacters);
|
|
assert.deepEqual(roundtripped.nonMeshData.find((data) => data.id === font.id).fontTextBoxes, fontTextBoxes);
|
|
assert.equal(roundtripped.nonMeshData.find((data) => data.id === font.id).activeFontTextBox, 1);
|
|
assert.deepEqual(roundtripped.nonMeshData.find((data) => data.id === font.id).fontLinks, fontLinks);
|
|
assert.equal(roundtripped.nonMeshData.find((data) => data.name === "CreatedWebCurve")?.controlPoints?.length, 21);
|
|
const createdAfterReopen = roundtripped.nonMeshData.find((data) => data.name === "CreatedWebCurve");
|
|
assert.deepEqual(createdAfterReopen.splineTypes, ["BEZIER", "NURBS"]);
|
|
assert.deepEqual(createdAfterReopen.splineOffsets, [0, 3, 7]);
|
|
assert.deepEqual(createdAfterReopen.cyclicU, [false, true]);
|
|
close(createdAfterReopen.handlePoints[1], twoSplineHandlePoints[1], "reopened bulk handle transaction");
|
|
command(engine, reopened, { type: "deleteNonMeshData", dataId: createdAfterReopen.id });
|
|
assert.equal(snapshot(engine, reopened).nonMeshData.some((data) => data.id === createdAfterReopen.id), false);
|
|
engine._web_engine_destroy(reopened);
|
|
console.log("nonmesh-roundtrip-ok types=CURVE,SURFACE,FONT,METABALL,VOLUME multispline-create-delete-bulk-handle-cyclic-surface-2d-topology-font-links-volume-properties=passed undo-redo=passed save-reopen=passed");
|