kcl-samples → propeller
propeller

KCL
// Ceiling Propeller
// A twisted 3-blade ceiling fan geometry made using boolean cutouts
@settings(defaultLengthUnit = mm)
// Main Parameters
bladeTwistAngle = 5 // Twist angle applied to the void cut geometry
bladeSpanLength = 100 // Blade span (radial length)
bladeWidth = 17.5 // Half-width of the blade at its widest point
bladeBaseThickness = 10 // Total thickness of the extruded root/base
bladeThickness = 2 // Thickness of the profile at void section
// Root geometry
bladeRootWidth = bladeWidth * 2
bladeSeamLength = bladeRootWidth / 2 / cos(30) // Angled base segment (30°)
bladeNeckLength = (bladeRootWidth / 2 - (bladeWidth / 2)) / sin(30) // Neck connects to span
// Fillet radii
rootFillet = bladeNeckLength / tan(30) * 0.5 // Rounded base root
tipFillet = bladeWidth * 0.49 // Rounded blade tip
// Void cutout dimensions
voidCutoutLength = bladeSpanLength + 30 // Longitudinal length of the void
voidCutoutWidth = bladeWidth * 3 // Width of the void cut
voidCutoutHeight = bladeWidth * 2 // Height (used in tangential arc)
// Construct blade profile
bladePlane = offsetPlane(XY, offset = -bladeBaseThickness / 2)
bladeSketch = startSketchOn(XY)
bladeProfile = startProfile(bladePlane, at = [0, 0])
|> angledLine(angle = 30, length = bladeSeamLength)
|> angledLine(angle = 120, length = bladeNeckLength, tag = $segUpperNeck)
|> yLine(length = bladeSpanLength, tag = $segSpanTop)
|> xLine(length = -bladeWidth, tag = $seg01)
|> yLine(length = -bladeSpanLength, tag = $segSpanBottom)
|> angledLine(angle = 240, length = bladeNeckLength, tag = $segLowerNeck)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
// Extrude blade solid and apply fillets
bladeSolid = extrude(bladeProfile, length = bladeBaseThickness)
|> fillet(
radius = rootFillet,
tags = [
getCommonEdge(faces = [segSpanBottom, segLowerNeck]),
getCommonEdge(faces = [segUpperNeck, segSpanTop])
],
)
|> fillet(
radius = tipFillet,
tags = [
getCommonEdge(faces = [segSpanTop, seg01]),
getCommonEdge(faces = [seg01, segSpanBottom])
],
)
// Construct twisted void cutout (one side only)
voidPlane = offsetPlane(YZ, offset = -voidCutoutWidth / 2)
voidSketch = startSketchOn(voidPlane)
voidProfile = startProfile(
voidSketch,
at = [
bladeSpanLength * 1.5,
bladeThickness / 2
],
)
|> xLine(length = -bladeSpanLength * 1.15)
// Arc defining central void (based on voidCutoutHeight)
|> tangentialArc(end = [-voidCutoutHeight, voidCutoutHeight])
|> xLine(length = bladeSpanLength * 1.5)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
voidCutoutOne = extrude(voidProfile, length = voidCutoutWidth)
|> rotate(roll = 0, pitch = bladeTwistAngle, yaw = 0)
// Mirror second void cut to other side
voidCutoutTwo = clone(voidCutoutOne)
|> rotate(roll = 0, pitch = 180, yaw = 0)
// Subtract voids from blade
bladeWithVoidOne = subtract([bladeSolid], tools = [voidCutoutOne])
bladeWithVoids = subtract([bladeWithVoidOne], tools = [voidCutoutTwo])
// Circular pattern: 3 blade instance fan
propellerAssembly = patternCircular3d(
bladeWithVoids,
instances = 3,
axis = [0, 0, 1],
center = [0, 0, 0],
)