Modeling parts

Make sure you have completed the Quickstart. At this point, you should be able to view your project in the viewer - either Openscad or the web viewer - and have a source code to edit.

In Solid Node, a project is organized in a tree structure, with leaf nodes and internal nodes. Leaf nodes use underlying modelling libraries, namely SolidPython, CadQuery, OpenScad and JScad, to generate solid models — each leaf node is one part. Internal nodes combine children nodes into assemblies and fusions, covered in Combining parts.

Each node implements the render() method. Leaf nodes return an object of the underlying library.

There are four types of LeafNodes, each supporting one underlying technology to create solids:

  • Solid2Node Uses Solid Python 2, which is a python wrapper around OpenScad

  • CadQueryNode Uses CadQuery, a pure python modeler based on OCCT

  • OpenScadNode A wrapper around one OpenScad module

  • JScadNode A wrapper around one JScad module

The Quickstart starts with a Solid2Node example showing a box with a hole. Below are the codes for the same model in each modelling technology.

Solid2Node

The starting structure created by solid new implements a Solid2Node node, which uses solidpython2 to create models. Open root/__init__.py:

from solid_node.node import Solid2Node
from solid2 import cube, cylinder, translate

class DemoProject(Solid2Node):

    def render(self):
        return translate(-25, -25, 0)(
            cube(50, 50, 50)
        ) - cylinder(r=10, h=100)

Rendered with solid export and embedded below:

Note that translate here is a solid2 primitive, applied inside the model. Nodes also have a translate() method of their own, used to position parts in assemblies — that one is covered in Animating with time.

CadQueryNode

The same model can be obtained using CadQuery:

import cadquery as cq
from solid_node.node import CadQueryNode

class DemoProject(CadQueryNode):

    def render(self):
        wp = cq.Workplane("XY")
        cube = wp.box(50, 50, 50)
        hole = wp.workplane(offset=-50).circle(10).extrude(100)
        return cube.cut(hole)

The same box with a hole, this time rendered by CadQuery:

TIP: if you want to use CQ-editor, you can add show_object without conflicting with Solid Node:

if __name__ == '__cq_main__':
    show_object(DemoProject().render())

OpenScadNode

The same model can also be obtained using an OpenScadNode, which is a small python wrapper around an OpenScad module.

from solid_node.node import OpenScadNode

class DemoProject(OpenScadNode):

    scad_source = 'demo.scad'

Create a file root/demo.scad with a module to create the model:

module demo() {
  difference() {
    translate([-25, -25, 0]) {
      cube([50, 50, 50]);
    }
    cylinder(r=10, h=100);
  }
}

And the same model again, driven by the OpenScad module above:

By default the module is expected to have the same name as the file (demo.scadmodule demo()); if it doesn’t, set the module_name property. Arguments passed to the node’s constructor are forwarded to the OpenScad module, so one .scad module can back several parametrized nodes:

class Demo(OpenScadNode):

    scad_source = 'shapes.scad'
    module_name = 'box_with_hole'

demo = Demo(50, hole_radius=10)

JScadNode

Finally, the model can also be obtained using a JScadNode, which similarly to OpenScadNode, it’s a python wrapper around a JScad function.

You need the jscad CLI tool installed in $PATH, and its node dependencies installed in the directory you run solid from.

from solid_node.node import JScadNode

class DemoProject(JScadNode):

    jscad_source = 'demo.js'

Create a file root/demo.js with a module to create the model:

const { square, circle } = require('@jscad/modeling').primitives
const { subtract } = require('@jscad/modeling').booleans
const { extrudeLinear  } = require('@jscad/modeling').extrusions

function main() {
  let outerSquare = square({size: 50 });
  let innerCircle = circle({radius: 10 });

  let shape = subtract(outerSquare, innerCircle);
  return extrudeLinear({ height: 50 }, shape);
}

module.exports = { main }

And the same model once more, rendered by JScad:

Model resolution: the fn property

Internally, every part becomes an STL file, and STLs are made of triangles: circles and holes are approximated by polygons. In OpenScad-derived nodes — Solid2Node and OpenScadNode — the number of segments in that approximation is controlled by OpenScad’s $fn variable, and the default is coarse: a small hole can come out as a hexagon.

Set the fn property on the node to raise the resolution:

class Pointer(Solid2Node):

    fn = 256

CadQuery is not affected — it exports STL files with high precision on its own.

This is mostly invisible while modeling, but it matters for fits: a hexagonal “hole” is tighter than the circle it approximates. It comes back in Test-driven CAD, where a pin fails to run free in a low-resolution hole.

Colors

Any node can set a color, as a hex RGB string, which is used by the viewer and carried into exports:

class Pointer(Solid2Node):

    color = '#cc4444'