-
-
Save ochafik/cf84c971339736b4aa2a84a0c6c86f18 to your computer and use it in GitHub Desktop.
OpenSCAD.js TS definitions brainstorming
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| - JS/TS library | |
| - VS Code to the rescue! | |
| - Mechanical, (mostly) bidirectional source code converter (OpenSCAD -> OpenSCAD.js guaranteed) | |
| - Node.js native extension: run OpenSCAD in Node.js at full native speed !!! | |
| - Emscripten: run OpenSCAD in the browser (or Node.js) at reduced speed. | |
| - | |
| - [HARD] Future extension: include OpenCSG w/ WebGL | |
| */ | |
| type Point2d = [number, number]; | |
| type Point3d = [number, number, number]; | |
| type Transform3d = [ | |
| number, number, number, number, | |
| number, number, number, number, | |
| number, number, number, number, | |
| number, number, number, number | |
| ]; | |
| const enum CsgOperation { | |
| Union = 1, | |
| Intersection = 2, | |
| Minkowski = 3, | |
| Difference = 4, | |
| } | |
| // Easily convertible to/from a Surface_mesh | |
| type Poly = { | |
| dimension: 2 | 3, | |
| points: Float64Array, | |
| components: Int32Array, | |
| convexity: number | |
| }; | |
| type FeatureName = | |
| 'fast-csg' | 'lazy-union'; | |
| type FeatureSet = FeatureName[];//{[name: FeatureName]: bool}; | |
| // NodeHandle is the index in an unordered_map<NodeHandle, std::shared_ptr<AbstractNode>> | |
| type NodeHandle = number; | |
| type ExecutionContext = { | |
| // ~ExecutionContext will delete all the nodes. | |
| createColorNode(color: string): NodeHandle; | |
| createImportNode(file: string): NodeHandle; | |
| createPolyNode(poly: Poly): NodeHandle; | |
| createCsgOperationNode(op: CsgOperation): NodeHandle; | |
| createMultmatrixNode(matrix: Transform3d): NodeHandle; | |
| deleteNode(node: NodeHandle): void; | |
| addChild(parent: NodeHandle, child: NodeHandle): void; | |
| // Creates a root, attaches to it and renders. | |
| renderGeometry(node: NodeHandle, opts?: {experimentalFeatures?: FeatureSet}): Poly; | |
| // getCurrentParent(): NodeHandle; | |
| // pushCurrentParent(node: NodeHandle): void; | |
| // popCurrentParent(): void; | |
| }; | |
| // declare let runtime: ExecutionContext; | |
| type SceneDeclaration = () => void; | |
| type RenderFormat = "csg" | "stl" | "nef"; | |
| class OpenSCADImpl { | |
| // Runtime is implemented in C++ and bound by a native extension in Node.js or through Emscripten FFI. | |
| private runtime: ExecutionContext | |
| private currentStack: NodeHandle[] = []; | |
| private withPushedNode(newParent: NodeHandle, ...children: SceneDeclaration[]) { | |
| this.currentStack.push(newParent); | |
| try { | |
| for (const child of children) { | |
| child(); | |
| } | |
| } finally { | |
| if (this.currentStack[this.currentStack.length] === newParent) throw 'Invalid state'; | |
| this.currentStack.pop(); | |
| } | |
| } | |
| private addChild(child: NodeHandle) { | |
| if (!child) throw "No child!"; | |
| if (this.currentStack.length == 0) throw "Now parent!"; | |
| const parent = this.currentStack[this.currentStack.length - 1]; | |
| if (!parent) throw "No parent!"; | |
| this.runtime.addChild(parent, child); | |
| } | |
| run(scene: SceneDeclaration) { | |
| const globals = ['cube', 'color', 'union', 'intersection', 'difference', 'translate', 'scale', 'polyhedron']; | |
| const oldValues = {}; | |
| for (const name of globals) { | |
| oldValues[name] = window[name]; | |
| window[name] = this[name].bind(this); | |
| } | |
| try { | |
| scene(); | |
| } finally { | |
| for (const name of globals) { | |
| window[name] = oldValues[name] | |
| } | |
| } | |
| } | |
| render(format: RenderFormat, opts?: {experimentalFeatures?: FeatureSet}) { | |
| } | |
| cube(size: number) { | |
| this.polyhedron([[0, 0, 0]], [[0]], 1); | |
| } | |
| color(color: string, ...children: SceneDeclaration[]) { | |
| this.withPushedNode(this.runtime.createColorNode(color), ...children); | |
| } | |
| union(...children: SceneDeclaration[]) { | |
| this.withPushedNode(this.runtime.createCsgOperationNode(CsgOperation.Union), ...children); | |
| } | |
| intersection(...children: SceneDeclaration[]) { | |
| this.withPushedNode(this.runtime.createCsgOperationNode(CsgOperation.Intersection), ...children); | |
| } | |
| difference(...children: SceneDeclaration[]) { | |
| this.withPushedNode(this.runtime.createCsgOperationNode(CsgOperation.Difference), ...children); | |
| } | |
| translate([tx, ty, tz]: [number, number, number], ...children: SceneDeclaration[]) { | |
| this.withPushedNode(this.runtime.createMultmatrixNode([1, 0, 0, tx, 0, 1, 0, ty, 0, 0, 1, tz, 0, 0, 0, 1]), ...children); | |
| } | |
| scale([sx, sy, sz]: [number, number, number], ...children: SceneDeclaration[]) { | |
| this.withPushedNode(this.runtime.createMultmatrixNode([sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1]), ...children); | |
| } | |
| polyhedron(...args) { | |
| let points: Point3d[], faces: number[], convexity: number; | |
| if (args.length == 1) { | |
| [{points, faces, convexity}] = args; | |
| } else { | |
| [points, faces, convexity] = args; | |
| } | |
| if (convexity == null) { | |
| convexity = 1; | |
| } | |
| let pointArray = new Float64Array(points.length * 3); | |
| for (let i = 0, n = points.length; i < n; i++) { | |
| const point = points[i]; | |
| if (point.length != 3) throw `Not a 3D point: ${JSON.stringify(point)}`; | |
| pointArray.set(point, i * 3); | |
| } | |
| let components = new Int32Array(faces); | |
| this.addChild(this.runtime.createPolyNode({dimension: 2, points: pointArray, components, convexity})); | |
| } | |
| } | |
| type RemoveMembersWithNames<Source, NameUnion> = | |
| {[K in keyof Source]: K extends NameUnion ? never : Source[K]}; | |
| type OpenSCAD = | |
| { | |
| new(): OpenSCAD; | |
| polyhedron(points: Point3d[], faces: number[], convexity?: number): void; | |
| polyhedron({points, faces, convexity}: {points: Point3d[], faces: number[], convexity?: number}): void; | |
| polygon(points: Point3d[], paths: number[], convexity?: number): void; | |
| polygon({points, paths, convexity}: {points: Point3d[], paths: number[], convexity?: number}): void; | |
| } | | |
| RemoveMembersWithNames<OpenSCADImpl, 'polyhedron' | 'polygon'>[keyof OpenSCADImpl] ; | |
| export const OpenSCAD: OpenSCAD = OpenSCADImpl as any; | |
| var openscad = new OpenSCAD(); | |
| openscad.polyhedron({points: [], faces: []}); | |
| openscad.cube(1); | |
| /* | |
| module A() scale(10) cube(1); | |
| difference() { | |
| union() { | |
| A(); | |
| translate([1, 0, 0]) A(); | |
| } | |
| } | |
| */ | |
| function A() { openscad.scale(10); openscad.cube(1); } | |
| openscad.difference(() => { | |
| openscad.union(() => { | |
| A(); | |
| openscad.translate([1, 0, 0], A); | |
| }) | |
| }) | |
| // declare interface Window { difference: any } | |
| declare global { | |
| interface Window { difference: any; } | |
| } | |
| window.difference = window.difference || {}; | |
| var openscad = new OpenSCAD(); | |
| openscad.run(() => { | |
| function A() { scale(10); cube(1); } | |
| difference(() => { | |
| union(() => { | |
| A(); | |
| translate([1, 0, 0], A); | |
| }) | |
| }) | |
| }) | |
| console.log(openscad.render()); | |
| // let openscad: OpenSCAD = new OpenSCADImpl(); | |
| // | |
| // Declarative API | |
| // | |
| // type SceneDeclaration = (context: ExecutionContext) => void; | |
| // // | |
| // // Object API: weird as we won't have access to the geometry. | |
| // // | |
| // type SceneDeclarationCtx = (ctx: SceneContext) => void; | |
| // class SceneContext { | |
| // cube(size: number) { | |
| // returnpolyhedron([[0, 0, 0]], [[0]], 1); | |
| // } | |
| // color(color: string, children: SceneDeclaration) { | |
| // children(); | |
| // } | |
| // union(...children: SceneDeclaration[]) { | |
| // withPushedNode(runtime.createCsgOperation(CsgOperation.Union), ...children); | |
| // } | |
| // intersection(...children: SceneDeclaration[]) { | |
| // withPushedNode(runtime.createCsgOperation(CsgOperation.Intersection), ...children); | |
| // } | |
| // difference(...children: SceneDeclaration[]) { | |
| // withPushedNode(runtime.createCsgOperation(CsgOperation.Difference), ...children); | |
| // } | |
| // translate([tx, ty, tz]: [number, number, number], children: SceneDeclaration) { | |
| // withPushedNode(runtime.createMultmatrix([1, 0, 0, tx, 0, 1, 0, ty, 0, 0, 1, tz, 0, 0, 0, 1]), ...children); | |
| // } | |
| // scale([sx, sy, sz]: [number, number, number], children: SceneDeclaration) { | |
| // withPushedNode(runtime.createMultmatrix([sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1]), ...children); | |
| // } | |
| // declare module OpenSCAD { | |
| // function polyhedron(points: Point3d[], faces: number[], convexity?: number): void; | |
| // function polyhedron(args: {points: Point3d[], faces: number[], convexity: number}): void; | |
| // function polygon(points: Point3d[], paths: number[], convexity?: number): void; | |
| // function polygon(args: {points: Point3d[], paths: number[], convexity: number}): void; | |
| // } | |
| // } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment