A Python package for parametric 3D modeling of keyboard keycaps.
Project description
Capistry
A Python package for parametric 3D modeling of keyboard keycaps using build123d.
Table of Contents
- Overview
- Installation
- Quick Start
- Features
- Documentation
- Examples
- License
- Contributing
- Acknowledgments
Overview
- Parametric Design: Create custom keycaps with precise control over dimensions, angles, and styling.
- Shapes: Rectangular, slanted, skewed, and trapezoidal keycap shapes.
- Stems: Compatible with MX and Choc switches.
- Advanced Geometry: Tapers, fillets, and surface modeling.
- Batching: Generate panels of multiple keycaps for 3D printing.
- Exporting: Export to any format supported by build123d, including STL, STEP, DXF, SVG, and more.
- Ergogen: Export
Ergogenconfigurations consisting of keycap positions and outlines. - Extensible: Designed to be extensible - you can create custom stems, cap classes, fillet strategies, and sprue designs by extending the base classes.
Installation
pip install capistry
Requirements
- Python 3.13+
- Dependencies:
- build123d
- rich
- mashumaro
- more-itertools
- attrs
- tzdata
Quick Start
from build123d import *
from capistry import *
import logging
# Initialize logging
init_logger(level=logging.INFO)
# Create a basic rectangular keycap
cap = RectangularCap(
width=18,
length=18,
height=5,
wall=1.25,
roof=1.25,
taper=Taper.uniform(7),
stem=MXStem(center_at=CenterOf.GEOMETRY),
fillet_strategy=FilletUniform()
)
# Export as STL
export_stl(cap.compound, "keycap.stl")
# Create a sprued grid-like panel for 3D printing
panel = Panel(
items=[
PanelItem(cap, quantity=10, mirror=True),
],
sprue=SprueCylinder(),
)
# Export panel as STL
export_stl(panel.compound, "panel.stl")
[!TIP] You can use
ocp-vscodeto preview your parts in VS-Code during development.
Features
Keycap Shapes
As of now, all keycap shapes are quadrilaterals, i.e. four-sided polygons.
RectangularCap
Standard rectangular keycap:
cap = RectangularCap(width=18, length=18, height=5)
SlantedCap
An asymmetric keycap where two sides are angled evenly away from their respective orientations. The resulting shape has two orthogonal (90°) corners, one corner at 90 + v° and another at 90 – v°, where v is the slant angle.
cap = SlantedCap(width=18, length=18, height=5, angle=7)
SkewedCap
Keycap with a parallelogram shape:
cap = SkewedCap(width=18, length=18, height=5, skew=5, skew_width=True)
TrapezoidCap
Keycap with a trapezoidal shape:
cap = TrapezoidCap(width=18, length=18, height=5, angle=5)
Stems
Support for different stems:
# MX-style stem
stem = MXStem()
# Choc-style stem
stem = ChocStem()
cap = RectangularCap(stem=stem)
Tapering
Control the slope of keycap sides:
# Uniform taper on all sides
taper = Taper.uniform(7)
# Side-specific tapering
taper = Taper(front=10, back=7, left=4, right=4)
cap = RectangularCap(taper=taper)
Fillet Strategies
Choose how edges are rounded:
# Uniform filleting
fillet_strat = FilletUniform()
# Sides-first filleting
fillet_strat = FilletSidesFirst()
# Sides-Last filleting
fillet_strat = FilletSidesFirst()
cap = RectangularCap(fillet_strategy=fillet_strat)
[!WARNING] Due to the nature of CAD modeling, some fillet configurations simply won't be compatible with every combination of cap, taper and surface due to geometric constraints. If you run into issues building a certain cap, try to adjust these parameters to resolve the problem.
FilletUniformtends to be the least error-prone strategy.
Surface Modeling
The top face of keycaps can be precisely modeled by defining a Surface. A Surface is represented by a matrix (a 2-dimensional list), specifying the offset values which will be used to model the top face. Optionally, a weights matrix may also be supplied.
surface = Surface(
[
[4, 4, 4, 4],
[2, -1, -1, 2],
[0, -1, -1, 0],
[0, 0, 0, 0],
]
)
cap = TrapezoidCap(surface=surface)
Comparisons
You can compare caps, fillets, stems, and surfaces using the capistry.Comparer class. This is useful for identifying both dimensional differences and property variations.
# Create two keycaps with different parameters
c1 = RectangularCap(width=18, length=18, height=5)
c2 = RectangularCap(width=19*2 - 1, length=18, height=5)
# Create a comparer
comparer = Comparer(c2, c2)
# Show the comparison, outputs a rich table in the console
comparer.show(show_deltas=True)
Panel Generation
Create grid-like panels of keycaps for simpler 3D printing by creating a capistry.Panel:
cap = RectangularCap()
# Create a 4x4 panel of a keycap
panel = Panel(
items=[PanelItem(cap, quantity=16)],
sprue=SprueCylinder(),
cols=4
)
# Export the entire panel
export_stl(panel.compound, "keycap_panel.stl")
Ergogen Export
Export your keycaps in their current locations to an Ergogen configuration file.
# Create two rectangular keycaps
c1 = RectangularCap()
c2 = RectangularCap()
# Position the second keycap to the right of the first
c2.locate(c1.top_right)
# Create an ergogen instance, and write the configuration to a YAML file
ergogen = Ergogen(c1, c2)
ergogen.write_yaml("config.yaml", precision=3)
Documentation
Full API documentation is available and generated using pdoc.
👉 View the full documentation here
Examples
For more detailed examples, see the examples/ directory in the repository.
Development
Building from Source
git clone https://github.com/larssont/capistry.git
cd capistry
uv sync
License
This project is licensed under the Mozilla Public License 2.0 - see the LICENSE.md file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Acknowledgments
- build123d — CAD library powering Capistry
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file capistry-0.1.0.tar.gz.
File metadata
- Download URL: capistry-0.1.0.tar.gz
- Upload date:
- Size: 69.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53795778e1b6ca4a3c1ec26907b2d6b7b7c5778284f2c5093a3068cc3006a24d
|
|
| MD5 |
ac2c9f7b16b835f88550d6a7a5ed36db
|
|
| BLAKE2b-256 |
33cc44c038b919cac4707aebd7f311775607710d7db0637257798ec2b4a3f6fc
|
Provenance
The following attestation bundles were made for capistry-0.1.0.tar.gz:
Publisher:
publish.yml on larssont/capistry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capistry-0.1.0.tar.gz -
Subject digest:
53795778e1b6ca4a3c1ec26907b2d6b7b7c5778284f2c5093a3068cc3006a24d - Sigstore transparency entry: 235649984
- Sigstore integration time:
-
Permalink:
larssont/capistry@fc3848f5f6362bde6ebe346a63f7d777bd306560 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/larssont
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc3848f5f6362bde6ebe346a63f7d777bd306560 -
Trigger Event:
release
-
Statement type:
File details
Details for the file capistry-0.1.0-py3-none-any.whl.
File metadata
- Download URL: capistry-0.1.0-py3-none-any.whl
- Upload date:
- Size: 75.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f348c7143003d7017f3cc488608b7e81b1b45ce778f0f5a2675a296fafe6d98
|
|
| MD5 |
cd893c5900f032fb0ae042fa0f6d72fb
|
|
| BLAKE2b-256 |
56cc46d3f9cbac7c79dc7b178cee3d6aaeb421bceebac7f68e16d90c6658930f
|
Provenance
The following attestation bundles were made for capistry-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on larssont/capistry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capistry-0.1.0-py3-none-any.whl -
Subject digest:
7f348c7143003d7017f3cc488608b7e81b1b45ce778f0f5a2675a296fafe6d98 - Sigstore transparency entry: 235649987
- Sigstore integration time:
-
Permalink:
larssont/capistry@fc3848f5f6362bde6ebe346a63f7d777bd306560 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/larssont
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc3848f5f6362bde6ebe346a63f7d777bd306560 -
Trigger Event:
release
-
Statement type: