Operations

This page describes how to use the various operations provided by the library.

Profile

Profile operations follow wires.

Outer profile

from cq_cam import JobV2
result = cadquery.Workplane("front").box(20.0, 20.0, 5)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .profile(result.wires('<Z'), outer_offset=1)
)
result.objects += cam.to_shapes(as_edges=True)

Inner profile

from cq_cam import JobV2

result = (
    cadquery.Workplane("front")
    .box(20.0, 20.0, 1)
    .faces('>Z')
    .workplane()
    .rect(10, 10)
    .cutThruAll()
)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .profile(result.faces('<Z'), inner_offset=-1)
)
result.objects += cam.to_shapes(as_edges=True)

Tabs

Often when profiling it is not ideal to cut the whole profile all the way to avoid the cutout becoming loose and getting damaged by the milling bit. Tabs are one way to deal with this. Two tabbing algorithms are provided. WireTabs works on a wire and EdgeTabs works on each edge of a wire independently. Because EdgeTabs works on individual edges it spaces the tabs evenly on the edge length and is therefore more likely to generate a more aesthetically pleasing result. WireTabs is more suited for situations where even spacing is critical on the whole wire length.

EdgeTabs

cq_cam.EdgeTabs generates tabs on each edge separately. Edges can be filtered by type (line, circle, arc) and tabs can be placed by count, spacing or manually defined positions.

from cq_cam import JobV2, EdgeTabs
result = cadquery.Workplane("front").box(20.0, 20.0, 5)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .profile(
        result.wires('<Z'),
        outer_offset=1,
        tabs=EdgeTabs(spacing=9, width=2, height=2)
    )
)
result.objects += cam.to_shapes(as_edges=True)

WireTabs

cq_cam.WireTabs generates tabs on a wire. Tabs can be placed by count, spacing or manually defined positions.

from cq_cam import JobV2, WireTabs
result = cadquery.Workplane("front").box(20.0, 20.0, 5)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .profile(
        result.wires('<Z'),
        outer_offset=1,
        tabs=WireTabs(count=8, width=2, height=2)
    )
)
result.objects += cam.to_shapes(as_edges=True)

Pocket

Pockets come in two variants Closed pockets have no open edges so the tool stays always inside the outer boundary. Open pockets may have open sides where the tool needs to travel outside of the outer boundary. These two cases require a slightly different approach but both are fully supported by cq_cam.Pocket. Additionally pocketing needs a toolpath strategy. Two strategies are currently implemented: cq_cam.ZigZagStrategy and cq_cam.ContourStrategy.

Closed pockets and strategies

from cq_cam import JobV2, ZigZagStrategy

result = cq.Workplane("front").box(50.0, 50.0, 2).faces('>Z').workplane().rect(40, 40).cutBlind(-1)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .pocket(
        clearance_height=5,
        top_height=0,
        o=result.faces('<Z[1]'),
        strategy=ZigZagStrategy
    )
)
result.objects += cam.to_shapes(as_edges=True)
from cq_cam import JobV2, ContourStrategy

result = cq.Workplane("front").box(50.0, 50.0, 2).faces('>Z').workplane().rect(40, 40).cutBlind(-1)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .pocket(
        clearance_height=5,
        top_height=0,
        o=result.faces('<Z[1]'),
        strategy=ContourStrategy
    )
)
result.objects += cam.to_shapes(as_edges=True)

Open pockets

Open pockets can be done by increasing the outer_boundary_offset and defining avoid. Avoid prevents the tool from entering the faces listed.

from cq_cam import JobV2

result = cq.Workplane("front").box(20.0, 20.0, 2).faces('>Z').workplane().rect(15, 15).cutBlind(-1).moveTo(0, -10).rect(5, 5).cutBlind(-1)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .pocket(
        clearance_height=5,
        top_height=0,
        o=result.faces('<Z[1]'),
        outer_boundary_offset=1,
        avoid=result.faces('>Z')
    )
)
result.objects += cam.to_shapes(as_edges=True)

Drill

from cq_cam import JobV2
result = cq.Workplane("front").box(20.0, 20.0, 2).faces('>Z').workplane().pushPoints([
    (3, 3), (-5, -8), (0, 0), (5, 2), (7, -3), (-8, 2)]).circle(1).cutThruAll()

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .drill(
        clearance_height=5,
        top_height=0,
        depth=2,
        o=result.faces('>Z').objects[0].innerWires()
    )
)
result.objects += cam.to_shapes(as_edges=True)

3D Surface

3D Surface requires that opencamlib is installed. The package is available in conda but not in pip.

import ocl
from cq_cam import JobV2
result = (
    cq.Workplane('XY').rect(30, 30).extrude(20)
    .faces('>Z').workplane().rect(20, 20).cutBlind(-5)
    .faces('>Z[1]').workplane().rect(10, 10).extrude(3)
    .faces('>Z[1]').fillet(1)
    .faces('>Z[2]').fillet(1)
    .faces('>Z')
)
result.objects = result.objects[0].innerWires()
result = result.fillet(1)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300)
    .surface3d(
        clearance_height=2,
        top_height=0,
        o=result.faces(),
        tool=ocl.CylCutter(3.175, 10),
        interpolation_step=0.1,
        outer_boundary_offset=0
    )
)
result.objects += cam.to_shapes(as_edges=True)

Other features

Multiple depths

A common feature is the need to perform an operation in multiple stepdown depths. Most operations support this feature.

from cq_cam import JobV2, EdgeTabs

result = cadquery.Workplane("front").box(20.0, 20.0, 5)

job_wp = result.faces('>Z').workplane()
cam = (
    JobV2(top=job_wp.plane, feed=300, tool_diameter=1)
    .profile(
        result.wires('<Z'),
        outer_offset=1,
        stepdown=2,
        tabs=EdgeTabs(spacing=9, width=2, height=4)
    )
)
result.objects += cam.to_shapes(as_edges=True)