|
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) ; |