// Piston
// A piston is the component of an engine that transfers linear motion into circular motion. The piston head is accelerated by exploding fuel, which drives a connecting rod to rotate a crankshaft.

// Set units
@settings(defaultLengthUnit = mm)

// Define parameters
stroke = 110
bore = 92
pistonHeight = 75
crankPos = 40deg
crankRadius = 36
pinHeight = 35
rodLength = 201.2

// A simple sketch to define piston position at different crank parameters
kinematicSketch = startSketchOn(XZ)
startProfile(kinematicSketch, at = [0, 0])
  |> angledLine(angle = crankPos, length = stroke / 2, tag = $seg01)
  |> line(
       endAbsolute = [
         0,
         stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2))
       ],
       tag = $seg02,
     )
  |> angledLine(angle = segAng(seg02) - 90, length = 1, tag = $seg03)

// Sketch a profile with a total width equal to half the bore of the piston head. Include lands and gaps for compression rings. Then revolve the profile about the center axis to form the start of a piston head
pistonBore = startSketchOn(XZ)
  |> startProfile(at = [
       0.1,
       pinHeight + stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2))
     ])
  |> xLine(length = bore / 2)
  |> yLine(length = -3)
  |> xLine(length = -3)
  |> yLine(length = -2)
  |> xLine(length = 3)
  |> yLine(length = -3)
  |> xLine(length = -3)
  |> yLine(length = -2)
  |> xLine(length = 3)
  |> yLine(length = -3)
  |> xLine(length = -3)
  |> yLine(length = -2)
  |> xLine(length = 3)
  |> yLine(endAbsolute = profileStartY(%) - pistonHeight - 3)
  |> xLine(length = -8)
  |> yLine(endAbsolute = profileStartY(%) - 8 - (bore / 4))
  |> tangentialArc(angle = 90, radius = bore / 4)
  |> xLine(endAbsolute = profileStartX(%))
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
  |> close()
  |> revolve(angle = 360, axis = Y)

// Create a square profile divot to subtract from the piston head
divots = startSketchOn(YZ)
  |> startProfile(at = [
       bore / 2 - 6,
       stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2)) + pistonHeight / 4 / 2
     ])
  |> yLine(length = -pistonHeight / 4)
  |> xLine(length = 25.75)
  |> yLine(endAbsolute = profileStartY(%))
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
  |> close()
  |> extrude(length = 314, symmetric = true)

// Rotate another divot to the opposite side
cutDivots = patternCircular3d(
  divots,
  instances = 2,
  axis = [0, 0, 1],
  center = [0, 0, 0],
  arcDegrees = 360,
  rotateDuplicates = true,
)

// Cut the divots from the piston head
// WORKED PREVIOUSLY
 pistonDiv = subtract([pistonBore], tools = union(cutDivots))

// Extrude and fillet a wrist pin
wristPin = startSketchOn(XZ)
  |> circle(
       center = [
         0,
         stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2))
       ],
       radius = pistonHeight / 10,
       tag = $seg04,
     )
  |> extrude(length = bore, symmetric = true)
  |> fillet(radius = pistonHeight / 40, tags = [getOppositeEdge(seg04), seg04])

// Cut a rounded profile to arc the bottom of the piston skirt
pistonSkirt = startSketchOn(XZ)
  |> startProfile(at = [
       0,
       stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2)) + pinHeight - (pistonHeight * 0.95)
     ])
  |> xLine(length = .1)
  |> tangentialArc(endAbsolute = [
       bore / 2 + 4,
       stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2)) + pinHeight - pistonHeight - 3
     ])
  |> yLine(endAbsolute = 0)
  |> xLine(endAbsolute = 0)
  |> mirror2d(axis = Y)
  |> close()
  |> extrude(length = 314, symmetric = true)
  // WORKED PREVIOUSLY
pistonHead = subtract([pistonDiv], tools = [pistonSkirt])

// Create a connecting rod that spans from the crank position to the piston height
connectingRodProfile = startSketchOn(XZ)
  |> startProfile(at = [
       segEndX(seg01) - (1.25 * crankRadius * cos(segAng(seg02))),
       segEndY(seg01) - (1.25 * crankRadius * sin(segAng(seg02)))
     ])
  |> angledLine(angle = segAng(seg02) - 90, length = crankRadius / 2)
  |> tangentialArc(angle = 45, radius = 5)
  |> angledLine(angle = segAng(seg02) - 45, length = crankRadius / 2)
  |> tangentialArc(angle = -45, radius = 5)
  |> angledLine(angle = segAng(seg02) - 90, length = crankRadius / 2)
  |> angledLine(angle = segAng(seg02), length = crankRadius * 1.625)
  |> angledLine(angle = segAng(seg02) + 90, length = crankRadius / 2)
  |> tangentialArc(angle = -45, radius = 5)
  |> angledLine(angle = segAng(seg02) + 45, length = crankRadius / 2)
  |> tangentialArc(angle = -45, radius = crankRadius)
  |> angledLineThatIntersects(angle = segAng(seg02), intersectTag = seg03, offset = -crankRadius / 2)
  |> tangentialArc(angle = -45, radius = 5)
  |> angledLine(angle = segAng(seg02) - 45, length = crankRadius / 5)
  |> tangentialArc(angle = -45, radius = 5)
  |> angledLine(angle = segAng(seg02) - 90, length = crankRadius / 5)
  |> angledLineThatIntersects(angle = segAng(seg02), intersectTag = seg03, offset = crankRadius / 3)
  |> angledLineThatIntersects(angle = segAng(seg02) + 45, intersectTag = seg03, offset = crankRadius / 2)
  |> angledLineThatIntersects(%, angle = segAng(seg02) + 90, intersectTag = seg02)
  |> mirror2d(axis = seg02)
  |> close()
  |> extrude(length = 54, symmetric = true)

// Define a plane perpendicular to the connecting rod
connectingRodPlane = {
  origin = [
    0,
    0.0,
    stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2))
  ],
  xAxis = [
    -1 * sin(segAng(seg02)),
    0,
    1 * cos(segAng(seg02))
  ],
  yAxis = [0.0, 1.0, 0.0]
}

// Extrude an oval profile through the connecting rod to define its boundaries in the other dimensions
connectingRodShape = startSketchOn(connectingRodPlane)
  |> startProfile(at = [crankRadius * 1.5, 0])
  |> yLine(length = 4)
  |> tangentialArc(angle = 80, radius = 8)
  |> tangentialArc(endAbsolute = [-lastSegX(%), lastSegY(%)])
  |> tangentialArc(angle = 80, radius = 8)
  |> yLine(endAbsolute = profileStartY(%))
  |> mirror2d(axis = X)
  |> close()
  |> extrude(length = 554, symmetric = true)

connectingRodShapeSubtract = intersect([
  connectingRodProfile,
  connectingRodShape
])

// Bore holes in the connecting rod for the wrist pin and crank
// REPLACED POLYGON CUTS WITH CIRCLES
boreHoles = startSketchOn(XZ)
crankBore = circle(boreHoles, center = polar(angle = crankPos, length = stroke / 2), radius = crankRadius)
  |> extrude(length = 54, symmetric = true)
wristPinHole = circle(
       boreHoles,
       center = [
         0,
         stroke / 2 * sin(crankPos) + sqrt(rodLength ^ 2 - ((stroke / 2 * cos(crankPos)) ^ 2))
       ],
       radius = pistonHeight / 10,
     )
  |> extrude(length = 54, symmetric = true)

// Subtract out the bore holes
connectingRodBore = subtract([connectingRodShapeSubtract], tools = union([wristPinHole, crankBore]))

// Bore holes for the connecting rod bolts
boltHoles = startSketchOn(connectingRodPlane)
  |> circle(center = [45, 0], radius = 5)
  |> patternCircular2d(
       instances = 2,
       center = [0, 0],
       arcDegrees = 360,
       rotateDuplicates = true,
     )
  |> extrude(length = 300)

connectingRod = subtract([connectingRodBore], tools = union(boltHoles))
