Examples
An assembly of several couplings that previews fast with shell_only.
Cameron K. Brooks - MIT License The locking channel is a boolean-heavy cut, so an assembly holding several bayonets gets slow to pan long before it gets slow to render. Setting the dynamically scoped $bayonet_shell_only special variable once, at the top of the assembly, drops the pin/channel features from every bayonet() call below it - including calls buried inside your own modules - while leaving the coupling shells where they are. Wiring it to $preview gives the usual OpenSCAD workflow: F5 previews the assembly as bare shells, F6 renders the real geometry. Nothing else in the file changes. Try it: comment the line out and pan the preview to feel the difference.
Source: examples/assembly_shell_only_preview.scad
// An assembly of several couplings that previews fast with shell_only.
// Cameron K. Brooks - MIT License
//
// The locking channel is a boolean-heavy cut, so an assembly holding several bayonets
// gets slow to pan long before it gets slow to render. Setting the dynamically scoped
// $bayonet_shell_only special variable once, at the top of the assembly, drops the
// pin/channel features from every bayonet() call below it - including calls buried
// inside your own modules - while leaving the coupling shells where they are.
//
// Wiring it to $preview gives the usual OpenSCAD workflow: F5 previews the assembly as
// bare shells, F6 renders the real geometry. Nothing else in the file changes.
//
// Try it: comment the line out and pan the preview to feel the difference.
use <../bayonet_lock.scad>;
$fn = $preview ? 64 : 128;
$bayonet_shell_only = $preview;
interface_radius = 10;
part_height = 10;
joint_count = 4;
// Shared parameters, so both halves of a joint mate.
module half(which) {
bayonet(
half=which,
interface_radius=interface_radius,
allowance=0.2,
part_height=part_height,
number_of_pins=3,
pin_radius=1,
sweep_angle=30,
pin_direction="outer",
turn_direction="CW"
);
}
// A complete, locked coupling. At a common origin the halves are at entry, so the pin half is
// turned by the sweep to lock it.
// Note that half() never mentions shell_only: the special variable reaches it anyway,
// which is the whole point when bayonet() calls are buried inside your own modules.
module joint() {
half("lock");
rotate([0, 0, bayonet_locked_rotation(30, "CW")]) half("pin");
}
// Four couplings spaced along a rail - the kind of assembly that gets painful to pan.
for (i = [0:joint_count - 1])
translate([i * interface_radius * 3, 0, 0])
joint();
// A per-call override still wins over the special variable, so one joint can stay fully
// detailed for inspection while the rest of the assembly stays light during preview.
translate([0, interface_radius * 3, 0])
bayonet(
half="lock",
interface_radius=interface_radius,
allowance=0.2,
part_height=part_height,
number_of_pins=3,
pin_radius=1,
sweep_angle=30,
pin_direction="outer",
turn_direction="CW",
shell_only=false
);
examples/assembly_shell_only_preview.scad on GitHub
A printable canister with a bayonet lid.
The lock half is the jar’s rim and the pin half the cap’s: push the cap down and turn it to lock. Laid out for printing, cap upside down beside the jar. Cameron K. Brooks - MIT License
Source: examples/canister.scad
// A printable canister with a bayonet lid.
// The lock half is the jar's rim and the pin half the cap's: push the cap down and turn it to
// lock. Laid out for printing, cap upside down beside the jar.
// Cameron K. Brooks - MIT License
use <../bayonet_lock.scad>;
/* [Canister] */
// Inside diameter of the jar
bore = 40; // [16:1:120]
// Inside depth of the jar below the coupling
depth = 50; // [10:1:200]
// Wall thickness of the jar and of both coupling halves
wall = 2.4; // [1.6:0.1:6]
// Thickness of the jar's floor and the cap's top
floor_thickness = 2; // [1:0.2:5]
/* [Coupling] */
// Number of pins, evenly spaced
number_of_pins = 3; // [2:6]
// Turn from entry to locked, in degrees
sweep_angle = 30; // [10:1:60]
// Direction the cap turns to lock
turn_direction = "CW"; // [CW, CCW]
// Radius of each pin
pin_radius = 1.2; // [0.6:0.1:2.5]
// Radial clearance between cap and jar; raise it if the cap binds
allowance = 0.3; // [0.1:0.05:0.8]
// Height of the coupling at the jar's rim
coupling_height = 10; // [6:0.5:20]
/* [View] */
view = "print"; // [print, assembled, cut away]
/* [Hidden] */
$fn = $preview ? 64 : 128;
// The jar's bore runs straight up through the lock half, so the lock's inside face is the bore.
interface_radius = bore / 2 - allowance / 2;
outer_radius = interface_radius + wall;
// The cap's top clears the rim by the allowance once the pins have seated.
cap_gap = allowance;
cap_height = coupling_height + cap_gap + floor_thickness;
module coupling(which) {
bayonet(
half=which,
interface_radius=interface_radius,
shell_thickness=wall,
allowance=allowance,
part_height=coupling_height,
number_of_pins=number_of_pins,
pin_radius=pin_radius,
sweep_angle=sweep_angle,
pin_direction="outer",
turn_direction=turn_direction
);
}
// The jar: a cup whose rim, from z = 0 up, is the lock half.
module jar() {
coupling("lock");
translate([0, 0, -depth - floor_thickness])
difference() {
cylinder(h=depth + floor_thickness, r=outer_radius);
translate([0, 0, floor_thickness]) cylinder(h=depth + 1, r=bore / 2);
}
}
// The cap, drawn at entry on the jar: its pin half drops inside the rim, its top sits above it.
module cap() {
coupling("pin");
// Carry the pin half's wall up through the gap to the top.
translate([0, 0, coupling_height])
difference() {
cylinder(h=cap_gap + 0.01, r=interface_radius - allowance / 2);
translate([0, 0, -1]) cylinder(h=cap_gap + 2, r=interface_radius - wall);
}
translate([0, 0, coupling_height + cap_gap]) cylinder(h=floor_thickness, r=outer_radius);
}
module assembled() {
jar();
rotate([0, 0, bayonet_locked_rotation(sweep_angle, turn_direction)]) cap();
}
if (view == "print") {
translate([0, 0, depth + floor_thickness]) jar();
translate([outer_radius * 2 + 10, 0, cap_height]) rotate([180, 0, 0]) cap();
} else if (view == "assembled") {
assembled();
} else if (view == "cut away") {
difference() {
assembled();
// Remove the half nearest the default camera, which looks along +y.
translate([-outer_radius - 1, -outer_radius - 1, -depth - 10])
cube([2 * outer_radius + 2, outer_radius + 1, depth + cap_height + 20]);
}
}
examples/canister.scad on GitHub
Both halves of one coupling, with every parameter in the Customizer.
Shows the lock and pin halves side by side, or the pin half locked into the lock. Cameron K. Brooks - MIT License
Source: examples/configurator.scad
// Both halves of one coupling, with every parameter in the Customizer.
// Shows the lock and pin halves side by side, or the pin half locked into the lock.
// Cameron K. Brooks - MIT License
use <../bayonet_lock.scad>;
/* [Coupling] */
// Which way the pins point: outer pins point out, into a lock that surrounds them
pin_direction = "outer"; // [outer, inner]
// Direction the pin half turns to lock
turn_direction = "CW"; // [CW, CCW]
// Number of pins, evenly spaced
number_of_pins = 3; // [1:8]
// Arc each pin travels from entry to locked, in degrees
sweep_angle = 30; // [5:1:120]
/* [Size] */
// Radius of the mating surface between the halves
interface_radius = 10; // [4:0.5:60]
// Wall of each half; 0 uses the default, twice the pin radius
shell_thickness = 0; // [0:0.25:10]
// Radius of each pin
pin_radius = 1; // [0.5:0.25:5]
// Radial clearance between the halves
allowance = 0.2; // [0:0.05:1]
// Height of each half
part_height = 10; // [4:0.5:40]
// Depth the pins drop before turning; 0 uses the default, half the height
entry_depth = 0; // [0:0.5:39]
/* [View] */
// Side by side for printing, or the pin half turned into the locked position
layout = "side by side"; // [side by side, locked]
/* [Hidden] */
$fn = $preview ? 64 : 128;
max_sweep = bayonet_max_sweep_angle(number_of_pins, interface_radius, pin_radius, allowance);
echo(str("sweep_angle ", sweep_angle, " of at most ", max_sweep, " for this pin count and size"));
module half(which) {
bayonet(
half=which,
interface_radius=interface_radius,
shell_thickness=shell_thickness > 0 ? shell_thickness : undef,
allowance=allowance,
part_height=part_height,
entry_depth=entry_depth > 0 ? entry_depth : undef,
number_of_pins=number_of_pins,
pin_radius=pin_radius,
sweep_angle=sweep_angle,
pin_direction=pin_direction,
turn_direction=turn_direction
);
}
half("lock");
if (layout == "locked")
rotate([0, 0, bayonet_locked_rotation(sweep_angle, turn_direction)]) half("pin");
else
translate([interface_radius * 3 + (shell_thickness > 0 ? shell_thickness : pin_radius * 2) * 2, 0, 0]) half("pin");
examples/configurator.scad on GitHub
A keyed 3-pin coupling: one locked orientation instead of three.
Cameron K. Brooks - MIT License
Source: examples/keyed_3pin.scad
// A keyed 3-pin coupling: one locked orientation instead of three.
// Cameron K. Brooks - MIT License
use <../bayonet_lock.scad>;
$fn = $preview ? 64 : 128;
interface_radius = 10;
pin_radius = 1;
allowance = 0.2;
sweep_angle = 30;
turn_direction = "CW";
part_height = 10;
// Evenly spaced pins repeat every 360/n, so an unkeyed 3-pin coupling mates in three
// orientations 120 degrees apart and nothing distinguishes them. Bringing one pin back by
// key_angle leaves a single seating. The offset has to clear the entry shaft's half-width or
// a wrong attempt still finds the mouth and starts to go in.
key_angle = 25;
pin_angles = bayonet_keyed_pin_angles(3, key_angle);
echo(str(
"pins ", pin_angles,
" | symmetry order ", bayonet_pin_pattern_order(pin_angles),
" | keyed ", bayonet_pin_pattern_is_keyed(pin_angles),
" | margin ", bayonet_pin_pattern_margin(pin_angles), " deg",
" vs channel half-width ", bayonet_channel_half_angle(interface_radius, pin_radius, allowance), " deg"
));
assert(
bayonet_pin_pattern_is_keyed(pin_angles),
"this example is about keying; the pattern has to be keyed"
);
assert(
bayonet_pin_pattern_margin(pin_angles) > bayonet_channel_half_angle(interface_radius, pin_radius, allowance),
"key_angle is too small to block a wrong seating"
);
module coupling(half) {
bayonet(
half=half,
interface_radius=interface_radius,
allowance=allowance,
part_height=part_height,
pin_angles=pin_angles,
pin_radius=pin_radius,
sweep_angle=sweep_angle,
pin_direction="outer",
turn_direction=turn_direction
);
}
// Locked: the pin half turned from entry (the common origin) by the sweep.
coupling("lock");
rotate([0, 0, bayonet_locked_rotation(sweep_angle, turn_direction)]) coupling("pin");
// The seating that an unkeyed coupling would also have accepted. Turned 120 degrees the pins
// land 25 degrees off the mouths, so intersecting the halves here is non-empty - it does not
// go together. Keep it visible while tuning key_angle.
translate([interface_radius * 3, 0, 0]) {
#intersection() {
coupling("lock");
rotate([0, 0, 120]) coupling("pin");
}
coupling("lock");
}
examples/keyed_3pin.scad on GitHub
The bare minimum: one bayonet() call per half, default shell and entry depth.
Cameron K. Brooks - MIT License
Source: examples/minimal.scad
// The bare minimum: one bayonet() call per half, default shell and entry depth.
// Cameron K. Brooks - MIT License
use <../bayonet_lock.scad>;
$fn = $preview ? 64 : 128;
example_interface_radius = 8;
// Lock - rendered at origin
bayonet(
half="lock",
interface_radius=example_interface_radius,
allowance=0.2,
part_height=8,
number_of_pins=2,
pin_radius=1,
sweep_angle=40,
pin_direction="inner",
turn_direction="CW"
);
// Pin - translated right for side-by-side comparison
translate([example_interface_radius * 3, 0, 0])
bayonet(
half="pin",
interface_radius=example_interface_radius,
allowance=0.2,
part_height=8,
number_of_pins=2,
pin_radius=1,
sweep_angle=40,
pin_direction="inner",
turn_direction="CW"
);