Make your own Part

A Part is a blueprint for an atomic solid to be placed into an Assembly.

Before continuing here, it’s highly recommend you read Part vs Assembly and Parametric Object.

Let’s start with a simple variably sized rectangle.

import cadquery
import cqparts
from cqparts.params import *
from cqparts.display import display

class Box(cqparts.Part):
    length = PositiveFloat(10, doc="box length (along x-axis)")
    width = PositiveFloat(10, doc="box width (along y-axis)")
    height = PositiveFloat(10, doc="box height (along z-axis)")

    def make(self):
        return cadquery.Workplane('XY').box(
            self.length, self.width, self.height,
            centered=(True, True, False),
        )
box = Box(height=5)
display(box)

display will render the cadquery.Workplane instance pulled from box.local_obj.

Tip

Remember, your Part is a Python class, so you can take advantage of everything Python has to offer.

Adding Mates

A Mate defines a coordinate system relative to the Part origin.

A mate is used by an Assembly to help place parts. More on this in the Make your own Assembly chapter.

A mate is often best defined by a Part because they’re placement is highly dependent on the part’s parameters.

So to elaborate on the box example above:

from cqparts.constraint import Mate
from cqparts.utils import CoordSystem

class Box(cqparts.Part):
    # ... (content as above)
    @property
    def mate_top(self):
        return Mate(self, CoordSystem(origin=(0, 0, self.height)))

box = Box()
box.mate_top

This will return a mate placed in the middle of the boxes top face.

But wait… cadquery offers another way to achieve the same result:

class Box(cqparts.Part):
    # ... (content as above)
    @property
    def mate_top(self):
        plane = self.local_obj.faces(">Z").workplane().plane
        return Mate(self, CoordSystem.from_plane(plane))

box = Box()
box.mate_top

In this case, the first is probably the best, due to its simplicity.

However, being able to query the part’s geometry to find mating points is a powerful feature that can be exploited.

Tip

To reiterate, these are just Python classes, the above mate_top method can be called anything.

You could also create a method that takes a parameter such as get_mate_top(twist_angle=0) to adjust the mate in a way that’s relevant to the part.

Some advice though, don’t make your part behave too differently to other parts you’re using, it will make your code confusing.

Modifying Geometry

You may want to use an existing part, and modify it without copying the code.

Let’s try a few examples of ways you can achieve this effectively.

Inherit : Cut a Hole

Let’s re-invent the wheel:

class Wheel(cqparts.Part):
    radius = PositiveFloat(100, doc="wheel's radius")
    width = PositiveFloat(10, doc="wheel's width")
    def make(self):
        return cadquery.Workplane('XY') \
            .circle(self.radius).extrude(self.width)
wheel = Wheel()
display(wheel)

However, we want to put this onto an axel, so we need to cut a hole through the center.

So let’s make our own wheel, using this wheel as a base.

We inherit the above Wheel, then in the make method we get the original object, then return a modified version:

class HolyWheel(Wheel):
    hole_diameter = PositiveFloat(20, "diameter for shaft")
    def make(self):
        obj = super(HolyWheel, self).make()
        return obj.faces(">Z").circle(self.hole_diameter / 2) \
            .cutThruAll()
my_wheel = HolyWheel(hole_diameter=50, width=15)
display(my_wheel)

The wheel was also made a bit wider to show the inherited parameters are still in effect.

Use an Instance : Union 2 Parts

Now you want 2 wheels connected by an axel, all in 1 (injection moulded) part.

Each wheel (left and right) can have their own radius, and width.

We can’t use inheritance for this example because we need to instantiate 2 different wheels, then union them:

class JoinedWheel(cqparts.Part):
    # Parameters
    l_radius = PositiveFloat(100, doc="left wheel's radius")
    l_width = PositiveFloat(10, doc="left wheel's radius")
    r_radius = PositiveFloat(100, doc="right wheel's radius")
    r_width = PositiveFloat(10, doc="right wheel's radius")
    axel_length = PositiveFloat(100, doc="axel length")
    axel_diam = PositiveFloat(10, doc="axel diameter")

    def make(self):
        # Make the axel
        obj = cadquery.Workplane('XY') \
            .circle(self.axel_diam / 2) \
            .extrude(self.axel_length)
        # Make the left and right wheels
        wheel_l = Wheel(radius=self.l_radius, width=self.l_width)
        wheel_r = Wheel(radius=self.r_radius, width=self.r_width)
        # Union them with the axel solid
        obj = obj.union(wheel_l.local_obj)
        obj = obj.union(
            wheel_r.local_obj.mirror('XY') \
                .translate((0, 0, self.axel_length))
        )
        return obj

joined_wheel = JoinedWheel(
    r_radius=80, l_width=20, axel_diam=30,
)
joined_wheel.local_obj
display(joined_wheel)

Note that we’re instantiating 2 Wheel classes, and using their local_obj attributes to union with the axel.

Rendering

In the above examples, you can see we’re using display() from the cqparts.display module.

When parts are displayed or exported, their colour, transparency, among other attributes are stored with the Part instance.

This detail is stored in a hidden property called _render.

We can change a Part default render properties by replacing the default _render property with our own using render_props().

from cqparts.display import render_props

class Box(cqparts.Part):
    _render = render_props(template='red', alpha=0.2)
    def make(self):
        return cadquery.Workplane('XY').box(10,10,10)

box = Box()
display(box)

Gives us a red appearance with an 80% transparency.

Have a read of Rendering for more details on rendering properties, and other rendering methods.

Further Reading

To learn more about how a Part works, read Part Build Cycle.

Browse the cadquery documentation to learn about how to create geometry.

If you’re comfortable with the above content, you may want to move to learn about how to create an Assembly in Make your own Assembly.