kcl-samples → propeller

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],
)