   new Slider("control-angle", "ca", 180, options.ca)
   new Slider("control-distance", "cd", 0.15, options.cd)
 
-  $("#circular").change(setShapeToCircular)
-  $("#quadratic").change(setShapeToQuadratic)
-  $("#cubic").change(setShapeToCubic)
-  $("#crossingcubic").change(setShapeToCrossingCubic)
+  $("#circular").change(starWithCircularTips.use)
+  $("#quadratic").change(starWithQuadraticTips.use)
+  $("#cubic").change(starWithCubicTips.use)
+  $("#crossingcubic").change(crossingCubicStar.use)
   $("#" + options.s).prop("checked", true)
   $("input[name=shape]:checked").change()
 

  refreshBackground($("#bg-color-picker").spectrum("get"))


## Math functions for calculating star shapes

class Point
  constructor: (@x, @y) ->

  calculateIntermediatePoints(innerPoints, shiftedInnerPoints, outerPoints, percentage)


controlVals = (controlNames...) ->
  for name in controlNames
    controls[name].val()

class Star
  constructor: (@name, @controls) ->

  refreshPath: () =>
    pathString = @path(controlVals(@controls...)...)
    $("#star").attr("d", pathString)
    return

  use: () =>
    currentStar = this
    showControls(@controls...)
    updateUrlQuery("s", @name)
    @refreshPath()

  path: (inputs...) ->
    points = @points(inputs...)
    @constructPath(points...)

class InnerAndIntermediateStar extends Star
  points: (innerRadius, percentage) ->
    outer = outerPoints()
    inner = innerPoints(innerRadius)
    [intermediate1, intermediate2] = calculateIntermediatePointsComingAndGoing(inner, outer, percentage)

    [inner, intermediate1, intermediate2, outer]

class LinearStar extends Star
  constructor: () ->
    super("linear", ["inner-radius"])

  points: (innerRadius) ->
    [innerPoints(innerRadius), outerPoints()]

  constructPath: (inner, outer) ->
    points = _.flatten(_.zip(inner, outer))
    return "M " + points.join(" L ") + " Z"

class StarWithCircularTips extends InnerAndIntermediateStar
  constructor: () ->
    super("circular", ["inner-radius", "straight-percentage"])

  path: (innerRadius, straightPercentage) ->
    if straightPercentage == "100"
      return linearStar.path(innerRadius)
    else
      super(innerRadius, straightPercentage)

  points: (innerRadius, straightPercentage) ->
    [inner, intermediate1, intermediate2, outer] = super(innerRadius, straightPercentage)
    radius = calculateRadius(intermediate1[0], outer[0], intermediate2[0])

    return [inner, intermediate1, intermediate2, radius]

  constructPath: (inner, intermediate1, intermediate2, radius) ->
    points = _.zip(inner, intermediate1, intermediate2)

    sectionStrings = points.map ([inner, intermediate1, intermediate2]) ->
      "#{ inner } L #{ intermediate1 } A #{ radius } #{ radius } 0 0 1 #{ intermediate2 }"
    return "M " + sectionStrings.join(" L ") + " Z"

class QuadraticStar extends Star
  constructor: () ->
    super(null, ["inner-radius"])

  points: (innerRadius) ->
    [innerPoints(innerRadius), outerPoints()]

  constructPath: (inner, outer) ->
    first = inner[0]
    inner.push(inner.shift())
    points = _.zip(outer, inner)

    sectionStrings = points.map ([outer, inner]) ->
      "Q #{ outer } #{ inner }"

    return "M #{ first } " + sectionStrings.join(" ") + " Z"

class StarWithQuadraticTips extends InnerAndIntermediateStar
  constructor: () ->
    super("quadratic", ["inner-radius", "straight-percentage"])

  path: (innerRadius, straightPercentage) ->
    if straightPercentage == "100"
      return linearStar.path(innerRadius)
    if straightPercentage == "0"
      return quadraticStar.path(innerRadius)

    super(innerRadius, straightPercentage)

  constructPath: (inner, intermediate1, intermediate2, outer) ->
    points = _.zip(inner, intermediate1, outer, intermediate2)

    sectionStrings = points.map ([inner, intermediate1, outer, intermediate2]) ->
      "#{ inner } L #{ intermediate1 } Q #{ outer } #{ intermediate2 }"
    return "M " + sectionStrings.join(" L ") + " Z"

class CubicStar extends InnerAndIntermediateStar
  constructor: () ->
    super(null, ["inner-radius", "control-percentage"])

  points: (innerRadius, controlPercentage) ->
    [inner, control1, control2, outer] = super(innerRadius, controlPercentage)
    [inner, control1, control2]

  constructPath: (inner, control1, control2) ->
    first = inner[0]
    inner.push(inner.shift())
    points = _.zip(control1, control2, inner)

    sectionStrings = points.map ([control1, control2, inner]) ->
      "C #{ control1 } #{ control2 } #{ inner }"
    return "M #{ first } " + sectionStrings.join(" ") + " Z"

class StarWithCubicTips extends Star
  constructor: () ->
    super("cubic", ["inner-radius", "straight-percentage", "control-percentage"])

  path: (innerRadius, straightPercentage, controlPercentage) ->
    if straightPercentage == "100"
      return linearStar.path(innerRadius)
    if straightPercentage == "0"
      return cubicStar.path(innerRadius, controlPercentage)

    super(innerRadius, straightPercentage, controlPercentage)

  points: (innerRadius, straightPercentage, controlPercentage) ->
    outer = outerPoints()
    inner = innerPoints(innerRadius)
    [intermediate1, intermediate2] = calculateIntermediatePointsComingAndGoing(inner, outer, straightPercentage)
    [control1, control2] = calculateIntermediatePoints(intermediate1, intermediate2, outer, controlPercentage)

    return [inner, intermediate1, intermediate2, control1, control2]

  constructPath: (inner, intermediate1, intermediate2, control1, control2) ->
    points = _.zip(inner, intermediate1, control1, control2, intermediate2)

    sectionStrings = points.map ([inner, intermediate1, control1, control2, intermediate2]) ->
      "#{ inner } L #{ intermediate1 } C #{ control1 } #{ control2 } #{ intermediate2 }"
    return "M " + sectionStrings.join(" L ") + " Z"

class CrossingCubicStar extends Star
  constructor: () ->
    super("crossingcubic", ["control-angle", "control-distance"])

  points: (controlAngle, controlDistance) ->
    outer = outerPoints()

    controlAngle = (controlAngle / 2) * (Math.PI / 180)
    angles = outerAngles()
    controlAngles1 = angles.map (angle) ->
      angle + Math.PI - controlAngle
    controlAngles2 = angles.map (angle) ->
      angle + Math.PI + controlAngle

    displacement1 = controlAngles1.map (angle) -> polarToCartesian(angle, controlDistance)
    displacement2 = controlAngles2.map (angle) -> polarToCartesian(angle, controlDistance)
    controlPoints1 = _.zip(outer, displacement1).map(addPoints)
    controlPoints2 = _.zip(outer, displacement2).map(addPoints)

    [outer, controlPoints1, controlPoints2]

  constructPath: (outer, control1, control2) ->
    "M #{outer[0]} C #{control1[0]} #{control2[2]} #{outer[2]}
                   C #{control1[2]} #{control2[4]} #{outer[4]}
                   C #{control1[4]} #{control2[1]} #{outer[1]}
                   C #{control1[1]} #{control2[3]} #{outer[3]}
                   C #{control1[3]} #{control2[0]} #{outer[0]}
                   Z"


linearStar = new LinearStar()
starWithCircularTips = new StarWithCircularTips()
quadraticStar = new QuadraticStar()
starWithQuadraticTips = new StarWithQuadraticTips()
cubicStar = new CubicStar()
starWithCubicTips = new StarWithCubicTips()
crossingCubicStar = new CrossingCubicStar()
