DeclaraCAD

A declarative parametric 3D modeling program built using Open Cascade. Allows you to build and edit 3D models in a simple and easy to learn language that extends python.


What is DeclaraCAD?

DeclaraCAD is a language and an application that let's you build 3D models using a very powerful subset of the python language called enaml. It brings the power of modern programming techniques to 3D modeling. Models are defined declaratively and properties are parametric. Model structure can be dynamically generated based on conditions, arrays, and other expressions (such as functions or data models). This enables you to very quickly build dynamic and sophisticated models that are still easy to read, manipulate, and extend.

Models are Declarative

Models are defined declaratively using shapes (cube, cylinder, sphere, etc..), operations (cut, fillet, extrude, etc..), and drawings. They are all nested as required to build the desired shape.

from enaml.core.api import Looper
from occ.shape import Box, Sphere
from occ.algo import Cut
from occ.part import Part

enamldef TurnersCube(Part):
    name = "Turners Cube"

    attr levels: int = 3
    Looper:
        iterable << range(1,1+levels)
        Cut:
            Box:
                position = (-loop_item/2.0,-loop_item/2.0,-loop_item/2.0)
                dx = loop_item
                dy = loop_item
                dz = loop_item
            Sphere:
                radius = loop_item/1.5
Parts are reusable

Making a new Part is as simple as defining it in a file, importing, and using it. It uses the same syntax as python. Allowing you to build complete libraries of parts and group them into packages.

from occ.part import Part
from toolbox.basic import Screw, Nut, Washer


enamldef Assembly(Part):
    Screw:
        dy = 10
        r = 1
    Washer:
        x = 4
        dx = 10
        r = 1
    Nut:
        x = 9
        dx = 10
        r =1
Properties are Parametric

Properties of models are parametric. You can define relations to other shapes and specify any equation that is evaluated to define a property.

from occ.shape import Cylinder, Prism, Face
from occ.draw import Segment, Arc, Wire, Point
from occ.algo import Fuse, ThickSolid, Fillet, Transform
from occ.part import Part

enamldef Bottle(Part): part:
    name = "Bottle"

    #: "Parametric" properties of this shape
    attr height = 7.0
    attr width = 5.0
    attr thickness = 3.0

    #: Actual shape
    ThickSolid:
        color = '#333333'
        # Hollows out the bottle
        #closing_faces << [neck.shape_faces[0]]
        offset << thickness/50.0
        Fuse:
            # Fuse the bottle to the neck
            Cylinder: neck:
                # Bottle neck
                position << (0,0,part.height)
                direction = (0,0,1)
                radius << thickness/4.0
                height << part.height/10.0
            Fillet: bottle:
                # Bottle, with filleted edges
                radius << thickness/12.0
                Prism:
                    # Create a solid from the bottle face
                    vector << (0,0,height)
                    Face:
                        # Create a face from the base profile
                        Wire:
                            # Create a wire from the profile and mirrored profile
                            Wire: profile:
                                Segment:
                                    Point: p1:
                                        position << (-width/2.0, 0, 0)
                                    Point: p2:
                                        position << (-width/2.0, -thickness/4.0, 0)
                                Arc:
                                    Point:
                                        position := p2.position
                                    Point: p3:
                                        position << (0, -thickness/2.0, 0)
                                    Point:
                                        position := p5.position

                                Segment:
                                    Point: p4:
                                        position << (width/2.0, 0, 0)
                                    Point: p5:
                                        position << (width/2.0, -thickness/4.0, 0)
                            Transform:
                                #: TODO coerce
                                mirror = ((0,0,0),(1,0,0))
                                shape = profile