Skip to content

Instantly share code, notes, and snippets.

@JamesNewton
Last active June 13, 2026 19:34
Show Gist options
  • Select an option

  • Save JamesNewton/22181ea697c9424ecfebe3d8037cd884 to your computer and use it in GitHub Desktop.

Select an option

Save JamesNewton/22181ea697c9424ecfebe3d8037cd884 to your computer and use it in GitHub Desktop.
Well Drilling
/*
1/2 inch treaded rod is cheap and easy to grind teeth into.
It can be joined, and while the sections buldge out, they can also have teeth ground into them
and so can cut their way down.
The problem is getting the muck out. A Ball valve will clog, a flap valve need to be bigger than
the ID of the pipe. You probably want a duckbill valve. e.g.
https://www.rubbervalve.com/duckbill-valve/?
But they aren't available in a size that fits in the common 1/2 threaded steel pipe. So we can
print one in TPU.
This is an https://openjscad.xyz program for a parametric model for a duckbill valve. You can
export the STL and then print it.
*/
const { cylinder, cuboid } = require('@jscad/modeling').primitives;
const { translate } = require('@jscad/modeling').transforms;
const { subtract, union } = require('@jscad/modeling').booleans;
const { hull } = require('@jscad/modeling').hulls;
const getParameterDefinitions = () => {
return [
// Flange (The wide "lip" that screws into the coupling)
{ name: 'flangeCoreOD', type: 'float', initial: 17, caption: 'Flange Core OD (mm)' },
{ name: 'flangeRibOD', type: 'float', initial: 18.0, caption: 'Flange Rib OD (mm)' },
{ name: 'flangeHeight', type: 'float', initial: 9.0, caption: 'Flange Total Height (mm)' },
{ name: 'flangeRibCount', type: 'int', initial: 4, caption: 'Flange Rib Count' },
// Base Tube (The smooth shaft)
{ name: 'baseOD', type: 'float', initial: 16.0, caption: 'Base Tube OD (mm)' },
{ name: 'baseID', type: 'float', initial: 14.0, caption: 'Base Tube ID (mm)' },
{ name: 'baseHeight', type: 'float', initial: 10.0, caption: 'Base Tube Height (mm)' },
// Duckbill Lips
{ name: 'billHeight', type: 'float', initial: 17.0, caption: 'Duckbill Height (mm)' },
{ name: 'lipThickness', type: 'float', initial: 0.8, caption: 'Lip Wall Thickness (mm)' },
{ name: 'slitWidth', type: 'float', initial: 0.2, caption: 'Slit Width (mm)' },
// Resolution
{ name: 'segments', type: 'int', initial: 64, caption: 'Resolution' }
];
};
const main = (params) => {
const {
flangeCoreOD, flangeRibOD, flangeHeight, flangeRibCount,
baseOD, baseID, baseHeight,
billHeight, lipThickness, slitWidth, segments
} = params;
// 1. Gasket / Flange Section (Now with a narrower core and locking ribs)
const flangeCore = cylinder({ radius: flangeCoreOD / 2, height: flangeHeight, segments });
const flangeCorePos = translate([0, 0, flangeHeight / 2], flangeCore);
let flangeRibsArray = [];
if (flangeRibCount > 0) {
const fSpacing = flangeHeight / (flangeRibCount - 0.62);
for (let i = 0; i < flangeRibCount; i++) {
// 1mm thick rings acting as threads
const fRib = cylinder({ radius: flangeRibOD / 2, height: 1.0, segments });
const fRibPos = translate([0, 0, fSpacing * i + 0.5], fRib);
flangeRibsArray.push(fRibPos);
}
}
const allFlangeRibs = flangeRibsArray.length > 0 ? union(flangeRibsArray) : null;
const ribbedFlange = allFlangeRibs ? union(flangeCorePos, allFlangeRibs) : flangeCorePos;
// 2. Base Tube Section (The smooth core)
const baseTube = cylinder({ radius: baseOD / 2, height: baseHeight, segments });
const baseTubePos = translate([0, 0, baseHeight / 2 + flangeHeight], baseTube);
// 3. Outer Duckbill (Hull from cylinder to flat line)
const billBaseOuter = cylinder({ radius: baseOD / 2, height: 0.1, segments });
const billBaseOuterPos = translate([0, 0, baseHeight + flangeHeight], billBaseOuter);
const billTipOuter = cuboid({ size: [baseOD * 0.9, lipThickness * 2 + slitWidth, 0.1] });
const billTipOuterPos = translate([0, 0, baseHeight + flangeHeight + billHeight], billTipOuter);
const outerBill = hull(billBaseOuterPos, billTipOuterPos);
// Combine entire outer body
const outerBody = union(ribbedFlange, baseTubePos, outerBill);
// 4. Inner Hollow Section (to subtract)
// Ensure the bore cuts cleanly through the bottom by extending it down slightly (-0.05)
const innerTube = cylinder({ radius: baseID / 2, height: baseHeight + flangeHeight + 0.1, segments });
const innerTubePos = translate([0, 0, (baseHeight + flangeHeight) / 2 - 0.05], innerTube);
const billBaseInner = cylinder({ radius: baseID / 2, height: 0.1, segments });
const billBaseInnerPos = translate([0, 0, baseHeight + flangeHeight], billBaseInner);
const billTipInner = cuboid({ size: [baseID * 1, slitWidth, 0.2] });
const billTipInnerPos = translate([0, 0, baseHeight + flangeHeight + billHeight], billTipInner);
const innerBill = hull(billBaseInnerPos, billTipInnerPos);
// Combine inner negative volume
const innerBody = union(innerTubePos, innerBill);
// 5. Subtract inside from outside to create the shell
return subtract(outerBody, innerBody);
};
module.exports = { main, getParameterDefinitions };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment