feat: add conservative topology history fallback
This commit is contained in:
@@ -8,6 +8,8 @@ export type { ApplyPlacementInput, BitBybitViewportAdapter, BitBybitWebCadFacade
|
||||
export { createEdgeSubshapeRefs, createSubshapeRefs, createVertexSubshapeRefs, matchSubshapes, signatureForEdge, signatureForFace, signatureForVertex } from './topologyNaming'
|
||||
export { createPersistedTopoRef, migrateTopoRefs, parseTopoRef, resolveTopoRef, serializeTopoRef } from './topologyReferences'
|
||||
export type { PersistedTopoRef, TopoRefResolution, TopologyMigration } from './topologyReferences'
|
||||
export { captureSignatureTopologyHistory } from './topologyHistory'
|
||||
export type { TopologyHistoryEntry, TopologyHistoryRelation, TopologyHistoryResult } from './topologyHistory'
|
||||
export { BasicSketchSolverAdapter, cloneSketch, createSketch, solveSketch } from './sketcher'
|
||||
export type { SketchConstraint, SketchDiagnostic, SketchGeometry, SketchPoint, SketchPointRef, SketchSnapshot, SketchSolveOptions, SketchSolveResult, SketchSolverAdapter, SketchSolverStatus } from './sketcher'
|
||||
export { createFacadeGeometryRecomputeExecutor, executeFacadeRecomputeNode, RecomputeCoordinator } from './recomputeEngine'
|
||||
|
||||
55
src/facade/topologyHistory.ts
Normal file
55
src/facade/topologyHistory.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { SubshapeRef } from './types'
|
||||
import { matchSubshapes, type SubshapeSignature } from './topologyNaming'
|
||||
|
||||
export type TopologyHistoryEntry = { ref: SubshapeRef; signature: SubshapeSignature }
|
||||
|
||||
export type TopologyHistoryRelation = {
|
||||
relation: 'preserved' | 'modified' | 'generated' | 'deleted' | 'ambiguous'
|
||||
sourceObjectId?: string
|
||||
sourcePersistentId?: string
|
||||
resultPersistentId?: string
|
||||
candidates?: Array<{ sourceObjectId: string; persistentId: string }>
|
||||
score: number
|
||||
}
|
||||
|
||||
export type TopologyHistoryResult = {
|
||||
operationId: string
|
||||
provider: 'signature-fallback'
|
||||
relations: TopologyHistoryRelation[]
|
||||
counts: Record<TopologyHistoryRelation['relation'], number>
|
||||
}
|
||||
|
||||
const sourceKey = (objectId: string, persistentId: string) => `${encodeURIComponent(objectId)}::${persistentId}`
|
||||
|
||||
export const captureSignatureTopologyHistory = (
|
||||
operationId: string,
|
||||
inputs: Array<{ objectId: string; entries: TopologyHistoryEntry[] }>,
|
||||
output: TopologyHistoryEntry[],
|
||||
): TopologyHistoryResult => {
|
||||
if (!operationId.trim()) throw new RangeError('Topology history operationId is required.')
|
||||
const sources = new Map<string, { objectId: string; persistentId: string; signature: SubshapeSignature }>()
|
||||
const previous = inputs.flatMap(({ objectId, entries }) => entries.map((entry) => {
|
||||
const key = sourceKey(objectId, entry.ref.persistentId)
|
||||
sources.set(key, { objectId, persistentId: entry.ref.persistentId, signature: entry.signature })
|
||||
return { ref: { ...entry.ref, persistentId: key }, signature: entry.signature }
|
||||
}))
|
||||
const matches = matchSubshapes(previous, output)
|
||||
const counts: TopologyHistoryResult['counts'] = { preserved: 0, modified: 0, generated: 0, deleted: 0, ambiguous: 0 }
|
||||
const relations = matches.map((match): TopologyHistoryRelation => {
|
||||
if (match.status === 'new') return { relation: 'generated', resultPersistentId: match.current.persistentId, score: match.score }
|
||||
if (match.status === 'deleted') {
|
||||
const source = match.previousId ? sources.get(match.previousId) : undefined
|
||||
return { relation: 'deleted', sourceObjectId: source?.objectId, sourcePersistentId: source?.persistentId, score: 0 }
|
||||
}
|
||||
if (match.status === 'ambiguous') {
|
||||
const candidates = (match.current.candidates ?? []).map((id) => sources.get(id)).filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate)).map((candidate) => ({ sourceObjectId: candidate.objectId, persistentId: candidate.persistentId }))
|
||||
return { relation: 'ambiguous', resultPersistentId: match.current.persistentId, candidates, score: match.score }
|
||||
}
|
||||
const source = match.previousId ? sources.get(match.previousId) : undefined
|
||||
const outputEntry = output.find((entry) => entry.ref.persistentId === match.current.persistentId || entry.ref.signature === match.current.signature)
|
||||
const relation = source && outputEntry && source.signature.hash === outputEntry.signature.hash ? 'preserved' : 'modified'
|
||||
return { relation, sourceObjectId: source?.objectId, sourcePersistentId: source?.persistentId, resultPersistentId: match.current.persistentId, score: match.score }
|
||||
})
|
||||
for (const relation of relations) counts[relation.relation] += 1
|
||||
return { operationId, provider: 'signature-fallback', relations, counts }
|
||||
}
|
||||
Reference in New Issue
Block a user