|
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<sub>' + (xI + 1) + '</sub> = ' + r0[xI].toPrecision(8)) ;
|
|
}
|
|
|
|
var update_ytext = function() {
|
|
d3.select('#ylabel').html('x<sub>' + (yI + 1) + '</sub> = ' + 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) ; |