c2d-tool is a command-line utility for programmatically modifying Carbide Create (.c2d) project files. Its primary purpose is to automate the workflow of updating vector data within existing project files, such as replacing specific layers with content from DXF files.
c2d-tool [OPTIONS] C2D_FILE [OPERATIONS]...Replace layer content: Clear an existing layer and import new vectors from a DXF file.
c2d-tool project.c2d \
--clear-layer "Profile Cut" \
--import-to-layer "Profile Cut" "shapes.dxf"Update multiple layers:
c2d-tool project.c2d \
--clear-layer "Pockets" --import-to-layer "Pockets" "pockets.dxf" \
--clear-layer "Drill" --import-to-layer "Drill" "holes.dxf"Inspect file contents:
c2d-tool project.c2d --list-layers --list-filesExport embedded assets:
c2d-tool project.c2d --export-file "background.png" "extracted_bg.png"C2D_FILE: Path to the input.c2dfile.-o, --output <filename>: Path to write the modified file. If omitted, the input file is modified in-place (a backup is created by default unless--no-backupis used).--no-backup: Disable automatic backup creation when modifying in-place.-v, --verbose: Enable verbose output.--fail-on-duplicate-layer: Exit with an error if an operation targets a layer name shared by multiple layers.
--list-layers: List all vector layers found in the project.--list-files: List all embedded files in thesqlararchive (images, previews, etc.).
Operations are generally performed in the order they appear, but clear-layer operations for a specific layer are prioritized before import-to-layer for the same layer to ensure clean replacement.
--rename-layer <old_name_or_uuid> <new_name>: Renames a layer. Supports targeting by name or UUID.--clear-layer <layer_name>: Removes all vector elements associated with the specified layer. The layer itself is preserved.--delete-layer <layer_name>: Completely removes the layer and all its associated vectors.--delete-unused-layers: Deletes all layers that are not referenced by any toolpath.--create-layer <layer_name>: Creates a new empty layer if it does not exist.--import-to-layer <layer_name> <dxf_file>: Parses the specified DXF file and imports supported entities (LINES, POLYLINES, CIRCLES, ARCS) into the target layer. If the layer does not exist, it will be created.--min-closed-area-mm2 <value>: Sets the minimum area threshold (mm²) for importing closed DXF shapes. Closed shapes smaller than this value are skipped during import. Default:0.1.
--set-param <key> <value>: Sets a specific project parameter (e.g.,--set-param display_mm 0).--resize-to-fit-layer <layer_name>: Updates the projectwidthandheightparameters to ensure they are large enough to contain all elements on the specified layer. (Assumes bottom-left origin).
--export-file <internal_name> <output_path>: Extracts a file from the internalsqlararchive to the local filesystem.--dump-file <internal_name>: Prints the content of an internal file to stdout.
--list-toolpaths: List toolpath groups and their toolpaths (type, tool number, depths, layers).
Whole Carbide Create jobs can be generated without a template. Everything written matches what Carbide Create (build 815) saves itself; see docs/C2D-FORMAT.md §8 and §11.
from c2d_tool.c2d import C2DFile
with C2DFile.create("front-setup1.c2d") as job: # blank native job
job.set_stock(width=300, height=300, thickness=12.7,
material="Hardwood", zero_x=0, zero_y=0, zero_z="top")
outline = job.create_layer("outline", color=(255, 0, 0))
job.import_dxf(outline, "outline.dxf")
job.import_dxf("holes", "holes.dxf") # creates the layer
setup = job.create_toolpath_group("Setup 1")
job.drill("Holes", group=setup, layers="holes", tool=326,
end_depth="t+0.1mm", drill_type=1, peck_distance=1.587)
job.pocket("Cell pockets", group=setup, layers=[outline], tool=320,
start_depth=0, end_depth="t-5mm")
job.advanced_vcarve("Walls", group=setup, layers=[outline],
tool=301, tool_pocket=320, end_depth=2.5)
job.contour("Cutout", side="outside", group=setup, layers=[outline],
tool=320, end_depth="t+0.1mm",
stepdown=3, stepover=3.175,
speeds={"feedrate": 3000, "plungerate": 203.2, "rpm": 18000})C2DFile.create(path, template=None, overwrite=False)writes a blank job: native schema, default params, an emptymodelitem and thenotes.txt/debug.txtmetadata entries. Passtemplate=a native.c2dto take the schema, params and metadata from it instead (it is opened read-only).set_stock(width, height, thickness, material, zero_x, zero_y, zero_z)writes params as TEXT.zero_zis measured from the top:0/"top", or-thickness/"bottom".create_layer(name, color=(r, g, b)),import_dxf(layer, dxf_path).create_toolpath_group(name, enabled=True).add_toolpath(type, name, group, layers, tool, start_depth, end_depth, stepdown, stepover, speeds, enabled=True, **type_fields)is the generic form; the typed helpers below call it.contour(name, side="inside" | "outside" | "on", ...)(stored as the native, misspelledofset_dir: -1 / 1 / 0).pocket(name, ...).drill(name, drill_type=0, peck_distance=3, ...).advanced_vcarve(name, tool=..., tool_pocket=..., speeds_pocket=, stepdown_pocket=, stepover_pocket=, pocket_enabled=True, ...)writes the native pair of same-named items (link_type 2 = pocket pass, 1 = V pass, sharing onelink_uuid), or one item with link_type 0 whenpocket_enabled=False.
- Depths are numbers (mm, stored as
"4.700") or Carbide expressions passed through untouched:"t+0.1mm","t-5mm","t/2"(t= stock thickness). groupis a group dict, uuid or name (a missing name is created);layersare layer dicts, names or uuids. Toolpaths select vectors throughtoolpath_layers;elementsstays empty.toolis a full tool dict, a tool number from the tool library, or a(number, name)tuple when a number is shared by several tools. Omittedspeeds,stepdownandstepovercome from the library's typical values for that tool and toolpath type.- Unknown type-specific fields raise
ValueError, so a typo can't produce a field Carbide Create silently ignores. num_toolpathsandactive_layerare kept consistent as layers and toolpaths are added.
c2d_tool/data/tools.json holds the full tool definitions (keyed by tool
number and name) and the feeds actually used with them, extracted from
existing jobs. It includes the Amana #320 51404-K, #326 46200-K and #321
51411-K bits and the #56125 roundover.
from c2d_tool.tools import ToolLibrary
lib = ToolLibrary.default() # or ToolLibrary.load("tools.json")
lib.tool(320) # native tool dict
lib.typical(320, toolpath_type="contour") # {"speeds": {...}, "stepdown": 3, "stepover": 3}
lib.get(10, name='1/4" Compression End Mill') # numbers can be ambiguousRebuild or extend it from any jobs (they are opened read-only):
python -m c2d_tool.tools extract -o tools.json a.c2d b.c2d
python -m c2d_tool.tools list tools.jsonThe importer currently supports the following DXF entities:
- LINE: Converted to open paths.
- LWPOLYLINE / POLYLINE: Converted to open or closed paths.
- CIRCLE: Converted to circle elements.
- ARC: Converted to open curved paths.
- Polyline bulges and arcs become cubic Bézier segments, with each segment's
controls stored on the point where it ends (
cp1[i],cp2[i]), as Carbide Create does. - SPLINE: (Planned) Approximated as polyline paths.
python -m pytest- Python 3.8 or higher
pip(Python package installer)
-
Navigate to the
cc-tooldirectory. -
Create and activate a virtual environment (recommended):
python3 -m venv .venv source .venv/bin/activate # On Windows use: .venv\Scripts\activate
-
Install the package:
pip install -e .
Once installed, you can run the tool directly:
c2d-tool --helpOr run it as a module:
python -m c2d_tool.main --helpThis project is licensed under the CC0 1.0 Universal text - see the LICENSE file for details.