Skip to main content

A Python 3D modelling API for generating OpenSCAD source code. This library simplifies the creating of 3D models. This contains only the core API and some generic basic models.

Project description

AnchorSCAD

AnchorSCAD is a Python 3D modeling API for generating OpenSCAD source code. This library simplifies the creation of 3D models and includes a suite of completed models, such as Raspberry Pi cases and others.

AnchorSCAD Quick Start

Author: Gianni Mariani
Published: March 2022 Latest update: April 2025

AnchorSCAD is a Python 3D modeling API for OpenSCAD. This document covers the minimal set of concepts you need to understand in order to build shapes using AnchorSCAD.

It's assumed that you are familiar with the Python programming language, especially classes, inheritance, dataclasses, and decorators.

For more information about AnchorSCAD, follow this link.

For an overview of AnchorSCAD and how to build complex models see html docs - this documents in detail shape operators (solid, hole etc) and how to chain calls to create Makers/model graph node builders.

How do I get set up?

You can follow the installation instructions to install AnchorSCAD and the prerequisite software.

Viewing Models with ad_viewer

AnchorSCAD, through its integration with the latest PythonOpenSCAD library, can now directly generate 3D meshes using the powerful manifold3d library. This means you can visualize your models without needing to install OpenSCAD itself.

The anchorscad.ad_viewer module provides a simple command-line tool to render and view specific shapes, examples, parts, or materials from your AnchorSCAD modules.

Example usage:

python -m anchorscad.ad_viewer --module anchorscad --shape Sphere --material default

This command will:

  1. Import the Sphere shape from the anchorscad module.
  2. Render its default example.
  3. Filter the resulting mesh to only include geometry associated with the material named default.
  4. Open a viewer window displaying the mesh.

You can use the --part and --material arguments to view specific components of complex multi-part/multi-material models. Run python -m anchorscad.ad_viewer --help for more options.

python -m anchorscad.ad_viewer --module anchorscad_models.cases.rpi.rpi5  --shape RaspberryPi5Case

AnchorSCAD Viewer Screenshot

Simple Shape

AnchorSCAD provides tools to wrap your creations in its own Shape class. The example below simplifies to demonstrate how shapes are composed in AnchorSCAD. The code below will print OpenSCAD script text to standard output, rendering a box tube.

AnchorScad example1

# A simple composition example of a box tube using AnchorSCAD.
import anchorscad as ad

# Create a shape that we build upon.
maker = ad.Box([20, 20, 40]).solid('box').at('centre')

# Add a hole shape.
hole = ad.Box([10, 10, 40.001]).hole('hole').at('centre')
maker.add_at(hole, 'centre')

# Render and print the OpenSCAD file for the shape.
print(ad.render(maker).rendered_shape)

This code demonstrates how to compose models as holes and solids. AnchorSCAD also supports other OpenSCAD compositions like intersection, unions, hull, etc.

Things to note from this example:

  • To compose a shape, it must be given a name and a composition mode. In this case, we have two shapes named 'box' and 'hole,' and the compositions are solid() and hole().
  • Once a shape is named, it is also given a frame of reference, becoming a builder object that can have more shapes added. Note that once a builder is added to another builder, it is copied, so subsequent changes to the added builder will not be reflected in the final composition.

Composite Shapes

The following code snippet generates a similar box tube shape as demonstrated in the previous example but as an AnchorSCAD Shape class. This demonstrates AnchorSCAD's "parametric" tools. Running this code will generate a file named "examples_out/anchorcad_SquarePipe_default_example.scad," which can also be imported into other Python programs as shown in the "SquarePipe" class as a reusable shape.

AnchorScad example2

import anchorscad as ad
EPSILON = 1.0e-3

@ad.shape
@ad.datatree  # wrapper over dataclasses
class SquarePipe(ad.CompositeShape):
    '''Pipe with box section consisting of an outer box with an 
    inner box hole.'''
    size: tuple
    wall_size: float = 5.0

    EXAMPLE_SHAPE_ARGS = ad.args((70, 50, 30))

    def build(self) -> ad.Maker:
        maker = ad.Box(self.size).solid('outer').at('centre')
        # Make the inner box slightly larger to stop tearing 
        # when rendered.
        inner_size = (
            self.size[0] - 2 * self.wall_size,
            self.size[1] - 2 * self.wall_size,
            self.size[2] + EPSILON
        )
        maker2 = ad.Box(inner_size).hole('hole').at('centre')
        maker.add_at(maker2, 'centre')
        return maker

MAIN_DEFAULT = ad.ModuleDefault(True)  # Set default for --write
if __name__ == '__main__':
    ad.anchorscad_main()

Note that the build() function is called via the dataclass generated __init__() constructor function. build() must return the final shape (maker) object representing the constructed shape.

While it is possible to use AnchorSCAD without the dataclass decorator, it greatly simplifies the code when it's used and it's highly recommended to use it. AnchorSCAD also extends the functionality of dataclass with the anchorscad.datatree decorator. Datatree is a wrapper over dataclass that provides automated parameter injection and binding, allowing for the composition of many shapes without requiring manual duplication of all the parameters, defaults, and documentation. More information about datatree can be found here.

Use Shape Templates to Create New Shapes

The anchorscad package includes several template files to use as starting points when creating new AnchorSCAD shape modules. These templates contain simple CompositeShape classes demonstrating common patterns.

  • template.py: The base template, showing a simple CompositeShape using a datatree Node (box_node) to build a basic Box.
  • template_with_dt_node.py: Similar to the base template, but explicitly uses ad.ShapeNode typing for the box_node.
  • template_with_extrusion.py: Demonstrates creating a shape based on a 2D path built with ad.PathBuilder and then extruded using ad.LinearExtrude.

When using a template, you should specialize the class name, docstring, fields, and the implementation of the build() function for the shape being coded. It makes sense to keep related shapes within the same Python module.

anchorscad_main()

All AnchorSCAD shape modules should include the following lines at the end of the

module. Running modules containing these lines will render and test the code for each shape class defined in the module.

if __name__ == '__main__':
    ad.anchorscad_main()

By default, ad.anchorscad_main() does not write rendered files. However, if the --write command-line parameter is provided, files for each shape will be generated and placed in the "examples_out" directory in the current working directory. Alternatively, adding a module variable named MAIN_DEFAULT will change options defaults for anchorscad_main() so that files are written by default.

MAIN_DEFAULT = ad.ModuleDefault(True)

ad.anchorscad_main() will also render a graph of the shape hierarchy if the --graph_write parameter is provided or MAIN_DEFAULT set to:

MAIN_DEFAULT = ad.ModuleDefault(True, True, True)

or, to generate all example resources use:

MAIN_DEFAULT = ad.ModuleDefault(all=True)

CompositeShape

The anchorscad.CompositeShape class is the most commonly used base class, as it provides all the properties of an anchorscad.Shape class while also providing a scaffold for creating easy-to-use parameterized shapes with a complex shape hierarchy. In general, it's best to keep the complexity of a single CompositeShape class to a minimum and build a deeper hierarchy of simple CompositeShapes shapes.

EXAMPLE_SHAPE_ARGS

To create example models, the EXAMPLE_SHAPE_ARGS class variable will be used to create a single "default" example when anchorscad_main or the anchorscad_runner is executed. The example below passes a size of (70, 50, 30) to the constructor of the SquarePipe shape when the example is created.

class SquarePipe(ad.CompositeShape):
    ...
    EXAMPLE_SHAPE_ARGS = ad.args((70, 50, 30))

Having anchors rendered on the example shapes also provides a powerful diagnostic tool. It is supported by providing a list of EXAMPLE_ANCHORS to render. Results from the anchorscad.surface_args and inner_args functions can be used in the EXAMPLE_ANCHORS list. (Note: inner_args() will not render an anchor's parameters while surface_args() will).

AnchorSCAD supports multiple additional example shapes using the (unannotated) EXAMPLES_EXTENDED class variable containing a dictionary of "example name" keys with anchorscad.ExampleParams values.

Shape Building

AnchorSCAD has a primary "Shape" type. To use a shape in AnchorSCAD, a name for the shape is required. A name is usually a string literal (str object) but can be any immutable, hashable, repr-izable object, commonly a tuple of str and int if not a simple str object. Providing a "name" and a Mode to a shape results in a "NamedShape," and other optional attributes can be applied, like color, debug, or other inheritable attributes. An anchor can be applied to provide the orientation and position of the shape. A single instance of a shape could be used multiple times with a different name and at() location. Shapes are copied (if mutable) when added to a Maker. Below is an example of a simple Box shape being named and positioned.

AnchorScad example2

A Maker is also an anchorscad.Shape. A Maker, in particular, is a builder of a node in a shape hierarchy, a collection of other Maker objects. Shapes can have 'anchors' that are used to create frames of reference, i.e., Anchors have both position and orientation. The anchors in a Maker are found by looking up the name specified in the anchor with the named shapes in the Maker itself, and then applying the remaining anchor attributes to the resulting entry. There is a special case that if omitted, the first shape need not be named in the anchor, this can occasionally lead to naming ambiguity but is especially useful in preserving anchor validity when adding nodes to the shape hierarchy.

Note that the evaluated form of an anchor is represented as an anchorscad.GMatrix type defined in AnchorSCAD's "linear" module (i.e., a 4x4 homogeneous matrix). When adding another shape, you're actually adding another Maker. The Maker.add_at() allows the specification of where (position + orientation) is placed relative to the shape being modified.

Note the terms "shape" and "model" are used interchangeably since an AnchorSCAD model that consists of composing many "shapes" is itself also an AnchorSCAD "Shape".

An AnchorSCAD shape is a subclass of the anchorscad.Shape class. The constructor parameters are arbitrary and specific to the implemented shape. Most AnchorSCAD models use the Python dataclass or anchorscad.datatree decorators to simplify the generation of Shape classes.

An Hello World / SquarePipe Example

The anchorscad_models.basic.SquarePipe model demonstrates the basic construction of a CompositeShape. The CompositeShape will likely be the most used base class in AnchorSCAD because most shapes are built from other shapes.

The resultant shape will consist of a Box with a smaller Box "hole" aligned at the centers, hence a "square pipe".

AnchorScad example3

import anchorscad as ad
EPSILON = 1.0e-3

@ad.shape
@ad.datatree
class SquarePipe(ad.CompositeShape):
    '''Pipe with box section consisting of an outer box with an 
    inner box hole.'''
    size: tuple = ad.dtfield(doc='Overall size of SquarePipe shape.')
    wall_size: float = ad.dtfield(5.0, 'Wall thickness of SquarePipe shape')

    EXAMPLE_SHAPE_ARGS = ad.args((70, 50, 30))
    EXAMPLE_ANCHORS = (ad.surface_args('face_centre', 5),
                       ad.surface_args('inner', 'face_centre', 2),)

    def build(self) -> ad.Maker:
        maker = ad.Box(self.size).solid('outer').at('centre')
        # Make the inner box slightly larger to stop tearing
        # when rendered.
        inner_size = (
            self.size[0] - 2 * self.wall_size,
            self.size[1] - 2 * self.wall_size,
            self.size[2] + EPSILON
        )
        maker2 = ad.Box(inner_size).hole('hole').at('centre')
        maker.add_at(maker2, 'centre')
        return maker

    @ad.anchor('Inner hole.')
    def inner(self, *args, **kwds):
        # Make Z axis point out in holes.
        return self.maker.at('hole', *args, **kwds) * ad.ROTX_180

MAIN_DEFAULT = ad.ModuleDefault(True)  # Set default for --write
if __name__ == '__main__':
    ad.anchorscad_main()

The build() function (called by the dataclass/datatree generated constructor) after the constructor has populated the instance fields is used to create the composite shape then return the Maker object. The returned shape will have its anchors exposed as the CompositeShape object's anchors.

AnchorSCAD modules should call ad.anchorscad_main() as its main function call. (use the –write command line argument or add a MAIN_DEFAULT=ad.ModuleDefault(True) definition in the module to generate the .scad files in the "examples_out" directory.) When the module is run as a main program, it will identify all shapes decorated with the @ad.shape function and execute and render the shapes with the configured example parameters. These shape python modules can still be imported by other shape python modules to allow for complex multi-python module hierarchical shapes.

Defining shape-specific anchors is done using the @ad.anchor as shown with the "inner(self, *args, **kwds)" function. The function name becomes the anchor name. In this case, "inner" anchor references the 'hole' shape within the maker's frame of reference and rotates along the X-axis so that the resulting surface anchor has the Z-axis pointing out of the shape's surface, which is the AnchorSCAD convention.

The maker.add_at() function is used to anchor a shape at an anchor point in the maker. Chaining .add_at() calls is possible since the return value is the maker object being called, but chaining .add_at() calls is not always desirable.

Running the SquarePipe shape will result in a "physical" file that does not contain anchors i.e. (anchorcad_SquarePipe_default_part-default_physical_example.scad). This file is show below.:

// Start: lazy_union
default_5_default_5();
// End: lazy_union

// Modules.

// 'PartMaterial undef-default - default 5.0'
module default_5_default_5() {
  // 'None : _combine_solids_and_holes'
  union() {
    // '_combine_solids_and_holes'
    difference() {
      // 'default : _combine_solids_and_holes'
      union() {
        // 'outer'
        multmatrix(m=[[1.0, 0.0, 0.0, -35.0], [0.0, 1.0, 0.0, -25.0], [0.0, 0.0, 1.0, -15.0], [0.0, 0.0, 0.0, 1.0]]) {
          // 'outer : _combine_solids_and_holes'
          union() {
            // 'outer'
            cube(size=[70.0, 50.0, 30.0]);
          }
        }
      }
      // 'default'
      multmatrix(m=[[1.0, 0.0, 0.0, -30.0], [0.0, 1.0, 0.0, -20.0], [0.0, 0.0, 1.0, -15.0005], [0.0, 0.0, 0.0, 1.0]]) {
        // 'hole : _combine_solids_and_holes'
        union() {
          // 'hole'
          cube(size=[60.0, 40.0, 30.001]);
        }
      }
    }
  }
} // end module default_5_default_5

Multi-Material and Multi-Part Models

AnchorSCAD now supports Multi-Material and Multi-Part models using OpenScad's experimental lazy-union feature when exporting 3mf files but will also generate STL files for each physical part. All example created models will use materials "default" and "anchor". The anchor part of the model is considered "non physical" and may be excluded from slicing if the part is configured appropriately. See the Multi-Material docs for more information.

See how to keep holes when composing shapes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anchorscad_core-0.2.4.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anchorscad_core-0.2.4-py3-none-any.whl (281.7 kB view details)

Uploaded Python 3

File details

Details for the file anchorscad_core-0.2.4.tar.gz.

File metadata

  • Download URL: anchorscad_core-0.2.4.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for anchorscad_core-0.2.4.tar.gz
Algorithm Hash digest
SHA256 82195a8251ad2f5b3c17f8bacba52c0bb40575beb624d38770bf490e5afd6a02
MD5 92e0768e3e1f2ca21e5d01247cb477ec
BLAKE2b-256 e624348aa0dea5dff69af985356d74721f8816bb5b85f333050d85622e2f74bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for anchorscad_core-0.2.4.tar.gz:

Publisher: publish.yml on owebeeone/anchorscad-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file anchorscad_core-0.2.4-py3-none-any.whl.

File metadata

  • Download URL: anchorscad_core-0.2.4-py3-none-any.whl
  • Upload date:
  • Size: 281.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for anchorscad_core-0.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 e0ca05fee389f3502cf2e6ae269c1eee43aef569f5c0537d82d4f095c84566f1
MD5 79dd4953b25ba28a4b12a19eadb4d50a
BLAKE2b-256 38fe5bd69bda63f084be37f1cfbb179929581d28c380d88c0ef8d04f1d14d62b

See more details on using hashes here.

Provenance

The following attestation bundles were made for anchorscad_core-0.2.4-py3-none-any.whl:

Publisher: publish.yml on owebeeone/anchorscad-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page