Skip to content

Instantly share code, notes, and snippets.

@dwblair
Created April 17, 2026 17:00
Show Gist options
  • Select an option

  • Save dwblair/c7c9616d8d25a7138c74e9c8c1dfaa69 to your computer and use it in GitHub Desktop.

Select an option

Save dwblair/c7c9616d8d25a7138c74e9c8c1dfaa69 to your computer and use it in GitHub Desktop.
// Playmobil Curved Sword — parametric OpenSCAD
// Based on hand sketch: curved blade (both edges), guard, rounded pommel
// Print flat on bed. Adjust handle_dia to fit Playmobil hand grip.
/* --- Tunable Parameters --- */
// Handle (must fit Playmobil C-clip hand)
handle_dia = 3.5; // mm — start here, test fit, adjust +/- 0.2
handle_len = 12; // mm
// Guard
guard_width = 10; // mm — total width of crossguard
guard_height = 3; // mm — height along blade axis
guard_thick = 3; // mm — thickness (z)
guard_curve_up = 4; // mm — how far the blade-side tip curves upward
guard_bar_dia = 2; // mm — diameter of the guard bar
// Blade
blade_len = 35; // mm — from guard to tip
blade_width = 8; // mm — widest point near guard
blade_thick = 2; // mm — flat profile thickness (z)
blade_curve = 6; // mm — lateral curve offset at midpoint
// Pommel
pommel_dia = 8; // mm
pommel_thick = 6; // mm
// Resolution
$fn = 64;
/* --- Modules --- */
// Curved blade profile — hull between cross-sections
module blade() {
// Blade is a series of hulled elliptical cross-sections
// from guard to tip, with lateral curve on both edges
steps = 40;
for (i = [0:steps-1]) {
t0 = i / steps;
t1 = (i + 1) / steps;
hull() {
blade_slice(t0);
blade_slice(t1);
}
}
}
module blade_slice(t) {
// t = 0 at guard, t = 1 at tip
// Width tapers from blade_width to 0
// X position curves using sine for the scimitar sweep
y = guard_height/2 + t * blade_len;
// Curve: sine sweep for scimitar shape
x = blade_curve * sin(t * 120);
// Width taper: starts at blade_width, narrows to point
w = blade_width * (1 - pow(t, 0.8));
// Thickness taper: slight taper toward tip
th = blade_thick * (1 - 0.3 * pow(t, 1.5));
translate([x, y, 0])
scale([max(w, 0.3), 1, max(th, 0.4)])
sphere(d=1);
}
// Guard — curved crossguard connected at both ends, blade-side curves up
module guard() {
steps = 20;
half_w = guard_width / 2;
// Sweep a circle along a curved path from -half_w to +half_w
for (i = [0:steps-1]) {
t0 = i / steps;
t1 = (i + 1) / steps;
hull() {
guard_point(t0);
guard_point(t1);
}
}
}
module guard_point(t) {
// t goes 0 to 1 across the guard width
half_w = guard_width / 2;
x = -half_w + t * guard_width;
// D-shape: ends connect at blade/handle junction (y=0), center bows downward
curve_factor = 4 * t * (1 - t); // parabola: 0 at ends, 1 at center
y_up = -guard_curve_up * curve_factor; // 0 at ends, dips down at center
translate([x, y_up, 0])
sphere(d=guard_bar_dia);
}
// Handle — cylinder sized for Playmobil grip
module handle() {
translate([0, -handle_len/2, 0])
rotate([-90, 0, 0])
cylinder(d=handle_dia, h=handle_len, center=false);
}
// Pommel — squashed sphere at bottom of handle
module pommel() {
translate([0, -handle_len, 0])
scale([pommel_dia, pommel_dia * 0.6, pommel_thick])
sphere(d=1);
}
/* --- Assembly --- */
module sword() {
color("silver") blade();
translate([2.5,-5,0])
rotate([0,0,90])
color("gold") guard();
translate([0,-5,0])
color("sienna") handle();
translate([0,13,0])
color("gold") pommel();
translate([0,1,0])
color("gold") pommel();
}
sword();
// Print info
echo(str("Total length: ~", handle_len + guard_height + blade_len + pommel_dia*0.3, " mm"));
echo(str("Handle diameter: ", handle_dia, " mm (adjust for Playmobil fit)"));
echo(str("Blade thickness: ", blade_thick, " mm (print flat on bed)"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment