Lets kickstart the 3D printing open things revolution!
...by creating a vibrant, coherent open things ecosystem modeled after the best practices already established by the Open Source Software community.
- Purely functional and almost declarative - no mutations or side-effects allowed
- Transpiled to JS which is then evaluated calling native
libs or optimized computation cores for heavy-lifting.
- Or transpile to TypeScript / AssemblyScript?
- GPGPU using WebGL?
- CGAL possible to compile with emscripten?
- Bindings to CGAL or similar allowing only desktop application will suffice.
- Using JS gives us web compat from the start and quite good performance for a functional language (V8 has tail call optimization?). Can reuse a lot of existing tooling. And the eage hype-prone userbase...
- Integrate with VSCode, make a codepen, ParametrizedThingVerse and whatnot.
- Live web preview when changing params.
- Heavy lifting with web generators done on client's machine, no need to beefy servers for all the computations.
- Easily use JS for scripting automation and everything besides generating the models themselves.
- Compile the resulting JS (or TypeScript) to WASM for perf?
- Question: Strong typing (like python) for sure but do we allow dynamic typing or go full strict static declarations?
- Every Geometry node (an object or group of) is rendered
to triangle mesh along the path and stored in cache. It shall
also be given a name and id. Hash can be computed based code
block and variable values - this is guaranteed to work since
we have immutability. This way we will always re-render only
the needed parts.
- Cache should (optionally) use persistent storage for greatest acceleration (save resources when automating!).
- Transpiled code can be used as intermediary format for various kinds of automation. Can be also precompiled for distribution or during installation to save time later.
- Every (Geometry Node can be assigned a custom name and other
properties like color and material (maybe later).
- Export rendered final models to formats preserving this information. (Of course for the ones rendered from top-level nodes.)
- Preview rendering automation in software like POVRAY or blender.
- Single source produces multiple top-level Geometry Nodes thus can automatically exported into separate models upon build.
- Geometry Nodes are connected to each other forming a graph, not a tree
like the usual approach.
- Allows for better caching - no need unwind the graph into a tree what would duplicate nodes and computing work.
- Graph will contain cycles, but this is unavoidable and happens in every programming language.
- We don't even need to walk the graph, we can just find any node that is not dependent on others and just execute the transpiled JS.
- The Graph can be represented in an UI which would allow interactive, visual manipulation of the nodes and give nice overview. Module parameters could also be directly adjusted from the GUI. Selecting a Node could jump you into the relevant source-code.
- Package manager (npm with custom repository?)
- Metadata schema (YAML/JSON-based) for providing info about
projects, libraries, parts, author, license, type, printing, etc.
- Also a universal open schema for describing physical things,
which will allow:
- Easy cataloging, scraping, search, discovery. Find by:
- Use-case
- Compatiblity
- Vistual style
- Production technology
- Tolerances
- Suggested materials
- Exploring links between projects, authors
- Creation of huge sets of visually matching or interconnectable, modular parts created by separate people
- Providing information for the actual production like suggested materials, parameters or even pre-computed production profiles
- Slice complicated multi-part gizmos in minutes and start printing right away getting best possible result without hassle
- Easy cataloging, scraping, search, discovery. Find by:
- Also a universal open schema for describing physical things,
which will allow:
- Ready-made automation for:
- Generating documentation
- Building distribution packages with precompiled models
- Distribution for use, reuse and remix
- Big built-in library of native functions and transforms:
- Single, perfomant, widely-used standard library instead of everybody reimplemeting the wheel and creating their own ecosystem - better interoperatiblity and reusability of community-made libraries.
- Better performance possible due to optimizations in "native" bindings.
- There's like a ton of cool stuff out there - subdivision, mesh repairing, finding intersections, wall centers. It would even allow this not only to create models but also easily remix existing ones which came from different ecosystems and are available only as single solids.
- Access Geometry Node mesh data for computations what
is impossible in OpenScad by design. We'd need a native
function (
resolve(somenode())
) which would force rendering of the node before it can be utilized in calculations. The possibilities are endless:- Get size of bounding box
- Find center of gravity
- Implement custom advanced transforms?
- Couple cool GUI ideas:
- Click on the preview to jump to code that generated the object under cursor.
- Live preview when adjusting parameters in the graph GUI via sliders and other controls. Can be limited to single node if performance is a concern.
- Language must have namespacing for easier reuse, organization, limiting naming conflicts, overshadowing.
- Model/Project parametrization metadata using JSON-schema and possible JSON-schema forms.
- Generate technical 2D drawings, intersections with measurements for documentation, presentation and modeling.
- Thanks to full metadata could be exported to WYSIWYG modelers
- The code-only GUI could be slowly extended with visual, interactive features.
- Imported libraries packages clearly separated in some ways to not clutter the UI and everything.
- Component libraries can be directly browsed (with visual previews)
with drag'n'drop or other easy mechanism (with editor hints?)
for using them in code. Their docs and parameters could be also
viewed directly from the context.
- User can collect and organize 3rd party packages system-wide and based on usage in particular project dependency metadata generation and update would be (fully or partially) automated.
- Features should be roughly a superset of OpenScad's what would allow to kickstart huge part library by just transpiling high quality open source projects that are already out there.
Question to myself: Since this is already functional/almost declarative - I could scrap this syntax and just use YAML like ansible?
This OpenScad-like syntax is certainly nice, but yaml is interoperable, flexible and there's no need to start with my own lexer and parser from the beginning - just allow js expression-based templating somehow.
Or why not start the easy way and then support both?
Starting with YAML would be like getting a free serialization, data transfer format for free.
Or: XML/HTML similar to react - the modularization and reuse certainly seems like a similar use-case.
num
- any numerical typevec
- a list (array) Among others - used to represent and RGBA color, vector, matrix (list of lists), faces, points, complete polyhedron mesh data, etc.str
- string Can be used likevec
most of the time, however, is stored internally differently and has other uses or limitations.obj
- nested dictionary (associative array, hash) modeled after JSON with the exact same syntax
def subtract(a, b) = a - b;
All variables are really functions and can be defined only once in every scope. Redefining or trying to assing to an existing variable will fail.
{
let int a = 10
let
}
Variables are always limited to scope of the current block, never passed to any outer or inner scope.
They are immutable and cannot be assigned to second time or redeclared. But they can be overshadowed in an inner block any level deeper.
The scope is defined by the body of:
function
module
block
- curly-braced or colon-separated
def rotate_and_translate(vec rot, vec trans, num size = 100) {
rotate(rot = rot) {
translate(trans = trans) {
cube(size = size);
}
}
}
def move_left(int distance = 5):
translate([- distance, 0, 0]):
children();
move_left():
cube(size = 1);
Can be indicated by wrapping statements with curly braces or separating them by colons.
def cubes(): {
translate([10, 0, 0]): {
cube(size = 10)
cylinder(r = 20)
}
rotate([0, 10, 0]): {
cube(size = 5)
}
cylinder(r = 10)
}
def cubes(): {
translate([10, 0, 0]):
cube(size = 10)
}
def cubes():
translate([10, 0, 0]):
cube(size = 10)
def cubes(): {
translate([10, 0, 0]):
cube(size = 10),
cylinder(r = 20)
;
}
def cubes(): {
translate([10, 0, 0]):
cube(size = 10),
cylinder(r = 20), {
pyramid(s = 5, h = 2.5);
}
}
def cubes():
translate([10, 0, 0]):
cube(size = 10),
cylinder(r = 20), mirror([0, -1, 0]): {
union():
pyramid(s = 5, h = 2.5),
pyramid(s = 5, h = 2.5)
}
There is no implicit grouping unless necesarry.
Consider this module relying on children:
def spread(int distance = 5):
for(i = [0: count(children()) - 1]):
translate([- distance * i, 0, 0]):
child(i);
Then this code:
def items(): {
cube(size = 1)
cylinder(radius = 5)
}
spread(distance = 20):
items()
Is equivalent to:
spread(distance = 20): {
cube(size = 1)
cylinder(radius = 5)
}
group() {
cube(size = 1)
cylinder(radius = 5)
}
We'll have two geometries created from four:
hull(): a(), b()
union(): x(), y()