feat: classify fillet ordered pair row
This commit is contained in:
@@ -205,6 +205,26 @@ const expectedPairs = [
|
||||
{ pair: 'groove->linear-pattern', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepPrimAPI_MakeRevol+BRepAlgoAPI_Cut', firstInputCount: 2, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'translationX', mutationTrajectory: [8, 9, 8] },
|
||||
{ pair: 'groove->polar-pattern', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepPrimAPI_MakeRevol+BRepAlgoAPI_Cut', firstInputCount: 2, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [90, 120, 90] },
|
||||
{ pair: 'groove->mirrored', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepPrimAPI_MakeRevol+BRepAlgoAPI_Cut', firstInputCount: 2, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'planeOriginX', mutationTrajectory: [5, 6, 5] },
|
||||
{ pair: 'groove->multi-transform', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepPrimAPI_MakeRevol+BRepAlgoAPI_Cut', firstInputCount: 2, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse(ordered)', secondInputCount: 1, mutationParameter: 'multiTranslationX', mutationTrajectory: [8, 9, 8] },
|
||||
{ pair: 'fillet->fuse', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Fuse', secondInputCount: 2, mutationParameter: 'toolOffsetX', mutationTrajectory: [8, 7, 8] },
|
||||
{ pair: 'fillet->cut', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Cut', secondInputCount: 2, mutationParameter: 'toolSize', mutationTrajectory: [4, 3, 4] },
|
||||
{ pair: 'fillet->common', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Common', secondInputCount: 2, mutationParameter: 'toolOffsetX', mutationTrajectory: [7, 6, 7] },
|
||||
{ pair: 'fillet->rotate', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [15, 22.5, 15] },
|
||||
{ pair: 'fillet->pad', decision: 'rejected', reasonCode: 'native-builder-and-freecad-profile-reject-with-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'occt-builder', secondBuilder: 'BRepPrimAPI_MakePrism', secondInputCount: 1, mutationParameter: 'length', mutationTrajectory: [5, 7.5, 5], nativeTypeId: 'PartDesign::Pad', freeCadDiagnostic: 'FeatureExtrusion: Length: Could not extrude the sketch!' },
|
||||
{ pair: 'fillet->pocket', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepPrimAPI_MakePrism+BRepAlgoAPI_Cut', secondInputCount: 2, mutationParameter: 'length', mutationTrajectory: [5, 4, 5] },
|
||||
{ pair: 'fillet->loft', decision: 'rejected', reasonCode: 'freecad-input-precondition-reject-with-kernel-diagnostic-and-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'freecad-document', kernelOutcome: 'accepted', secondBuilder: 'BRepOffsetAPI_ThruSections', secondInputCount: 2, mutationParameter: 'ruled', mutationTrajectory: [false, true, false], nativeTypeId: 'Part::Loft', freeCadDiagnostic: 'Profile shape is not a single vertex, edge, wire nor face.' },
|
||||
{ pair: 'fillet->pipe', decision: 'rejected', reasonCode: 'freecad-input-precondition-reject-with-kernel-diagnostic-and-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'freecad-document', kernelOutcome: 'invalid-result', secondBuilder: 'BRepOffsetAPI_MakePipe', secondInputCount: 2, mutationParameter: 'spineLength', mutationTrajectory: [15, 12, 15], nativeTypeId: 'Part::Sweep', freeCadDiagnostic: 'A fatal error occurred when making the sweep' },
|
||||
{ pair: 'fillet->revolution', decision: 'rejected', reasonCode: 'native-builder-and-freecad-profile-reject-with-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'occt-builder', secondBuilder: 'BRepPrimAPI_MakeRevol', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [360, 270, 360], nativeTypeId: 'Part::Revolution', freeCadDiagnostic: 'Solids are not Processed' },
|
||||
{ pair: 'fillet->groove', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepPrimAPI_MakeRevol+BRepAlgoAPI_Cut', secondInputCount: 2, mutationParameter: 'angle', mutationTrajectory: [360, 180, 360] },
|
||||
{ pair: 'fillet->fillet', decision: 'rejected', reasonCode: 'native-builder-and-freecad-profile-reject-with-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'occt-builder', secondBuilder: 'BRepFilletAPI_MakeFillet', secondInputCount: 1, mutationParameter: 'radius', mutationTrajectory: [0.4, 0.6, 0.4], nativeTypeId: 'Part::Fillet', freeCadDiagnostic: 'There are no suitable edges for chamfer or fillet' },
|
||||
{ pair: 'fillet->chamfer', decision: 'rejected', reasonCode: 'native-builder-and-freecad-profile-reject-with-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'occt-builder', secondBuilder: 'BRepFilletAPI_MakeChamfer', secondInputCount: 1, mutationParameter: 'distance', mutationTrajectory: [0.4, 0.6, 0.4], nativeTypeId: 'Part::Chamfer', freeCadDiagnostic: 'There are no suitable edges for chamfer or fillet' },
|
||||
{ pair: 'fillet->hole', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepPrimAPI_MakeCylinder+BRepAlgoAPI_Cut', secondInputCount: 1, mutationParameter: 'radius', mutationTrajectory: [1, 1.5, 1] },
|
||||
{ pair: 'fillet->draft', decision: 'rejected', reasonCode: 'native-builder-and-freecad-profile-reject-with-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'occt-builder', secondBuilder: 'BRepOffsetAPI_DraftAngle', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [5, 8, 5], nativeTypeId: 'PartDesign::Draft', freeCadDiagnostic: '', inputShapeTransfer: 'partdesign-feature-summary-exact-with-explicit-brep-drift' },
|
||||
{ pair: 'fillet->thickness', decision: 'rejected', reasonCode: 'native-and-freecad-profile-no-op-with-clean-resave', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, rejectionAuthority: 'occt-no-op', kernelOutcome: 'no-op', secondBuilder: 'BRepOffsetAPI_MakeThickSolid', secondInputCount: 1, mutationParameter: 'offset', mutationTrajectory: [-0.4, -0.6, -0.4], nativeTypeId: 'Part::Thickness', freeCadDiagnostic: 'Valid', freecadProfileNoOp: true },
|
||||
{ pair: 'fillet->linear-pattern', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'translationX', mutationTrajectory: [8, 9, 8] },
|
||||
{ pair: 'fillet->polar-pattern', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse', secondInputCount: 1, mutationParameter: 'angle', mutationTrajectory: [30, 45, 30] },
|
||||
{ pair: 'fillet->multi-transform', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeFillet', firstInputCount: 1, secondBuilder: 'BRepBuilderAPI_Transform+BRepAlgoAPI_Fuse(ordered)', secondInputCount: 1, mutationParameter: 'multiTranslationX', mutationTrajectory: [8, 9, 8] },
|
||||
{ pair: 'chamfer->fuse', decision: 'accepted', reasonCode: 'native-builders-mutation-naming-and-fcstd-resave-pass', firstBuilder: 'BRepFilletAPI_MakeChamfer', firstInputCount: 1, secondBuilder: 'BRepAlgoAPI_Fuse', secondInputCount: 2, mutationParameter: 'toolOffsetX', mutationTrajectory: [8, 7, 8] },
|
||||
]
|
||||
if (report.nativeProbe?.artifacts?.length !== 3 || report.classifications?.length !== expectedPairs.length) fail('native artifacts or classification prefix is incomplete.')
|
||||
for (const [index, entry] of report.classifications.entries()) {
|
||||
@@ -226,20 +246,31 @@ for (const [index, entry] of report.classifications.entries()) {
|
||||
}
|
||||
} else {
|
||||
if (rejection.kernelRestoredExactly !== true || rejection.kernelAttempts?.length !== 3) fail(`${expected.pair} OCCT diagnostic trajectory is incomplete.`)
|
||||
for (const [attemptIndex, attempt] of rejection.kernelAttempts.entries()) if (attempt.operation !== secondOperation || attempt.builder !== expected.secondBuilder || attempt.inputCount !== expected.secondInputCount || attempt.parameter !== expected.mutationParameter || attempt.parameterValue !== expected.mutationTrajectory[attemptIndex] || attempt.runtime !== 'occt-native' || attempt.outcome !== expected.kernelOutcome || attempt.resultProduced !== true || attempt.shapeProduced !== true || attempt.validShapeProduced !== (expected.kernelOutcome === 'accepted') || attempt.historyProduced !== true || attempt.historyProvider !== 'occt-native' || attempt.summary?.isValid !== (expected.kernelOutcome === 'accepted') || attempt.historyRecords < 1 || !attempt.historySha256 || attempt.authority !== (expected.kernelOutcome === 'accepted' ? 'kernel-superset-only' : 'kernel-invalid-result')) fail(`${expected.pair} OCCT compatibility attempt ${attemptIndex} is invalid.`)
|
||||
for (const [attemptIndex, attempt] of rejection.kernelAttempts.entries()) {
|
||||
const validKernelShape = expected.kernelOutcome !== 'invalid-result'
|
||||
const expectedAuthority = expected.kernelOutcome === 'no-op' ? 'kernel-no-op' : expected.kernelOutcome === 'accepted' ? 'kernel-superset-only' : 'kernel-invalid-result'
|
||||
if (attempt.operation !== secondOperation || attempt.builder !== expected.secondBuilder || attempt.inputCount !== expected.secondInputCount || attempt.parameter !== expected.mutationParameter || attempt.parameterValue !== expected.mutationTrajectory[attemptIndex] || attempt.runtime !== 'occt-native' || attempt.outcome !== expected.kernelOutcome || attempt.resultProduced !== true || attempt.shapeProduced !== true || attempt.validShapeProduced !== validKernelShape || attempt.historyProduced !== true || attempt.historyProvider !== 'occt-native' || attempt.summary?.isValid !== validKernelShape || attempt.historyRecords < 1 || !attempt.historySha256 || attempt.authority !== expectedAuthority || (expected.kernelOutcome === 'no-op' && JSON.stringify(attempt.summary) !== JSON.stringify(entry.first.summary))) fail(`${expected.pair} OCCT compatibility attempt ${attemptIndex} is invalid.`)
|
||||
}
|
||||
}
|
||||
const persistence = rejection.persistence
|
||||
const diagnostic = persistence?.diagnostic
|
||||
const freecadProfileAccepted = expected.freecadProfileAccepted === true
|
||||
const persistenceChecksPass = Object.values(persistence?.checks ?? {}).every(Boolean) || (freecadProfileAccepted && persistence?.checks?.nativeFeatureRejected === false && Object.entries(persistence?.checks ?? {}).filter(([key]) => key !== 'nativeFeatureRejected').every(([, value]) => value === true)) || (!freecadProfileAccepted && persistence?.checks?.nativeFeatureRejected === true && Object.entries(persistence?.checks ?? {}).filter(([key]) => key !== 'freecadProfileAccepted').every(([, value]) => value === true))
|
||||
const freecadProfileNoOp = expected.freecadProfileNoOp === true
|
||||
const persistenceChecksPass = Object.values(persistence?.checks ?? {}).every(Boolean) || (freecadProfileAccepted && persistence?.checks?.nativeFeatureRejected === false && Object.entries(persistence?.checks ?? {}).filter(([key]) => key !== 'nativeFeatureRejected').every(([, value]) => value === true)) || (freecadProfileNoOp && persistence?.checks?.freecadProfileNoOp === true && persistence?.checks?.freecadProfileAccepted === false && persistence?.checks?.nativeFeatureRejected === false && Object.entries(persistence?.checks ?? {}).filter(([key]) => !['nativeFeatureRejected', 'freecadProfileAccepted'].includes(key)).every(([, value]) => value === true)) || (!freecadProfileAccepted && !freecadProfileNoOp && persistence?.checks?.nativeFeatureRejected === true && Object.entries(persistence?.checks ?? {}).filter(([key]) => key !== 'freecadProfileAccepted').every(([, value]) => value === true))
|
||||
if (persistence?.status !== 'pass' || persistence.decision !== 'rejected' || persistence.pair !== entry.pair || persistence.operation !== secondOperation || persistence.nativeTypeId !== expected.nativeTypeId || persistence.freecadVersion !== '1.1.1' || !persistenceChecksPass) fail(`${expected.pair} FCStd rejection evidence is incomplete.`)
|
||||
if (diagnostic?.nativeTypeId !== expected.nativeTypeId || diagnostic.parameter?.name !== expected.mutationParameter || diagnostic.parameter.value !== expected.mutationTrajectory[0] || diagnostic.inputShape?.solids !== 1 || diagnostic.recomputeResult !== true || !diagnostic.objects?.some(({ name, typeId }) => name === 'PairResult' && typeId === expected.nativeTypeId)) fail(`${expected.pair} did not capture a real FreeCAD profile evaluation.`)
|
||||
if (freecadProfileAccepted) {
|
||||
if (freecadProfileNoOp) {
|
||||
if (diagnostic.shapeNull !== false || diagnostic.shapeValid !== true || diagnostic.statusString !== 'Valid' || !diagnostic.state?.includes('Up-to-date') || JSON.stringify(diagnostic.resultShape) !== JSON.stringify(diagnostic.inputShape)) fail(`${expected.pair} FreeCAD no-op evidence is incomplete.`)
|
||||
} else if (freecadProfileAccepted) {
|
||||
if (persistence.checks.nativeFeatureRejected !== false || persistence.checks.freecadProfileAccepted !== true || diagnostic.shapeNull !== false || diagnostic.shapeValid !== true || diagnostic.statusString !== 'Valid' || !diagnostic.state?.includes('Up-to-date')) fail(`${expected.pair} FreeCAD profile acceptance evidence is incomplete.`)
|
||||
} else if (diagnostic.shapeNull !== true || diagnostic.shapeValid !== false || !diagnostic.state?.includes('Invalid') || diagnostic.statusString === 'Valid') {
|
||||
fail(`${expected.pair} did not capture a real rejected FreeCAD recompute.`)
|
||||
}
|
||||
if (expected.freeCadDiagnostic && diagnostic.statusString !== expected.freeCadDiagnostic) fail(`${expected.pair} FreeCAD profile diagnostic changed.`)
|
||||
if (Object.hasOwn(expected, 'freeCadDiagnostic') && diagnostic.statusString !== expected.freeCadDiagnostic) fail(`${expected.pair} FreeCAD profile diagnostic changed.`)
|
||||
if (expected.inputShapeTransfer === 'partdesign-feature-summary-exact-with-explicit-brep-drift') {
|
||||
const input = diagnostic.input
|
||||
if (input?.name !== 'DraftBase' || input.typeId !== 'PartDesign::Feature' || JSON.stringify(input.subelements) !== JSON.stringify(['Face1']) || input.sourceName !== persistence.source?.name || input.shapeSummaryTransferredExactly !== true || typeof input.sourceBrepSha256 !== 'string' || typeof input.draftBaseBrepSha256 !== 'string' || input.sourceBrepSha256 === input.draftBaseBrepSha256) fail(`${expected.pair} PartDesign input Shape transfer evidence is incomplete.`)
|
||||
}
|
||||
if (persistence.source?.shape?.solids !== 1 || !persistence.source.shape.brepSha256 || JSON.stringify(persistence.rollback?.before) !== JSON.stringify(persistence.rollback?.after)) fail(`${expected.pair} abort did not restore the source object set and Shape.`)
|
||||
const phases = persistence.phases
|
||||
if (JSON.stringify(phases?.initial) !== JSON.stringify(phases?.reopened) || JSON.stringify(phases?.initial) !== JSON.stringify(phases?.resaved) || phases.initial.objects?.length !== persistence.rollback.before.objects.length || phases.initial.objects?.some(({ typeId }) => typeId === 'PartDesign::Body' || typeId === 'PartDesign::Pad' || typeId === 'Part::Loft' || typeId === 'Part::Revolution')) fail(`${expected.pair} polluted the clean FCStd save/reopen/resave chain.`)
|
||||
@@ -255,6 +286,6 @@ for (const [index, entry] of report.classifications.entries()) {
|
||||
const phases = entry.persistence.phases
|
||||
if (JSON.stringify(phases?.initial) !== JSON.stringify(phases?.reopened) || JSON.stringify(phases?.initial) !== JSON.stringify(phases?.resaved) || phases.initial.namingEvidenceSha256 !== entry.second.namingEvidenceSha256) fail(`${expected.pair} FCStd phases changed Shape or naming evidence.`)
|
||||
}
|
||||
const expectedSummary = { registeredOperations: 19, orderedPairs: 361, classifiedPairs: 187, accepted: 143, rejected: 44, unknown: 174 }
|
||||
const expectedSummary = { registeredOperations: 19, orderedPairs: 361, classifiedPairs: 207, accepted: 155, rejected: 52, unknown: 154 }
|
||||
if (JSON.stringify(report.summary) !== JSON.stringify(expectedSummary)) fail('summary is inconsistent.')
|
||||
console.log(JSON.stringify({ status: 'freecad-ordered-operation-pair-classification-pass', completedTasks: report.classifications.map(({ taskId }) => taskId), pairs: report.classifications.map(({ pair, classification }) => ({ pair, classification })), nativeBuilderRuns: report.classifications.length * 4, fcstdPhases: report.classifications.length * 3, remainingPairs: report.summary.unknown }, null, 2))
|
||||
|
||||
Reference in New Issue
Block a user