Skip to content

Instantly share code, notes, and snippets.

@randompast
Last active May 1, 2019 12:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save randompast/996dd765aca6d4952f233ef4f151dffa to your computer and use it in GitHub Desktop.
Save randompast/996dd765aca6d4952f233ef4f151dffa to your computer and use it in GitHub Desktop.
Brachistochrone via gradient descent with pretty rainbow iso lines and sliding balls
// Tweet (gif) : https://twitter.com/randompast/status/897526013437018114
// $ budo --live cov2.js
// Attempt at local convergence to a global solution via Brachistochrone
// Intro:
// https://www.youtube.com/watch?v=skvnj67YGmw
// https://www.youtube.com/watch?v=Cld0p3a43fU
// Mathier:
// http://mathworld.wolfram.com/BrachistochroneProblem.html
// https://www.youtube.com/watch?v=6HeQc7CSkZs&t=22s
// https://www.youtube.com/watch?v=QEWGZtFTlRU
// Needs work, there's some slippage, some boundary condition issue
document.title = "CoV Brachistocrone"
var fit = require('canvas-fit')
var canvas = document.body.appendChild(document.createElement('canvas'))
window.addEventListener('resize', fit(canvas), false)
var ctx = canvas.getContext('2d')
var S = []
var C = []
var X = []
var L = []
for(var i = 0; i < 101; i++){
S.push([i*i/20, 1*(i*2 + 15*Math.sin(i/1.98945))])
C.push([i*i/20, 1*(i*2 + 15*Math.sin(i/1.98945))])
X.push([i*i/20, 1*(i*2)])
L.push([i*(500-0.00)/100, 1*(i*2)])
}
var offx = 10
var offy = 10
var yscale = 1
var ds = function(a,b){
ctx.beginPath()
ctx.lineWidth = 2
ctx.moveTo(a[0]+offx, (a[1]+offy)*yscale)
ctx.lineTo(b[0]+offx, (b[1]+offy)*yscale)
ctx.stroke()
ctx.fillStyle = "black"
ctx.fillRect(a[0]+offx,(a[1]+offy)*yscale,2,2)
}
var draw_ball = function(p){
var size = 4
ctx.fillStyle = ctx.strokeStyle
ctx.beginPath()
ctx.arc(p[0]+offx, (p[1]+offy)*yscale - size, size, 0, Math.PI * 2, true)
ctx.fill()
ctx.stroke()
}
var line = function(A){
for(var i = 0; i < A.length - 1; i++){
ds( A[i], A[i+1] )
}
}
var iso = function(A, i, t_leftover){
if( i < A.length - 1 && 0 <= i){
var Dt = dt(A[i],A[i+1],0)
if(t_leftover < Dt){
var t = t_leftover/Dt
// t: 0 -> 1
// LERP a -> b
var x = A[i][0] * (1 - t) + A[i+1][0] * t
var y = A[i][1] * (1 - t) + A[i+1][1] * t
return [x,y]
}else{
return iso(A, i+1, t_leftover - Dt)
}
} else {
return A[A.length-1]
}
}
var rand = function(A){
var r = Math.floor(Math.random()*(A.length-2)) + 1
var Dy = perturb( A[r-1], A[r], A[r+1] )
ds(A[r], [ A[r][0], A[r][1] + Dy*100 ])
A[r][1] += Dy
}
var dist = function(a, b, Dy){
var dx = a[0] - b[0]
var dy = a[1] - (b[1] + Dy)
return Math.sqrt(dx*dx + dy*dy)
}
var dt = function(a, b, Dy){
var g = 9.8
var v = Math.sqrt( g * (a[1] + b[1] + Dy) )
return dist(a, b, Dy) / v
}
var T = function(A){
var t = 0
for(var i = 1; i < A.length; i++){
t += dt(A[i-1], A[i], 0)
}
return t
}
var perturb = function(a,b,c){
var Dy = 0.4 * (Math.random()-0.5)
return ( dt(a,b,Dy) + dt(c,b,Dy) < dt(a,b,0) + dt(c,b,0) ) ?
Dy : 0
}
var iter = 0
var render = function(){
window.requestAnimationFrame(render)
ctx.clearRect(0, 0,canvas.width, canvas.height)
// ctx.fillStyle = "green"
// // line(C)
// ctx.fillText(" Total Time = " + T(C).toFixed(4), 350, 60)
ctx.font = '48px serif';
ctx.fillStyle = "black"
ctx.fillText("Brachistochrone", 175, 50)
ctx.font = '10px serif';
ctx.strokeStyle = "hsl("+120+",100%,40%)"
line(L)
ctx.fillStyle = ctx.strokeStyle
ctx.fillText(" Total Time = " + T(L).toFixed(4), 350, 70)
ctx.strokeStyle = "red"
line(X)
ctx.fillStyle = ctx.strokeStyle
ctx.fillText(" Total Time = " + T(X).toFixed(4), 350, 80)
ctx.strokeStyle = "blue"
line(S)
ctx.fillStyle = ctx.strokeStyle
ctx.fillText(" Total Time = " + T(S).toFixed(4), 350, 90)
iter += iter < 1 ? 0.01 : -1
for(var i = 0; i < 20; i+= 0.5){
var t = i + iter
ctx.strokeStyle = "hsl("+(t*20)+",100%,50%)"
ctx.fillStyle = ctx.strokeStyle
//recomputing a lot
var A = [iso(L, 0, t), iso(X, 0, t), iso(S, 0, t)]
line(A)
for(var j = 0; j < A.length; j++){
if (A[j][0] < 500)
draw_ball(A[j])
}
}
for(var i = 0; i < 400; i++)
rand(S)
}
render()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment