modelscript/languages/scad at main · modelscript/modelscript · GitHub
Skip to content

Latest commit

 

History

History

scad

Folders and files

README.md

بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ
In the name of Allah, the Compassionate, the Merciful

@modelscript/scad

OpenSCAD parametric 3D Constructive Solid Geometry (.scad) compiler, WebAssembly GLR parser, fluent method-chaining evaluator, tag_port() boundary port tagger, 9-rule semantic linter (SCAD_*), OpenCascade B-Rep & ISO 10303-21 STEP exporter (compileScadToStep), and ScadPatcher 3D gizmo AST writeback engine for ModelScript.


Core Capabilities

  • WASM GLR Incremental Parser (getScadParser): Generated by @modelscript/dsl (buildParser) into native WebAssembly (parser.wasm), supporting standard OpenSCAD prefix trees (translate(...) rotate(...) difference() { ... }), modules, functions, control flow (if, for), and ModelScript's fluent CAD extensions.
  • Fluent Method-Chaining Syntax: Extends standard OpenSCAD primitives (cube, cylinder, sphere, circle, square, polyhedron, linear_extrude) with fluent postfix transformation and edge-feature chains:
    • Spatial transforms: .translate([x, y, z]), .rotate([rx, ry, rz]), .scale([sx, sy, sz]), .mirror([nx, ny, nz]), .color("...")
    • B-Rep edge features: .fillet(r) and .chamfer(d)
  • tag_port() Physical Boundary Tagging: First-class tag_port("portName", "frame" | "fluid" | "thermal") operator attaching named topological coordinate frames and surface normals to CSG faces for automatic multibody joint alignment, FEA load application, and Modelica/SysML v2 port synthesis.
  • 9 SCAD_* Semantic Lint Rules (lintScad, lintScadCst): Single-pass lexical scope analyzer with OpenSCAD block declaration hoisting, constant-folded arithmetic evaluation, division-by-zero detection, call arity validation, and strictly positive CSG primitive dimension verification—all reporting exact AST token byte and line/column ranges.
  • OpenCascade B-Rep & STEP Compiler (compileScadToStep, compileScadToSolid, extractScadCadComponents): Evaluates CSG trees (ScadEvaluator) into @modelscript/cad Solid representations, tessellates watertight 3D manifold triangle meshes (ManifoldWorker), and compiles exact ISO 10303-21 STEP B-Rep solids (compileToStep).
  • ScadPatcher 3D Gizmo AST Writeback (ScadPatcher, scadWriteback): Preserves SolidSourceMetadata linking 3D viewport meshes back to their originating CST nodes so interactive 3D TransformControls gizmo drags patch exact vector components (patchTransformArgument, patchVectorComponent, patchVariable, patchNode) in .scad source code without reformatting surrounding code.
  • 13 Bidirectional Polyglot TGG Rules: Synchronizes OpenSCAD modules, tag_port() frames, projected frontal areas, variables, CSG primitives, and boolean composites with SysML v2, ISO 10303 STEP products, and Modelica MultiBody visualizers/frames.

Package Structure

Path Description
src/index.ts High-level API (getScadParser, lintScad, compileScadToSolid, compileScadToStep, extractScadCadComponents)
src/language.ts scadLanguage grammar definition, LSP modelscript/getCadComponents handler, actions (preview_csg, export_step), and 13 TGG rules
src/evaluator.ts ScadEvaluator interpreting OpenSCAD ASTs, loops, modules, fluent .translate()/.fillet() chains, and tag_port() tags
src/linter.ts lintScadCst semantic linter implementing all 9 SCAD_* diagnostic rules with constant folding
src/patcher.ts ScadPatcher surgical CST byte-slice mutator (patchNode, patchVariable, patchTransformArgument, patchVectorComponent)
src/writeback.ts scadWriteback integration hook for LSP & 3D CAD viewport synchronization
build.js Compiles src/language.ts into dist/parser.wasm and src-gen/bindings.js

Semantic Lint Rules (SCAD_*)

Code Severity Description
SCAD_INVALID_DIMENSION error Non-positive ($\le 0$) or wrong-length dimension in cube, cylinder, sphere, circle, square, linear_extrude, .fillet(), or .chamfer()
SCAD_UNDEFINED_VARIABLE error Referenced variable is not declared in the lexical scope hierarchy or OpenSCAD built-in specials ($fn, PI)
SCAD_UNDEFINED_FUNCTION error Called function is neither declared in scope nor an OpenSCAD built-in mathematical/list function
SCAD_UNDEFINED_MODULE error Instantiated module is neither declared in scope nor a built-in CSG primitive/operator
SCAD_UNKNOWN_METHOD error Unknown fluent postfix method (supported: translate, rotate, scale, mirror, color, fillet, chamfer)
SCAD_DIVISION_BY_ZERO error Constant-folded divisor in / or % binary expression evaluates to 0
SCAD_ARGUMENT_COUNT error / warning Excess positional arguments (error) or missing required parameter without default value (warning)
SCAD_UNKNOWN_PARAMETER error Named argument in a user module/function call does not match any declared parameter
SCAD_DUPLICATE_DECLARATION error / warning Duplicate module/function/parameter declaration (error) or shadowed variable assignment (warning)

Polyglot Triple Graph Grammar (TGG) Rules

@modelscript/scad defines 13 bidirectional TGG rules in scadLanguage.polyglot.rules:

Rule Name Source (scad) Target (sysml2 / step / modelica) Priority
ScadProjectedAreaToModelicaParam TagPortOp(args: portName) modelica: ComponentClause(name: "A", defaultValue: area) 15
ScadProjectedAreaToSysmlAttribute TagPortOp(args: portName) sysml2: AttributeUsage(declaredName: "totalFrontalArea", defaultValue: area) 15
ScadBooleanOpToStepComposite BooleanOp(op: opName, body: children) step: ProductDefinition(name: opName, description: "CSGComposite") 15
ScadCylinderToStepProduct CylinderPrimitive(args: dims) step: ProductDefinition(name: dims, description: "CylinderSolid") 10
ScadCylinderToMultiBodyShape CylinderPrimitive(args: dims) modelica: ComponentClause(typeSpecifier: "Modelica.Mechanics.MultiBody.Visualizers.Advanced.Shape") 10
ScadSphereToStepProduct SpherePrimitive(args: dims) step: ProductDefinition(name: dims, description: "SphereSolid") 5
ScadCubeToMultiBodyShape CubePrimitive(args: dims) modelica: ComponentClause(typeSpecifier: "Modelica.Mechanics.MultiBody.Visualizers.Advanced.Shape") 5
ScadSphereToMultiBodyShape SpherePrimitive(args: dims) modelica: ComponentClause(typeSpecifier: "Modelica.Mechanics.MultiBody.Visualizers.Advanced.Shape") 5
ScadModuleToSysmlPart ModuleDeclaration(name: modName) sysml2: PartDefinition(declaredName: modName) 0
ScadTagPortToSysmlPort TagPortOp(args: portName) sysml2: PortUsage(declaredName: portName) 0
ScadVariableToSysmlAttribute VariableDeclaration(name: varName) sysml2: AttributeUsage(declaredName: varName, defaultValue: val) 0
ScadSolidToStepProduct CubePrimitive(args: dims) step: ProductDefinition(name: dims) 0
ScadTagPortToModelicaFrame TagPortOp(args: portName) modelica: ComponentClause(name: portName, typeSpecifier: "Modelica.Mechanics.MultiBody.Interfaces.Frame_a") 0

Scripts

Command Description
npm run build Build WASM parser (node build.js) and compile TypeScript
npm run build:parser Force-rebuild parser.wasm and generated bindings
npm test Run CSG evaluator, linter, and patcher tests via test-runner.js
npm run lint Run ESLint across src/ and tests/
npm run typecheck Run tsc --noEmit type verification
npm run clean Remove dist/, src-gen/, as-gen/, and .wasm artifacts

Programmatic Usage

import { compileScadToStep, extractScadCadComponents, getScadParser, lintScad, ScadPatcher } from "@modelscript/scad";

const source = `
arm_length = 180;
cube([arm_length, 24, 6], center = true)
  .translate([90, 0, 0])
  .fillet(1.5);
tag_port("motor_mount", "frame")
  cylinder(h = 12, r = 14).translate([180, 0, 6]);
`;

// 1. Run semantic linter (9 SCAD_* rules)
const diagnostics = await lintScad(source);

// 2. Compile directly to ISO 10303-21 STEP B-Rep solid
const stepFileContent = await compileScadToStep(source);

// 3. Extract interactive 3D mesh components with sourceMetadata
const components = await extractScadCadComponents(source);

// 4. Patch AST transform vector from 3D gizmo drag
const parser = await getScadParser();
const tree = parser.parse(source);
const patched = ScadPatcher.patchTransformArgument(source, tree.rootNode, "translate", 0, [95.0, 0.0, 2.5]);