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