// Bridge between a plugin's Python and the fin engine. // // Python plugins (Orca today) run this bundle, built by plugins/shared/bundle.py, // inside an embedded V8 (mini-racer). Plain V8 has no atob/btoa/TextDecoder, so // geometry crosses the boundary as base64 of raw little-endian bytes, decoded here // by hand. That is ~20x smaller and much faster than a JSON number array for a // 100k-triangle part. // // in : base64 float64 triangle soup (posed, mm) + JSON options // out: JSON { triangles: base64 float32 soup (seated frame), offset, pieces, // overFaces, smallFaces, stats } // drawWallB64 does the same for Draw mode (draw_entry.js): one wall between two points. import { computeFins, ENGINE_DEFAULTS, optionsFromDialog, optionVisible, OPTIONS_SCHEMA } from './fins_entry.js'; import { drawWall } from './draw_entry.js'; import { reportLine } from './report.js'; const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; const LOOKUP = new Uint8Array(256); for (let i = 0; i < B64.length; i++) LOOKUP[B64.charCodeAt(i)] = i; export function b64ToBytes(s) { let pad = 0; if (s.endsWith('==')) pad = 2; else if (s.endsWith('=')) pad = 1; const n = (s.length / 4) * 3 - pad; const out = new Uint8Array(n); let o = 0; for (let i = 0; i < s.length; i += 4) { const a = LOOKUP[s.charCodeAt(i)], b = LOOKUP[s.charCodeAt(i + 1)]; const c = LOOKUP[s.charCodeAt(i + 2)], d = LOOKUP[s.charCodeAt(i + 3)]; const v = (a << 18) | (b << 12) | (c << 6) | d; if (o < n) out[o++] = (v >> 16) & 255; if (o < n) out[o++] = (v >> 8) & 255; if (o < n) out[o++] = v & 255; } return out; } export function bytesToB64(bytes) { const parts = []; let chunk = ''; for (let i = 0; i < bytes.length; i += 3) { const a = bytes[i], b = i + 1 < bytes.length ? bytes[i + 1] : 0, c = i + 2 < bytes.length ? bytes[i + 2] : 0; const v = (a << 16) | (b << 8) | c; chunk += B64[(v >> 18) & 63] + B64[(v >> 12) & 63] + (i + 1 < bytes.length ? B64[(v >> 6) & 63] : '=') + (i + 2 < bytes.length ? B64[v & 63] : '='); if (chunk.length > 65536) { parts.push(chunk); chunk = ''; } } parts.push(chunk); return parts.join(''); } const soupOf = (b64) => { const bytes = b64ToBytes(b64); return new Float64Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 8); }; const trianglesB64 = (t) => bytesToB64(new Uint8Array(t.buffer, t.byteOffset, t.byteLength)); export function computeFinsB64(soupB64, optionsJson) { const options = optionsJson ? JSON.parse(optionsJson) : {}; const res = computeFins(soupOf(soupB64), options); return JSON.stringify({ triangles: trianglesB64(res.triangles), offset: res.offset, pieces: res.pieces, overFaces: Array.from(res.overFaces), smallFaces: Array.from(res.smallFaces), stats: res.stats }); } // a, b: JSON [x, y, z] in the soup's frame. export function drawWallB64(soupB64, aJson, bJson, optionsJson) { const options = optionsJson ? JSON.parse(optionsJson) : {}; const res = drawWall(soupOf(soupB64), JSON.parse(aJson), JSON.parse(bJson), options); if (!res.ok) return JSON.stringify(res); return JSON.stringify({ ok: true, triangles: trianglesB64(res.triangles), offset: res.offset, stats: res.stats }); } // A Python host's settings dialog, as JSON strings like computeFinsB64 (supportfins_host // host_schema / host_options / host_visible). export const optionsSchemaJson = () => JSON.stringify(OPTIONS_SCHEMA); export const optionsFromDialogJson = (valuesJson) => JSON.stringify(optionsFromDialog(JSON.parse(valuesJson))); export const optionVisibleJson = (key, valuesJson) => optionVisible(key, JSON.parse(valuesJson)); // optionsFromDialog / optionVisible / OPTIONS_SCHEMA: what a host's settings dialog needs; // reportLine: the one-line result, for a host whose dialog is a web page (SolidWorks) export { computeFins, drawWall, ENGINE_DEFAULTS, optionsFromDialog, optionVisible, OPTIONS_SCHEMA, reportLine };