openpencil/packages/fig/src/node-change/vector-geometry.ts
Danila Poyarkov c2f3338877 refactor(fig): own vector blob conversion
- Move vector network blob codecs and NodeChange geometry resolution into @open-pencil/fig\n- Preserve the existing core vector compatibility exports without pass-through wrappers\n- Add package-local round-trip coverage for handle mirroring
2026-07-18 01:41:31 +03:00

68 lines
2 KiB
TypeScript

import type { NodeChange } from '@open-pencil/kiwi/fig/codec'
import type { GeometryPath, VectorNetwork, WindingRule } from '@open-pencil/scene-graph'
import type { Vector } from '@open-pencil/scene-graph/primitives'
import { decodeVectorNetworkBlob } from './vector-network'
export function resolveVectorNetwork(nc: NodeChange, blobs: Uint8Array[]): VectorNetwork | null {
const vectorData = nc.vectorData as
| {
vectorNetworkBlob?: number
normalizedSize?: Vector
styleOverrideTable?: Array<{ styleID: number; handleMirroring?: string }>
}
| undefined
if (vectorData?.vectorNetworkBlob === undefined) return null
const idx = vectorData.vectorNetworkBlob
if (idx < 0 || idx >= blobs.length) return null
try {
const network = decodeVectorNetworkBlob(blobs[idx], vectorData.styleOverrideTable)
const ns = vectorData.normalizedSize
const nodeW = nc.size?.x ?? 0
const nodeH = nc.size?.y ?? 0
if (ns && nodeW > 0 && nodeH > 0 && (ns.x !== nodeW || ns.y !== nodeH)) {
const sx = nodeW / ns.x
const sy = nodeH / ns.y
for (const v of network.vertices) {
v.x *= sx
v.y *= sy
}
for (const seg of network.segments) {
seg.tangentStart = { x: seg.tangentStart.x * sx, y: seg.tangentStart.y * sy }
seg.tangentEnd = { x: seg.tangentEnd.x * sx, y: seg.tangentEnd.y * sy }
}
}
return network
} catch {
return null
}
}
interface KiwiPath {
windingRule?: string
commandsBlob?: number
}
export function resolveGeometryPaths(
paths: KiwiPath[] | undefined,
blobs: Uint8Array[]
): GeometryPath[] {
if (!paths || paths.length === 0) return []
const result: GeometryPath[] = []
for (const p of paths) {
if (p.commandsBlob === undefined || p.commandsBlob < 0 || p.commandsBlob >= blobs.length)
continue
const blob = blobs[p.commandsBlob]
if (blob.length === 0) continue
result.push({
windingRule: (p.windingRule === 'EVENODD' ? 'EVENODD' : 'NONZERO') as WindingRule,
commandsBlob: blob
})
}
return result
}