Sam Gentle.com

Bezier Playground



Drag red circles to move control points

Edit this playground

$ = document.querySelector.bind(document)

order = 3
w = $('section').offsetWidth
h = 480
r = Raphael "holder", w, h

interp = ([x1, y1], [x2, y2], t) ->
  [
    x1*(1-t) + x2*t,
    y1*(1-t) + y2*t
  ]

nodes = []

curve = null
drawCurve = (t) ->
  pts = ([n.attr('cx'), n.attr('cy')] for n in nodes)
  points = (bezier pts, n for n in [0..t] by 0.01)
  curve.attr path: 'M' + ("#{x} #{y}" for [x, y] in points).join('L')

bezier = (pts, t) ->
  newpts = (interp pts[i], pts[i+1], t for i in [0..pts.length-2])
  if newpts.length > 1
    bezier newpts, t
  else
    newpts[0]


drawPoints = (pts, t) ->
  newpts = (interp pts[i], pts[i+1], t for i in [0..pts.length-2])
  if newpts.length > 1
    hue = 0.1 + (order-newpts.length) / order
    els = (r.circle(x, y, 5).attr(fill: "hsla(#{hue}, 0.8, 0.5)") for [x, y] in newpts)
    els = els.concat drawPoints newpts, t
  else
    [x, y] = newpts[0]
    els = [r.circle(x, y, 5).attr(fill: 'black')]
  
  els.push r.path('M' + ("#{x} #{y}" for [x, y] in pts).join('L')).toBack()
  els

paths = null
update = () ->
  t = $('#slider').value
  path.remove() for path in paths if paths?          
  paths = drawPoints([n.attr('cx'), n.attr('cy')] for n in nodes, t)
  drawCurve(t)
  node.toFront() for node in nodes

makeDraggy = (c) ->
  odx = ody = 0
  c.drag (dx, dy) ->
    x = @attr('cx') + dx - odx
    y = @attr('cy') + dy - ody
    @attr cx: x, cy: y
    [odx, ody] = [dx, dy]
    update()
  c.mouseup ->
    odx = ody = 0

setup = ->
  r.clear()
  paths = null
  curve = r.path().attr 'stroke-width': 3
  myx = 0
  nodes = for i in [0..order]
    c = r.circle myx+=Math.random()*(w-20)/order, Math.random()*h, 6
    c.attr fill:'red'
    c.node.style.cursor = 'move'
    makeDraggy(c)      
  update()


onOrder = ->
  neworder = parseInt($('#order').value, 10)
  if order != neworder
    order = neworder
    setup()

for e in ['change', 'input']
  $('#slider').addEventListener e, update
  $('#order').addEventListener e, onOrder

setup()

  
<div id='holder'></div>
<label for='slider'>T=</label>
<input id='slider'
  type='range'
  min='0'
  max='1'
  step='0.01'
  value='0' /><br />
<label for='order'>Order</label>
<input id='order' type='number' min='1' max'100' step='1' value='3'/><br />
Drag red circles to move control points

  

(Note: this is not saved anywhere)