1/**
2 * Random STEP models from abc-step-1000 — the first 1000 STEP files of the
3 * ABC CAD dataset (Koch et al., CVPR 2019), rehosted gzip-compressed at
4 * https://concept-collection.github.io/abc-step-1000/. No OCCT here: this
5 * module only fetches bytes; the app feeds them to the normal import path.
6 */
8export const ABC_BASE = 'https://concept-collection.github.io/abc-step-1000'
9export const ABC_DATASET_URL = 'https://deep-geometry.github.io/abc-dataset/'
11// Files above this size make OCCT churn for a long time (the collection's
12// largest is 204 MB); random picks stay snappy by drawing from the rest.
13const MAX_STEP_BYTES = 2_000_000
15interface AbcIndexFile {
16 id: string
17 name: string
18 path: string
19 stepBytes: number
20 gzBytes: number
21}
23let indexPromise: Promise<AbcIndexFile[]> | null = null
25function fetchIndex(): Promise<AbcIndexFile[]> {
26 indexPromise ??= fetch(`${ABC_BASE}/index.json`)
27 .then((res) => {
28 if (!res.ok) throw new Error(`abc-step-1000 index: HTTP ${res.status}`)
29 return res.json()
30 })
31 .then((index: { files: AbcIndexFile[] }) => index.files)
32 .catch((e) => {
33 indexPromise = null // allow retry after a transient failure
34 throw e
35 })
36 return indexPromise
37}
39async function gunzipIfNeeded(buf: ArrayBuffer): Promise<Uint8Array> {
40 const head = new Uint8Array(buf, 0, 2)
41 if (head[0] !== 0x1f || head[1] !== 0x8b) return new Uint8Array(buf)
42 const stream = new Blob([buf]).stream().pipeThrough(new DecompressionStream('gzip'))
43 return new Uint8Array(await new Response(stream).arrayBuffer())
44}
46export async function fetchRandomAbcStep(): Promise<{ name: string; bytes: Uint8Array }> {
47 const files = (await fetchIndex()).filter((f) => f.stepBytes <= MAX_STEP_BYTES)
48 const file = files[Math.floor(Math.random() * files.length)]
49 const res = await fetch(`${ABC_BASE}/${file.path}`)
50 if (!res.ok) throw new Error(`${file.name}: HTTP ${res.status}`)
51 return { name: file.name, bytes: await gunzipIfNeeded(await res.arrayBuffer()) }
52}