import { readFile } from 'node:fs/promises' import { resolve } from 'node:path' const root = resolve(new URL('..', import.meta.url).pathname) const oracle = JSON.parse(await readFile(resolve(root, 'config/freecad-attachment-mode-oracle.json'), 'utf8')) const fail = (message) => { throw new Error(`FreeCAD attachment mode oracle: ${message}`) } const near = (actual, expected) => typeof actual === 'number' && Math.abs(actual - expected) <= oracle.tolerance const same = (actual, expected) => JSON.stringify(actual) === JSON.stringify(expected) const modes = [ 'Deactivated', 'Translate', 'ObjectXY', 'ObjectXZ', 'ObjectYZ', 'FlatFace', 'TangentPlane', 'NormalToEdge', 'FrenetNB', 'FrenetTN', 'FrenetTB', 'Concentric', 'SectionOfRevolution', 'ThreePointsPlane', 'ThreePointsNormal', 'Folding', 'ObjectX', 'ObjectY', 'ObjectZ', 'AxisOfCurvature', 'Directrix1', 'Directrix2', 'Asymptote1', 'Asymptote2', 'Tangent', 'Normal', 'Binormal', 'TangentU', 'TangentV', 'TwoPointLine', 'IntersectionLine', 'ProximityLine', 'ObjectOrigin', 'Focus1', 'Focus2', 'OnEdge', 'CenterOfCurvature', 'CenterOfMass', 'IntersectionPoint', 'Vertex', 'ProximityPoint1', 'ProximityPoint2', 'AxisOfInertia1', 'AxisOfInertia2', 'AxisOfInertia3', 'InertialCS', 'FaceNormal', 'OZX', 'OZY', 'OXY', 'OXZ', 'OYZ', 'OYX', 'ParallelPlane', 'MidPoint', ] const implemented = { plane: ['Translate', 'ObjectXY', 'ObjectXZ', 'ObjectYZ', 'FlatFace', 'TangentPlane', 'NormalToEdge', 'FrenetNB', 'FrenetTN', 'FrenetTB', 'Concentric', 'SectionOfRevolution', 'ThreePointsPlane', 'ThreePointsNormal', 'Folding', 'InertialCS', 'OZX', 'OZY', 'OXY', 'OXZ', 'OYZ', 'OYX', 'ParallelPlane'], line: ['ObjectX', 'ObjectY', 'ObjectZ', 'AxisOfCurvature', 'Directrix1', 'Directrix2', 'Asymptote1', 'Asymptote2', 'Tangent', 'Normal', 'Binormal', 'TwoPointLine', 'IntersectionLine', 'ProximityLine', 'AxisOfInertia1', 'AxisOfInertia2', 'AxisOfInertia3', 'FaceNormal'], point: ['ObjectOrigin', 'Focus1', 'Focus2', 'OnEdge', 'CenterOfCurvature', 'CenterOfMass', 'Vertex', 'ProximityPoint1', 'ProximityPoint2'], } implemented.sketch = implemented.plane const engineTypes = { plane: 'Attacher::AttachEnginePlane', line: 'Attacher::AttachEngineLine', point: 'Attacher::AttachEnginePoint', sketch: 'Attacher::AttachEnginePlane' } const objectTypes = { plane: 'PartDesign::Plane', line: 'PartDesign::Line', point: 'PartDesign::Point', sketch: 'Sketcher::SketchObject' } if (oracle.schemaVersion !== 1 || oracle.baselineId !== 'freecad-1.1.1-attachment-mode-oracle' || oracle.freecadVersion !== '1.1.1' || oracle.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || oracle.status !== 'pass' || oracle.tolerance !== 1e-7) fail('baseline is invalid.') if (oracle.registry?.modeCount !== 55 || !same(oracle.registry.modes, modes) || oracle.registry.implementedUnionCount !== 50 || !same(oracle.registry.unimplementedModes, ['Deactivated', 'TangentU', 'TangentV', 'IntersectionPoint', 'MidPoint'])) fail('persistent mode registry or implemented union changed.') for (const name of Object.keys(engineTypes)) { const engine = oracle.engines?.[name] if (engine?.typeId !== objectTypes[name] || engine.attacherType !== engineTypes[name] || !same(engine.completeModeList, modes) || !same(engine.implementedModes, implemented[name])) fail(`${name} engine identity or implemented modes changed.`) for (const mode of implemented[name]) { const info = engine.modeInfo?.[mode] if (info?.modeIndex !== modes.indexOf(mode) || !Array.isArray(info.referenceCombinations) || info.referenceCombinations.length === 0) fail(`${name}.${mode} mode metadata is incomplete.`) } } if (!same(oracle.engines.plane.modeInfo.FlatFace.referenceCombinations, [['Plane']]) || !same(oracle.engines.line.modeInfo.TwoPointLine.referenceCombinations, [['Vertex', 'Vertex'], ['Line']]) || !same(oracle.engines.point.modeInfo.Vertex.referenceCombinations, [['Vertex'], ['Line']])) fail('representative reference combination contract changed.') const expectedSuccess = { plane: { typeId: 'PartDesign::Plane', mode: 'FlatFace', refs: ['Plane'], support: ['Face1'] }, line: { typeId: 'PartDesign::Line', mode: 'TwoPointLine', refs: ['Vertex', 'Vertex'], support: ['Vertex1', 'Vertex2'] }, point: { typeId: 'PartDesign::Point', mode: 'Vertex', refs: ['Vertex'], support: ['Vertex1'] }, sketch: { typeId: 'Sketcher::SketchObject', mode: 'FlatFace', refs: ['Plane'], support: ['Face1'] }, } for (const phase of ['initial', 'edited', 'roundtrip']) { for (const [name, expected] of Object.entries(expectedSuccess)) { const result = oracle.success?.[phase]?.[name] if (result?.typeId !== expected.typeId || result.mapMode !== expected.mode || result.positionBySupport !== true || !same(result.state, ['Up-to-date']) || result.status !== 'Valid' || result.suggestion?.message !== 'OK' || !result.suggestion.allApplicableModes.includes(expected.mode) || !same(result.suggestion.referenceTypes, expected.refs) || !same(result.support?.[0]?.subElements, expected.support) || result.support?.[0]?.object !== 'SourceBox') fail(`${phase} ${name} success evidence is invalid.`) } } if (!near(oracle.success.initial.point.placement.position?.[2], 6) || !near(oracle.success.edited.point.placement.position?.[2], 9) || !near(oracle.success.roundtrip.point.placement.position?.[2], 9)) fail('source mutation did not move the attached point through edit and round-trip.') const failureContract = { InvalidPlane: ['PartDesign::Plane', 'Vertex', 'Attachment mode Vertex is not implemented.'], InvalidLine: ['PartDesign::Line', 'FlatFace', 'Attachment mode FlatFace is not implemented.'], InvalidPoint: ['PartDesign::Point', 'FlatFace', 'Attachment mode FlatFace is not implemented.'], } for (const [name, [typeId, mode, status]] of Object.entries(failureContract)) { const initial = oracle.failures?.initial?.[name] const roundtrip = oracle.failures?.roundtrip?.[name] if (initial?.typeId !== typeId || initial.mapMode !== mode || !same(initial.state, ['Touched', 'Invalid']) || initial.status !== status || roundtrip?.mapMode !== mode || !same(roundtrip.state, ['Touched', 'Invalid']) || roundtrip.status !== status) fail(`${name} unsupported-mode failure did not survive FCStd round-trip.`) } console.log(JSON.stringify({ status: 'freecad-attachment-mode-oracle-pass', baselineId: oracle.baselineId, persistentModes: oracle.registry.modeCount, implementedModes: { plane: implemented.plane.length, line: implemented.line.length, point: implemented.point.length, sketch: implemented.sketch.length }, representativeSuccesses: Object.keys(expectedSuccess), unsupportedFailures: Object.keys(failureContract), pointMutationZ: [oracle.success.initial.point.placement.position[2], oracle.success.edited.point.placement.position[2]], fcstdRoundtrip: true }, null, 2))