var copyPosition = function() {
copyToClipboard(r0.toString());
}
var copyToClipboard = function(text) {
window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
} ;
var fscale = 3 ;
var fscaleSlider = d3.select("#fscaleSlider")
.attr('value', fscale)
.on("input", function() {
fscale = +this.value;
set_zscale() ;
z_range() ;
redraw() ;
}) ;
/// *******************
///
/// initialize position:
///
/// *******************
var rmin = Math.pow(2, 1/6) ; // the minimizer of the lennard-jones potential
var r0 = [0, 0, 0, rmin, 0, 0, 0, rmin, 0, 0, 0, rmin] ; // , rmin, rmin, rmin
var N = r0.length / 3 ; // number of L-J particles
var xI = 0 ;
var yI = 1 ;
var Ndim = r0.length ; // number of free dimensions / degrees of freedom
var canvas = d3.select('#canvas_1') ;
var size = 127 ;
canvas
.attr('width', size)
.attr('height', size)
var element = document.getElementById("canvas_1") ;
var c = element.getContext("2d") ;
// read the width and height of the canvas
var width = element.width ;
var height = element.height ;
var body = d3.select(document.body) ;
var td = d3.select('#svg_td') ;
var copyButton = td.append('input')
.attr('type', 'button')
.attr('value', 'current position')
.attr('onClick', 'copyPosition() ; return false ;') ;
td.append('br')
var dimSelect = [] ;
var Ndsel = 2 ;
dimSelect1 = td.append('input')
.attr('id', 'dimSelect1')
.attr('type', 'range')
.attr('value', 1)
.attr('min', 1)
.attr('max', Ndim - 1)
.attr('step', 1)
.on('input', function() {
if(Number(this.value) >= Number(dimSelect2.node().value)) {
dimSelect2.node().value = Number(this.value) + 1 ;
}
xI = Number(dimSelect1.node().value) - 1 ;
yI = Number(dimSelect2.node().value) - 1 ;
dselOut1.html('i = ' + (xI + 1)) ;
dselOut2.html('j = ' + (yI + 1)) ;
update_figure(true) ;
}) ;
var dselOut1 = td.append('output')
.attr('id', 'dimSelect1out')
.attr('for', 'dimSelect1')
.html('i = ' + (xI + 1)) ;
td.append('br') ;
dimSelect2 = td.append('input')
.attr('id', 'dimSelect2')
.attr('type', 'range')
.attr('value', 2)
.attr('min', 2)
.attr('max', Ndim)
.attr('step', 1)
.on('input', function() {
if(Number(this.value) <= Number(dimSelect1.node().value)) {
dimSelect1.node().value = Number(this.value) - 1 ;
}
xI = Number(dimSelect1.node().value) - 1 ;
yI = Number(dimSelect2.node().value) - 1 ;
dselOut1.html('i = ' + (xI + 1)) ;
dselOut2.html('j = ' + (yI + 1)) ;
update_figure(true) ;
}) ;
var dselOut2 = td.append('output')
.attr('id', 'dimSelect2out')
.attr('for', 'dimSelect2')
.html('j = ' + (yI + 1)) ;
function setPixel(imageData, x, y, r, g, b, a) {
var index = (x + y * imageData.width) * 4 ;
imageData.data[index + 0] = r ;
imageData.data[index + 1] = g ;
imageData.data[index + 2] = b ;
imageData.data[index + 3] = a ;
}
// create a new pixel array
var imageData = c.createImageData(width, height) ;
var xRange = [-2.5, 2.5] ;
var yRange = [-2.5, 2.5] ;
var xscale ;
var yscale ;
var set_scale = function() {
xscale = d3.scale.linear()
.domain([0, width])
.range(xRange) ;
yscale = d3.scale.linear()
.domain([0, height])
.range(yRange) ;
} ;
set_scale() ;
var zscale = d3.scale.linear() ;
var zdomain ;
var zrange = ["rgb(0, 88, 11)", "rgb(0, 255, 33)", "rgb(0, 3, 33)", "rgb(0, 11, 55)", "rgb(0, 88, 155)", "rgb(0, 88, 155)", "rgb(111, 0, 33)", "rgb(122, 0, 33)", "rgb(155, 0, 88)"] ;
var f = [] ;
var fvec = [] ;
var fmin = Infinity, fmax = -Infinity ;
var lennard_jones = function(d) { // Lennard-Jones term
var V = Math.pow(d, -6) ;
V = V * V - V ;
return V ;
}
var objective_function = function(r) {
var V = 0 ;
for(var i = 0 ; i < N - 1 ; i++) {
for(var j = i + 1 ; j < N ; j++) {
var d2 = 0 ;
for(k = 0 ; k < 3 ; k++) {
var drk = r[i * 3 + k] - r[j * 3 + k] ; // 3 dimensions per particle: (x, y, z)
d2 += drk * drk ;
}
var d = Math.sqrt(d2) ; // the distance between the spheres
V += lennard_jones(d) ;
}
}
V *= 4 ; // so each perfect contact scores a -1 energy
return V ;
}
var stepSize = 0.00001 ; // for numerical gradient (secant)
var gradient_function = function(r) {
var g = [] ;
for(var i = 0 ; i < r.length ; i++) {
var r1 = r.slice(0) ;
r1[i] += stepSize ;
fp = objective_function(r1) ;
r1[i] -= 2 * stepSize ;
fm = objective_function(r1) ;
g[i] = (fp - fm) / (2 * stepSize) ;
}
// console.log('grad', g, r.length)
return g ;
} ;
var xval = [] ;
var yval = [] ;
function update_image() {
var count = 0 ;
for (var i = 0 ; i < height ; i++) {
f[i] = [] ;
xval[i] = [] ;
yval[i] = [] ;
for(var j = 0 ; j < width ; j++) {
var x = xRange[0] + (i / width) * (xRange[1] - xRange[0]) ;
var y = yRange[0] + (j / width) * (yRange[1] - yRange[0]) ;
var r = r0.slice(0) ;
for (k = 0 ; k < r0.length ; k++) {
if(k == xI) r[xI] = x + r0[xI] ;
if(k == yI) r[yI] = y + r0[yI] ;
}
f[i][j] = objective_function(r) ;
xval[i][j] = x ;
yval[i][j] = y ;
// console.log('x', x, 'y', y, 'r', r, 'f', f[i][j])
if(f[i][j] < fmin) {
fmin = f[i][j] ;
ropt = r.slice(0) ;
}
if(f[i][j] > fmax) fmax = f[i][j] ;
fvec[count] = f[i][j] ;
count++ ;
}
}
var fmid = 0.5 * (fmin + fmax) ;
var fsort = fvec.slice(0).sort(d3.ascending)
var fmed = d3.quantile(fsort, 0.5) ;
var fran = fmed - fmin ;
// zdomain = [fmin, d3.quantile(fsort, 1/3), d3.quantile(fsort, 2/3), Infinity] ;
set_zscale() ;
//.domain([fmin, d3.quantile(fvec, 0.1), d3.quantile(fvec, 0.45), d3.quantile(fvec, 0.5), d3.quantile(fvec, 0.75), d3.quantile(fvec, 0.95), fmax])
//.range(["rgb(0, 0, 44)", "rgb(11, 33, 121)", "rgb(22, 88, 121)", "rgb(111, 111, 111)", "rgb(121, 88, 22)", "rgb(111, 11, 0)", "rgb(44, 0, 0)"]);
} ; // end update_image
var set_zscale = function() {
zdomain = [fmin, fmin + .01 * fscale, fmin + .1 * fscale, fmin + 0.2 * fscale, fmin + 0.4 * fscale, fmin + .8 * fscale, fmin + 1.6 * fscale, fmax * 0.1 * fscale, Infinity] ;
zdomain.sort(d3.ascending) ;
}
var redraw = function() {
for (var i = 0 ; i < height ; i++) {
for(var j = 0 ; j < width ; j++) {
var r = d3.rgb(zscale(f[i][j])).r ;
var g = d3.rgb(zscale(f[i][j])).g ;
var b = d3.rgb(zscale(f[i][j])).b ;
if(i == Math.ceil(height / 2) && j == Math.ceil(width / 2)) {
r = 255 ;
g = 255 ;
b = 0 ;
}
setPixel(imageData, i, j, r, g, b, 255) ; // 255 opaque
}
}
// copy the image data back onto the canvas
c.putImageData(imageData, 0, 0) ; // at coords 0,0
}
var update_figure = function (scaleSwitch) {
update_image() ;
if(scaleSwitch) {
set_scale() ;
z_range() ;
}
redraw() ;
update_text() ;
}
var updating = false ;
var update_position = function(dx, dy, i, j) {
if(updating) return ;
updating = true ;
var Nstep = 4 ;
var kstep = 0 ;
// console.log('upos', 'r0', r0, dx, dy)
var step = function() {
if(kstep < Nstep) {
r0[i] += dx / Nstep ;
r0[j] += dy / Nstep ;
update_figure() ;
kstep++ ;
var delay = 16 ;
setTimeout(step, delay) ;
// console.log('stepping', kstep)
} else {
// console.log('done stepping', kstep, 'r0', r0)
z_range() ;
redraw() ;
update_text() ;
updating = false ;
}
}
step() ;
}
var update_text = function() {
update_xtext() ;
update_ytext() ;
update_ttext() ;
}
var update_ttext = function() {
d3.select('#temperatureText').text('f(x) = ' + objective_function(r0).toPrecision(6)) ;
}
var update_xtext = function() {
d3.select('#xlabel').html('x' + (xI + 1) + ' = ' + r0[xI].toPrecision(8)) ;
}
var update_ytext = function() {
d3.select('#ylabel').html('x' + (yI + 1) + ' = ' + r0[yI].toPrecision(8)) ;
}
var z_range = function() {
zscale
.domain(zdomain)
.range(zrange) ;
}
update_position(0, 0, xI, yI) ;
canvas.on('click', function() {
var xy = d3.mouse(canvas.node()) ;
xy[0] /= canvas.node().clientWidth ;
xy[1] /= canvas.node().clientHeight ;
xy[0] *= width ;
xy[1] *= height ;
xy[0] = Math.floor(xy[0]) ;
xy[1] = Math.floor(xy[1]) ;
console.log(xy)
Npix = 3 ; // for local search to "snap" to the minimum pixel close to where the user clicked
var minf = f[xy[1]][xy[0]] ;
var mini = xy[1] ;
var minj = xy[0] ;
var iStart = Math.max(0, xy[0] - Npix) ;
var iEnd = Math.min(width - 1, xy[0] + Npix) ;
var jStart = Math.max(0, xy[1] - Npix) ;
var jEnd = Math.min(height - 1, xy[1] + Npix) ;
for (var i = iStart ; i <= iEnd ; i++) {
for (var j = jStart ; j <= jEnd ; j++) {
if(f[i][j] < minf) {
minf = f[i][j] ;
mini = i ;
minj = j ;
}
}
}
update_position(xval[mini][minj], yval[mini][minj], xI, yI) ;
}
) ;
var scrolling = false ;
var scroll = function() {
if(scrolling) return ;
scrolling = true ;
console.log(d3.event)
if(d3.event.wheelDelta !== undefined) {
var delta = d3.event.wheelDelta / Math.abs(d3.event.wheelDelta) ;
}
if(d3.event.detail !== undefined) {
var delta = -d3.event.detail / Math.abs(d3.event.detail) ;
}
var step = .2 ;
if(delta < 0) {
step = 1 + step ;
} else {
step = 1 - step ;
}
xRange[0] = step * xRange[0] ;
xRange[1] = step * xRange[1] ;
yRange[0] = step * yRange[0] ;
yRange[1] = step * yRange[1] ;
set_scale()
update_image() ;
z_range() ;
redraw() ;
scrolling = false ;
} ;
canvas
.on("mousewheel.zoom", null)
.on("DOMMouseScroll.zoom", null) // disables older versions of Firefox
.on("wheel.zoom", null) // disables newer versions of Firefox
canvas.on("mousewheel", scroll) // default scroll wheel listener
canvas.on("DOMMouseScroll.zoom", scroll) ;