Skip to main content

pip-hinge

A parametric print-in-place piano hinge in build123d, designed for clamshell cases.

clamshell with HALF knuckle and corner magnet pockets, flat-open print orientation

Start with four main inputs; the parameter table covers the remaining fit and geometry controls.

from pip_hinge import Knuckle, PinStyle, PrintInPlaceHinge

hinge = PrintInPlaceHinge(
    case_h        = 10,             # case wall height (mm)
    hinge_length  = 60,             # total hinge length along the axis (mm)
    stations      = 6,              # alternating tab count (even, ≥ 2)
    knuckle       = Knuckle.FULL,   # FULL = "bump on top", no ramp needed
)
base_leaf, lid_leaf = hinge.cylinder_side, hinge.pin_side

The default pin has 45° conical tips. Choose PinStyle.ROUNDED or the experimental PinStyle.BRIDGED with the pin_style argument when needed.

PrintInPlaceHinge is a build123d Compound with two labelled children, one per leaf:

  • cylinder_side — the leaf whose knuckle tabs carry the bores;
  • pin_side — the leaf with the end caps and the captured pin.

(Internally these are abbreviated cs and ps.) Each child is a Compound, because a bare leaf can be several solids — see mounting_flat below.

make_hinge(HingeParams(...)) builds the same thing from a reusable parameter value.

Where the hinge comes out

The hinge is built flat-open, in print orientation, ready to fuse into a case whose walls stand on the bed:

  • bed at Z = 0, wall top at Z = case_h;
  • hinge axis along Y through X = 0, Z = case_h + pivot_z_offset;
  • cylinder_side reaches X = +hinge.leaf_width, pin_side X = −hinge.leaf_width — those outer faces are where the two case back walls go;
  • Y spans ±hinge_length / 2.

So for a base whose back wall's outer face is at X = x0, place the leaves (build123d children are relative to their parent, so move the leaves themselves, not the hinge):

from build123d import Pos

at = Pos(x0 - hinge.leaf_width, y_centre, 0)
base = base + at * hinge.cylinder_side
lid = lid + at * hinge.pin_side

Joints

cylinder_side.joints["pivot"] is a RevoluteJoint on the hinge axis and pin_side.joints["pivot"] a matching RigidJoint, so the hinge can be swung in an assembly view (0° = flat-open as printed, 180° = closed):

hinge.cylinder_side.joints["pivot"].connect_to(hinge.pin_side.joints["pivot"], angle=180)

Each leaf also has a "mount" RigidJoint at the bottom centre of its outer face (X = ±leaf_width, Y = 0, Z = 0), axes aligned with the hinge frame. To attach the hinge to a case wall with joints instead of Pos:

from build123d import Location, RigidJoint

RigidJoint("hinge", base, Location((x0, y_centre, 0)))    # back-wall outer face, bottom centre
base.joints["hinge"].connect_to(hinge.cylinder_side.joints["mount"])
hinge.cylinder_side.joints["pivot"].connect_to(hinge.pin_side.joints["pivot"], angle=0)
base = base + hinge.cylinder_side                         # leaves are now in place
lid = lid + hinge.pin_side

In context: a flat-open clamshell with HALF knuckle

Built by examples/clamshell.py — case_h = 10mm, 80 × 50 mm footprint, 60 mm hinge with Knuckle.HALF, plus four 6 × 3 mm corner magnet pockets to latch the case shut. Both halves print as one piece in the orientation shown. The example also emits a bare HALF/FULL variant (no magnets) for reference.

Quick start

Install version 0.3.0 from PyPI with uv add "pip-hinge>=0.3.0". Earlier releases do not include PinStyle or FitProfile. In a checkout, install it for development with:

uv venv
uv pip install -e .

Then in your build123d code:

from pip_hinge import Knuckle, PrintInPlaceHinge

hinge = PrintInPlaceHinge(case_h=10, hinge_length=60, knuckle=Knuckle.FULL)
base = my_base + hinge.cylinder_side
lid = my_lid + hinge.pin_side

To run the included examples from a checkout:

uv run python examples/clamshell.py          # writes clamshell_{full,half,small,magnets}.{step,stl}
uv run python examples/hinge_only.py         # writes the bare hinge_{full,half}.{step,stl}
uv run python examples/conical_test_print.py --fit tighter  # two-size print plate with tighter fit

Geometry at a glance

parameters guide

Cross-section (Panel A) shows the spatial parameters: case_h (wall height), pivot_z_offset (extra lift), mounting_flat (flat past the disc edge), plus the derived Po/Ro/T/W and the pin/bore inset. Top view (Panel B) shows hinge_length, stations, derived station pitch (hinge_length / stations), and clasp_clearance between tabs.

Knuckle sizes

knuckle options

knuckle knuckle diameter ramp gap between case walls (flat-open)
Knuckle.FULL 2 × (case_h + pivot_z_offset) none — rests on bed 2 × (case_h + pivot_z_offset) + 2 × mounting_flat
Knuckle.HALF case_h + pivot_z_offset 45° self-supporting teardrop case_h + pivot_z_offset + 2 × mounting_flat
Knuckle.SMALL max((case_h + pivot_z_offset) / 2, 5 mm) ~25° from vertical (smaller knuckle → naturally steeper) max((case_h + pivot_z_offset) / 2, 5 mm) + 2 × mounting_flat

The knuckle diameter Po is sized to the lifted axis height case_h + pivot_z_offset, and the flat-open gap between the case walls is always Po + 2 × mounting_flat (= 2 × hinge.leaf_width). For case_h = 10 with the defaults that is 21.4 mm (FULL), 11.2 mm (HALF) and 6.1 mm (SMALL).

See docs/clamshell-integration.md for mounting, orientation, multi-hinge layouts, and the closed-vs-open view.

Parameters

The four primary inputs:

Parameter Default Meaning
case_h (required) Height in mm of the case wall each leaf attaches to
hinge_length (required) Total hinge length in mm along the rotation axis (Y)
stations 6 Even count of tab positions along Y, at least 2. Each end cap occupies half a position; 4, 6, 8, … are supported
knuckle Knuckle.FULL FULL, HALF, or SMALL — see the option table below

stations counts alternating tab positions, not pin pairs or bored tabs. For example, four stations make two bored cylinder-side tabs, one full pin-side middle tab, and two half-width pin-side end caps. Any even count of at least two is accepted; the usable count also depends on tab width.

Fit and pin options:

Parameter Default Meaning
pin_style PinStyle.CONICAL CONICAL: 45° tapered pin tips in matching tapered bores; ROUNDED: original hemispherical tips in round bores; BRIDGED: one continuous cylindrical pin in a teardrop-roof bore. Both revised bores released in one small clamshell print; the bridged hinge rocked noticeably
knuckle_wall None Radial material thickness around the bore at its widest round section. None gives CONICAL at least 1.0 mm of modeled wall and grows the pin where station spacing permits. BRIDGED preserves at least 1.0 mm above its higher roof. Check the actual perimeter count in your slicer; set a value in mm to override
mounting_flat 0.5 Width in mm of the flat leaf strip beyond the knuckle where the case wall joins. At or below the resolved pivot_clearance the bare leaf can contain separate solids that join when fused to the wall
pivot_clearance None Difference in mm between bore and pin diameters at the straight shank. The gap there is half this value; CONICAL also keeps that gap normal to its sloped tip. None selects 0.6 mm with STANDARD or 0.4 mm for small conical hinges with TIGHT
pivot_z_offset 0.2 Lift of the hinge axis above the wall top. When closed, the lid then rests 2 × pivot_z_offset above the base instead of meeting it on a zero-tolerance plane, so a high spot along the seam can't spring the front of the case open. Only the knuckle is raised — the leaves stay flush with the wall top. 0 disables it; must be less than the knuckle radius
clasp_clearance None Gap in mm along Y between neighbouring tab faces. None selects the fit profile's value; an explicit value overrides it
fit_profile FitProfile.STANDARD STANDARD retains 0.3 mm radial and axial gaps. TIGHT gives small conical hinges the narrower gaps that worked in the earlier cylindrical-bore and the small shaped-bore trial

Tighter fit for small conical hinges

The two-size conical test plate printed and released with the standard 0.3 mm gaps, but both lids rocked around the pin and the smaller case also slid along the hinge axis. The tighter trial printed well on that printer. Both printed plates used the earlier cylindrical bore. The new shaped-bore trial also released and felt good on that printer. Select the tighter gap values with:

from pip_hinge import FitProfile, Knuckle, PrintInPlaceHinge

hinge = PrintInPlaceHinge(
    case_h=6, hinge_length=24, stations=4, knuckle=Knuckle.SMALL,
    fit_profile=FitProfile.TIGHT,
)
Profile and geometry Radial pin gap Axial tab gap
STANDARD (all hinges) 0.3 mm 0.3 mm
TIGHT: conical SMALL, barrel diameter ≤ 5.5 mm, station pitch ≤ 6 mm 0.2 mm 0.2 mm
TIGHT: conical SMALL, barrel diameter ≤ 5.5 mm, station pitch > 6 mm 0.2 mm 0.3 mm
TIGHT: other geometry 0.3 mm 0.3 mm

Station pitch is hinge_length / stations. The straight-shank radial gap and conical-tip normal gap are half of pivot_clearance, so 0.2 mm means pivot_clearance=0.4. Passing pivot_clearance or clasp_clearance explicitly overrides that part of the profile. Fit still depends on the printer, material, and slicer settings; try a small test print before committing to a large case. The successful prints reported here were made on one Bambu Lab P1S. Material, nozzle diameter, line width, layer height, and slicer profile were not recorded. These results do not establish separate clearance recommendations for 0.4 mm and 0.6 mm nozzles; use the small Python trial to check a different setup.

Pin shapes and print tradeoffs

CONICAL uses short 45° pin tips and matching tapered bores. The small shaped-bore trial released and felt good in one print. ROUNDED preserves the older hemispherical tips and cylindrical bores. BRIDGED runs one continuous pin through the whole hinge, which can resist pulling the leaves apart along the axis, but its internal spans must print unsupported.

Conceptual conical, rounded, and bridged pin and bore sections

Cutaway of a continuous bridged pin

BRIDGED makes the printer span each bored tab with an unsupported pin. The unsupported distance is approximately hinge_length / stations + clasp_clearance: 5.3 mm for a 20 mm hinge with 4 stations, or 10.3 mm at the 60 mm, 6-station defaults. The default pivot_clearance leaves only 0.3 mm of radial space between the printed pin and bore. Sagging plastic can use up that space and fuse the hinge, even when the CAD solids are separate. A tester reported that the earlier round bore roof settled onto the pin and fused. The revised 45° teardrop roof released in one small clamshell print, but that hinge was very loose and rocked around its pin. In that size, the pointed roof leaves about 0.74 mm of modeled space above the pin at its peak, even though the gap at the round sides is 0.3 mm. The conical hinge from the same print felt much better. Check in the slicer that the unsupported pin spans use bridge settings. Treat BRIDGED as an experimental option when a continuous pin is needed; do not assume its printed fit will match the conical hinge.

ROUNDED preserves the older pin and bore sizing. On a large barrel with closely spaced stations, neighbouring rounded tips can meet inside a bore; the library warns because the joined span may sag or fuse during printing. The default CONICAL profile keeps an axial gap between its tips.

For 4 or more stations, the two middle tab types have the same width, hinge_length / stations − clasp_clearance. Each end cap has half that width. For example, a 20 mm hinge with 4 stations and the default gap has two 4.7 mm cylinder-side tabs, one 4.7 mm pin-side middle tab, and two 2.35 mm end caps. The pin shank and tips extend into the bores beyond the middle tab; that visible pin is wider than the tab itself.

The shank is the constant-diameter part of a pin between its pin-side tab and its tapered or rounded tip. Three advanced settings change the shanks of the CONICAL and ROUNDED pins. pin_cyl_extra adds to the station width to give the middle shank length; it protrudes (pin_cyl_extra + clasp_clearance) / 2 past each middle tab face. pin_end_offset sets how far an end shank protrudes past its end cap face; its reach into the next bored tab is that value minus clasp_clearance. pin_short_cyl_factor × (hinge_length / stations) is the total end shank length, including the portion buried in the cap. These settings do not affect the continuous BRIDGED pin. That pin runs the full hinge_length; a shorter bridged pin is not currently parameterized.

Geometry changes in 0.3

The default pin is now conical, with a matching tapered bore. It can use a wider bore than the original rounded pin when station spacing leaves room for separate 45° tips; on shorter hinges it uses a smaller bore to prevent those tips from joining. PinStyle.ROUNDED retains the original pin tip profile and cylindrical bore. The STANDARD tab gap is 0.3 mm measured between faces, with equal-width middle tabs on both sides. Re-export existing designs and check the pin and tab fit before relying on an older printed part.

Validation

PrintInPlaceHinge / make_hinge() raise ValueError for hard geometric problems:

  • non-positive case_h, hinge_length, or mounting_flat
  • stations < 2 or odd
  • negative pivot_z_offset, or one not smaller than the knuckle radius
  • bore Ø ≤ pivot_clearance (knuckle too small for the pivot clearance)
  • knuckle_wall outside the knuckle radius
  • invalid pin_style or clearances that leave no tab or pin material

And warns (warnings.warn) when:

  • tab width hinge_length / stations − clasp_clearance drops below ~3 mm (too thin for FDM)

Printing

Lay flat on the bed with the hinge axis along Y (parallel to bed). Start with 0.2 mm layers and the part-cooling fan on. If adhesion needs a brim, keep it clear of the hinge gaps. After printing, gently flex the leaves to break the clearance gaps free.

For a first fit check of the current conical bore, use the small shaped-bore trial. The paired Python example generates the conical and bridged cases used in the printed comparison and accepts gap overrides for printer calibration. The earlier two-size plate documents the cylindrical-bore prints and their fit results.

  • FULL knuckle body rests on the bed without supports. The optional BRIDGED pin still requires an unsupported bridge through each bore.
  • HALF knuckle body has a self-supporting underside on valid sizes: it meets the disc tangentially at 45° and runs to the bed as a teardrop. The optional BRIDGED pin still needs a bridge test.

How this was built

The build123d code, API design discussions, station-count generalisation, self-supporting ramp, clamshell example, and documentation in this repository were produced through a paired design session with Claude Code (Anthropic's Claude Opus 4.7). I drove the design decisions — what the API should look like, which knuckle geometries to support, what trade-offs to accept — and Claude wrote the code, generated the diagrams, ran the verifications, and opened the PRs. The conversation is the source of truth for why the code looks the way it does; the commit history reflects the steps.

The original FreeCAD geometry from r0berts provided the dimensional starting point. The current pin profiles and fit defaults also include changes described above. The Claude collaboration is on the build123d port and the case-designer-facing API built on top of it.

Provenance

This is a port of "Parametric print-in-place hinge. FreeCAD." by r0berts on Printables, licensed CC BY 4.0.

The original is a spreadsheet-driven FreeCAD model. This repository:

  1. Translates the FreeCAD geometry into build123d Python via fcd2b123d.
  2. Reparameterises around four case-designer-facing inputs (case_h, hinge_length, stations, knuckle) with the original dimensional relationships derived under the hood.
  3. Generalises the comb pattern (hardcoded 6 stations in the original) to any even number of stations ≥ 2, adds optional Knuckle.HALF and Knuckle.SMALL modes, and offers three pin profiles.

Per the CC BY 4.0 terms: design and dimensional relationships are r0berts'; modifications are the build123d port, the four-input API, the configurable station count, knuckle sizes, pin profiles, and fit defaults. The original model's printer settings and extent of physical testing are not documented in this repository.

License

This work is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0), matching the upstream Printables source. See LICENSE.

When using or redistributing, please credit:

  • r0berts — original FreeCAD design (Printables)
  • Paul Fremantle (pzfreo) — build123d port, four-input parameterisation, station generalisation, and ramp option

Metadata

Release files for pip-hinge 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pip-hinge 0.3.0
File Size Uploaded Provenance
pip_hinge-0.3.0.tar.gz 382.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pip-hinge 0.3.0
File Interpreter ABI Platform
pip_hinge-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 402.7 kB

Release files / pip_hinge-0.3.0.tar.gz

Download URL pip_hinge-0.3.0.tar.gz
Size 382.0 kB
Tags Source
SHA-256 checksum
How to use checksums
35983177d5ee61f67fe05611a2d25d59adc9824b2e035fac55ead568cfd6cfa9
BLAKE2b-256 checksum
How to use checksums
971cab54ad388213045a593b862b2640233d06f86583feb70c5f48fe6a811c7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / pip_hinge-0.3.0-py3-none-any.whl

Download URL pip_hinge-0.3.0-py3-none-any.whl
Size 20.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
362c44940fca3c38d51c552469503b2179cc154486f23cdbb6b9da9e1db946e1
BLAKE2b-256 checksum
How to use checksums
e6d5cc5efd9550fb053410fa9b8370a7374d280aaaa065e77841961f257db970
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Security for pip-hinge 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pip_hinge-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page