Skip to content
Snippets Groups Projects
Select Git revision
  • main
  • arts-xml-data-2.6
  • arts-xml-data-2.4
  • arts-xml-data-2.2
  • arts-xml-data-2.0
5 results

ChangeLog

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    canvas.js 27.23 KiB
    // christopher pietsch
    // cpietsch@gmail.com
    // 2015-2018
    
    
    function Canvas() {
        var margin = {
            top: 20,
            right: 50,
            bottom: 30,
            left: 50
        };
    
        var minHeight = 400;
        var width = window.innerWidth - margin.left - margin.right;
        var widthOuter = window.innerWidth;
        var height = window.innerHeight < minHeight ? minHeight : window.innerHeight;
    
        var scale;
        var scale1 = 1;
        var scale2 = 1;
        var scale3 = 1;
        var allData = [];
    
        var translate = [0, 0];
        var scale = 1;
        var timeDomain = [];
        var loadImagesCue = [];
    
        var x = d3.scale.ordinal()
            .rangeBands([margin.left, width + margin.left], 0.2);
    
        var Quadtree = d3.geom.quadtree()
            .x(function (d) {
                return d.x;
            })
            .y(function (d) {
                return d.y;
            });
    
        var quadtree;
    
        var maxZoomLevel = utils.isMobile() ? 400 : 800;
    
        var zoom = d3.behavior.zoom()
            .scaleExtent([1, maxZoomLevel])
            .size([width, height])
            .on("zoom", zoomed)
            .on("zoomend", zoomend)
            .on("zoomstart", zoomstart);
    
        var canvas;
        var config;
        var container;
        var entries;
        var years;
        var data;
        var rangeBand = 0;
        var rangeBandImage = 0;
        var imageSize = 256;
        var imageSize2 = 1024;
        var imageSize3 = 4000;
        var collumns = 4;
        var renderer, stage;
    
        var svgscale, voronoi;
    
        var selectedImageDistance = 0;
        var selectedImage = null;
    
        var drag = false;
        var sleep = false
    
        var stagePadding = 40;
        var imgPadding;
    
        var bottomPadding = 70;
        var extent = [0, 0];
        var bottomZooming = true;
    
        var touchstart = 0;
        var vizContainer;
        var spriteClick = false
    
        var state = {
            lastZoomed: 0,
            zoomingToImage: false,
            mode: "time",
            init: false
        };
    
        var zoomedToImage = false;
        var zoomedToImageScale = 30;
        var zoomBarrier = 2;
    
        var startTranslate = [0, 0];
        var startScale = 0;
        var cursorCutoff = 1;
        var zooming = false;
        var detailContainer = d3.select(".sidebar")
        var timelineData;
        var stage, stage1, stage2, stage3, stage4, stage5;
        var timelineHover = false;
        var tsne = []
        var tsneIndex = {}
    
        var scaleFactor = 0.9
    
        function canvas() {}
    
        canvas.rangeBand = function () {
            return rangeBand
        }
        canvas.width = function () {
            return width
        }
        canvas.height = function () {
            return height
        }
        canvas.rangeBandImage = function () {
            return rangeBandImage
        }
        canvas.zoom = zoom
        canvas.selectedImage = function () {
            return selectedImage
        }
        canvas.x = x
    
        canvas.resize = function () {
            if (!state.init) return;
            width = window.innerWidth - margin.left - margin.right;
            height = window.innerHeight < minHeight ? minHeight : window.innerHeight;
            widthOuter = window.innerWidth;
            renderer.resize(width + margin.left + margin.right, height);
            canvas.makeScales();
            canvas.project();
        }
    
        canvas.makeScales = function () {
            x.rangeBands([margin.left, width + margin.left], 0.2)
    
            rangeBand = x.rangeBand();
            rangeBandImage = x.rangeBand() / collumns;
    
            imgPadding = rangeBand / collumns / 2;
    
            scale1 = imageSize / (x.rangeBand() / collumns);
            scale2 = imageSize2 / (x.rangeBand() / collumns);
            scale3 = imageSize3 / (x.rangeBand() / collumns);
    
            stage3.scale.x = 1 / scale1;
            stage3.scale.y = 1 / scale1;
            stage3.y = height;
    
            stage4.scale.x = 1 / scale2;
            stage4.scale.y = 1 / scale2;
            stage4.y = height;
    
            stage5.scale.x = 1 / scale3;
            stage5.scale.y = 1 / scale3;
            stage5.y = height;
    
            timeline.rescale(scale1)
    
            cursorCutoff =  1/scale1 * imageSize * 0.48
            zoomedToImageScale = 0.3 / (x.rangeBand() / collumns / width)
            // console.log("zoomedToImageScale", zoomedToImageScale)
        }
    
        canvas.init = function (_data, _timeline, _config) {
            data = _data;
            config = _config;
    
            container = d3.select(".page").append("div").classed("viz", true);
            detailVue._data.structure = config.detail.structure
    
            collumns = config.projection.columns;
            imageSize = config.loader.textures.medium.size;
            imageSize2 = config.loader.textures.detail.size;
    
            if (config.loader.textures.big) {
                imageSize3 = config.loader.textures.big.size;
            }
    
            PIXI.settings.SCALE_MODE = 1
    
            var renderOptions = {
                resolution: 1,
                antialiasing: false,
                width: width + margin.left + margin.right,
                height: height
            };
            renderer = new PIXI.WebGLRenderer(renderOptions);
            renderer.backgroundColor = parseInt(config.style.canvasBackground.substring(1), 16)
            window.renderer = renderer;
    
            var renderElem = d3.select(container.node().appendChild(renderer.view));
    
            stage = new PIXI.Container();
            stage2 = new PIXI.Container();
            stage3 = new PIXI.Container();
            stage4 = new PIXI.Container();
            stage5 = new PIXI.Container();
    
            stage.addChild(stage2);
            stage2.addChild(stage3);
            stage2.addChild(stage4);
            stage2.addChild(stage5);
    
            _timeline.forEach(function (d) {
                d.type = "timeline";
            });
    
            var canvasDomain = d3.nest()
                .key(function (d) {
                    return d.year;
                })
                .entries(_data.concat(_timeline))
                .sort(function (a, b) {
                    return a.key - b.key;
                })
                .map(function (d) {
                    return d.key;
                })
    
            timeDomain = canvasDomain.map(function (d) {
                return {
                    key: d,
                    values: _timeline.filter(function (e) {
                        return d == e.year;
                    })
                }
            })
    
            timeline.init(timeDomain)
            x.domain(canvasDomain);
            canvas.makeScales();
    
            // add preview pics
            data.forEach(function (d, i) {
                var sprite = new PIXI.Sprite(PIXI.Texture.WHITE);
    
                sprite.anchor.x = 0.5;
                sprite.anchor.y = 0.5;
    
                sprite.scale.x = scaleFactor;
                sprite.scale.y = scaleFactor;
    
                sprite._data = d;
                d.sprite = sprite;
    
                stage3.addChild(sprite);
    
            })
    
    
            vizContainer = d3.select(".viz")
                .call(zoom)
                .on("mousemove", mousemove)
                .on("dblclick.zoom", null)
                .on("touchstart", function (d) {
                    mousemove(d);
                    touchstart = new Date() * 1;
                })
                .on("touchend", function (d) {
                    var touchtime = (new Date() * 1) - touchstart;
                    if (touchtime > 250) return;
                    if (selectedImageDistance > 15) return;
                    if (selectedImage && !selectedImage.id) return;
                    if (selectedImage && !selectedImage.active) return;
                    if (drag) return;
    
                    zoomToImage(selectedImage, 1400 / Math.sqrt(Math.sqrt(scale)))
                })
                .on("click", function () {
                    console.log("click");
                    // if (spriteClick) {
                    //     spriteClick = false;
                    //     return;
                    // }
    
                    // if (selectedImage && !selectedImage.id) return;
                    if (drag) return;
                    // if (selectedImageDistance > cursorCutoff) return;
                    // if (selectedImage && !selectedImage.active) return;
                    if (timelineHover) return;
                    // console.log(selectedImage)
    
                    // if (Math.abs(zoomedToImageScale - scale) < 0.1) {
                    //     canvas.resetZoom();
                    // } else {
                    //     zoomToImage(selectedImage, 1400 / Math.sqrt(Math.sqrt(scale)));
                    // }
                    console.log("zoom to ")
                    zoomToImage(selectedImage, 1400 / Math.sqrt(Math.sqrt(scale)));
    
                })
    
            canvas.project();
            animate();
    
            // selectedImage = data.find(d => d.id == 88413)
            // showDetail(selectedImage)
            state.init = true;
        };
    
        canvas.addTsneData = function (d) {
            console.time("tsne")
            var clean = d.map(function (d) {
                return {
                    id: d.id,
                    x: parseFloat(d.x),
                    y: parseFloat(d.y)
                }
            })
            var xExtent = d3.extent(clean, function (d) {
                return d.x
            })
            var yExtent = d3.extent(clean, function (d) {
                return d.y
            })
    
            var x = d3.scale.linear().range([0, 1]).domain(xExtent)
            var y = d3.scale.linear().range([0, 1]).domain(yExtent)
    
            d.forEach(function (d) {
                tsneIndex[d.id] = [
                    x(d.x),
                    y(d.y)
                ]
            })
    
            data.forEach(d => {
                d.active = tsneIndex[d.id] !== undefined
            })
    
            console.timeEnd("tsne")
        }
    
    
        function mousemove(d) {
            if (timelineHover) return;
    
            var mouse = d3.mouse(vizContainer.node());
            var p = toScreenPoint(mouse);
    
            var distance = 200
    
            var best = nearest(p[0] - imgPadding, p[1] - imgPadding, {
                d: distance,
                p: null
            }, quadtree);
    
            selectedImageDistance = best.d;
            // console.log(cursorCutoff,scale, scale1,  selectedImageDistance)
    
            
                if (best.p) {
                    var d = best.p;
                    var center = [((d.x + imgPadding) * scale) + translate[0], (height + d.y + imgPadding) * scale + translate[1]];
                    zoom.center(center);
                    selectedImage = d;
                }
    
                container.style("cursor", function () {
                    return ((selectedImageDistance < cursorCutoff) && selectedImage.active) ? "pointer" : "default";
                });
            
        }
    
    
        function stackLayout(data, invert) {
    
            var years = d3.nest()
                .key(function (d) {
                    return d.year;
                })
                .entries(data)
    
            years.forEach(function (year) {
                var startX = x(year.key);
                var total = year.values.length;
                year.values.sort(function (a, b) {
                    return b._layerId - a._layerId;
                })
    
                year.values.forEach(function (d, i) {
                    var row = (Math.floor(i / collumns) + 2);
                    d.ii = i;
    
                    d.x = startX + ((i % collumns) * (rangeBand / collumns));
                    d.y = (invert ? 1 : -1) * (row * (rangeBand / collumns));
    
                    d.x1 = d.x * scale1 + imageSize / 2;
                    d.y1 = d.y * scale1 + imageSize / 2;
    
                    d.sprite.scale.x = d.sprite.scale.y = scaleFactor
    
                    if (d.sprite.position.x == 0) {
                        d.sprite.position.x = d.x1;
                        d.sprite.position.y = d.y1;
                    }
    
                    if (d.sprite2) {
                        d.sprite2.position.x = d.x * scale2 + imageSize2 / 2;
                        d.sprite2.position.y = d.y * scale2 + imageSize2 / 2;
                    }
    
                    d.order = (invert ? 1 : 1) * (total - i);
                })
            })
        }
    
        canvas.distance = function (a, b) {
            return Math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
        }
    
        function toScreenPoint(p) {
            var p2 = [0, 0];
    
            p2[0] = p[0] / scale - translate[0] / scale;
            p2[1] = (p[1] / scale - height) - translate[1] / scale;
    
            return p2;
        }
    
        function imageAnimation() {
            var sleep = true
    
            data.forEach(function (d, i) {
                var diff;
                diff = (d.x1 - d.sprite.position.x);
                if (Math.abs(diff) > 0.1) {
                    d.sprite.position.x += diff * 0.1;
                    sleep = false;
                }
    
                diff = (d.y1 - d.sprite.position.y);
                if (Math.abs(diff) > 0.1) {
                    d.sprite.position.y += diff * 0.1
                    sleep = false;
                }
    
                diff = (d.alpha - d.sprite.alpha);
                if (Math.abs(diff) > 0.01) {
                    d.sprite.alpha += diff * 0.2
                    sleep = false;
                }
    
                d.sprite.visible = d.visible && d.sprite.alpha > 0.1;
    
                // d.sprite.visible = d.visible;
    
    
                if (d.sprite2) {
                    diff = (d.alpha2 - d.sprite2.alpha);
                    if (Math.abs(diff) > 0.01) {
                        d.sprite2.alpha += diff * 0.2
                        sleep = false;
                    }
    
                    d.sprite2.visible = d.sprite2.alpha > 0.1;
                    //else d.sprite2.visible = d.visible;
                }
            });
            return sleep
        }
    
    
        canvas.wakeup = function () {
            sleep = false
        }
    
        canvas.setMode = function (mode) {
            state.mode = mode
            canvas.project()
        }
    
        function animate(time) {
            requestAnimationFrame(animate);
            loadImages();
            if (sleep) return
            sleep = imageAnimation();
            renderer.render(stage);
        }
    
        function zoomToYear(d) {
    
            var xYear = x(d.year);
            var scale = 1 / (rangeBand * 4 / width);
            var padding = rangeBand * 1.5
            var translateNow = [-scale * (xYear - padding), -scale * (height + d.y)];
    
            vizContainer
                .call(zoom.translate(translate).event)
                .transition().duration(2000)
                .call(zoom.scale(scale).translate(translateNow).event)
        }
    
        function zoomToImage(d, duration) {
            state.zoomingToImage = true;
            zoom.center(null);
            loadMiddleImage(d);
            d3.select(".tagcloud").classed("hide", true);
            var padding = x.rangeBand() / collumns / 2;
            var sidbar = width / 8;
            var scale = 0.3 / (x.rangeBand() / collumns / width);
            var translateNow = [(-scale * d.x) - sidbar + width/2 - imageSize/2, (-scale * (height + d.y)) + height/4];
    
            console.log(translateNow, width, scale, d.x)
    
            zoomedToImageScale = scale;
    
            setTimeout(function () {
                hideTheRest(d);
            }, duration / 2);
    
            vizContainer
                .call(zoom.translate(translate).event)
                .transition().duration(duration)
                .call(zoom.scale(scale).translate(translateNow).event)
                .each("end", function () {
                    zoomedToImage = true;
                    selectedImage = d;
                    hideTheRest(d);
                    showDetail(d);
                    loadBigImage(d, "click");
                    state.zoomingToImage = false;
                })
        }
    
        function showDetail(d) {
            // console.log("show detail", d)
    
            detailContainer
                .select(".outer")
                .node()
                .scrollTop = 0;
    
            detailContainer
                .classed("hide", false)
                .classed("sneak", utils.isMobile())
    
            // needs to be done better
            var detailData = {}
            for (field in selectedImage) {
                if (field[0] === '_') detailData[field] = selectedImage[field]
            }
            detailData['_id'] = selectedImage.id
            detailData['_keywords'] = selectedImage.keywords
            detailData['_year'] = selectedImage.year
            detailData['_imagenum'] = selectedImage.imagenum || 1
            detailVue._data.item = detailData
            detailVue._data.id = d.id
            detailVue._data.page = d.page
    
        }
    
        canvas.changePage = function (id, page) {
            console.log("changePage", id, page, selectedImage);
            selectedImage.page = page
            detailVue._data.page = page
            clearBigImages();
            loadBigImage(selectedImage)
        }
    
    
        function hideTheRest(d) {
            return;
            data.forEach(function (d2) {
                if (d2.id !== d.id) {
                    d2.alpha = 0;
                    d2.alpha2 = 0;
    
                }
            })
        }
    
        function showAllImages() {
            data.forEach(function (d) {
                d.alpha = d.active ? 1 : 0.2;;
                d.alpha2 = d.visible ? 1 : 0;
            })
        }
    
        function zoomed() {
            translate = d3.event.translate;
            scale = d3.event.scale;
            if (!startTranslate) startTranslate = translate
            drag = startTranslate && translate !== startTranslate;
            // check borders
            var x1 = -1 * translate[0] / scale;
            var x2 = (x1 + (widthOuter / scale));
    
            if (d3.event.sourceEvent != null) {
                if (x1 < 0) {
                    translate[0] = 0;
                } else if (x2 > widthOuter) {
                    translate[0] = ((widthOuter * scale) - widthOuter) * -1;
                }
    
                zoom.translate([translate[0], translate[1]]);
    
                x1 = -1 * translate[0] / scale;
                x2 = (x1 + (width / scale))
            }
    
            if (zoomedToImageScale != 0 && scale > zoomedToImageScale*0.9 && !zoomedToImage && selectedImage && selectedImage.type == "image") {
    
                zoomedToImage = true;
                zoom.center(null);
                zoomedToImageScale = scale;
                hideTheRest(selectedImage);
                showDetail(selectedImage)
            }
    
            if (zoomedToImage && zoomedToImageScale *0.8 > scale) {
                // console.log("clear")
                zoomedToImage = false;
                state.lastZoomed = 0;
                showAllImages();
                clearBigImages();
                detailContainer.classed("hide", true)
            }
    
            timeline.update(x1, x2, scale, translate, scale1);
    
            // toggle zoom overlays
            if (scale > zoomBarrier) {
                d3.select(".tagcloud").classed("hide", true);
                d3.select(".searchbar").classed("hide", true);
                d3.select(".infobar").classed("sneak", true);
            } else {
                d3.select(".tagcloud").classed("hide", false);
                d3.select(".searchbar").classed("hide", false);
            }
    
    
            stage2.scale.x = d3.event.scale;
            stage2.scale.y = d3.event.scale;
            stage2.x = d3.event.translate[0];
            stage2.y = d3.event.translate[1];
    
            sleep = false
        }
    
        function zoomstart(d) {
            zooming = true;
            startTranslate = false;
            drag = false
            startScale = scale;
            sleep = false
        }
    
        function zoomend(d) {
            drag = startTranslate && translate !== startTranslate;
            zooming = false;
            filterVisible();
            sleep = false
    
            if (zoomedToImage && !selectedImage.big && state.lastZoomed != selectedImage.id && !state.zoomingToImage) {
                loadBigImage(selectedImage, "zoom");
            }
        }
    
        canvas.highlight = function () {
            data.forEach(function (d, i) {
                d.alpha = d.highlight ? 1 : 0.2;
            });
            canvas.wakeup();
        }
    
        // canvas.project = function () {
        //     sleep = false
        //     canvas.split();
        //     canvas.resetZoom();
        // }
    
        canvas.project = function () {
            sleep = false
            if (state.mode == "tsne") {
                scaleFactor = 0.5
                canvas.projectTSNE();
            } else {
                scaleFactor = 0.9
                canvas.split();
            }
            canvas.resetZoom();
        }
    
        canvas.projectTSNE = function () {
    
            var marginBottom = -height / 2.5;
    
            var inactive = data.filter(function (d) {
                return !d.active;
            });
            var inactiveSize = inactive.length;
    
            var active = data.filter(function (d) {
                return d.active;
            });
    
            // inactive.sort(function (a, b) {
            //     return a.rTSNE - b.rTSNE
            // });
    
            var dimension = Math.min(width, height) * 0.8
    
            inactive.forEach(function (d, i) {
                var r = dimension / 1.9 + Math.random() * 40;
                var a = -Math.PI / 2 + (i / inactiveSize) * 2 * Math.PI;
    
                d.x = r * Math.cos(a) + width / 2 + margin.left;
                d.y = r * Math.sin(a) + marginBottom;
            });
    
            active.forEach(function (d) {
    
                var factor = height / 2;
                var tsneEntry = tsneIndex[d.id]
                if(tsneEntry) {
                    d.x = (tsneEntry[0] * dimension) + width / 2 - dimension / 2 + margin.left;
                    d.y = (tsneEntry[1] * dimension) - dimension / 2 + marginBottom;
                }
                // var tsneEntry = tsne.find(function (t) {
                //     return t.id == d.id
                // })
               
    
            })
    
            data.forEach(function (d) {
                d.x1 = d.x * scale1 + imageSize / 2;
                d.y1 = d.y * scale1 + imageSize / 2;
    
                d.sprite.scale.x = d.sprite.scale.y = scaleFactor
    
                if (d.sprite.position.x == 0) {
                    d.sprite.position.x = d.x1;
                    d.sprite.position.y = d.y1;
                }
    
                if (d.sprite2) {
                    d.sprite2.position.x = d.x * scale2 + imageSize2 / 2;
                    d.sprite2.position.y = d.y * scale2 + imageSize2 / 2;
                }
            });
    
    
            quadtree = Quadtree(data);
            //chart.resetZoom();
    
    
        }
    
        canvas.resetZoom = function () {
            var duration = 1400;
    
            extent = d3.extent(data, function (d) {
                return d.y;
            });
    
            var y = -extent[1]*2 -height -extent[0]/4;
            // y = (extent[1] / -1.5) - bottomPadding
    
            vizContainer
                .call(zoom.translate(translate).event)
                .transition().duration(duration)
                .call(zoom.translate([-width/2, y]).scale(2).event)
        }
    
        canvas.split = function () {
            var active = data.filter(function (d) {
                return d.active;
            })
            stackLayout(active, false);
            var inactive = data.filter(function (d) {
                return !d.active;
            })
            stackLayout(inactive, true);
            quadtree = Quadtree(data);
        }
    
        function filterVisible() {
            var zoomScale = scale;
            if (zoomedToImage) return;
    
            data.forEach(function (d, i) {
                var p = d.sprite.position;
                var x = (p.x / scale1) + translate[0] / zoomScale;
                var y = ((p.y / scale1) + (translate[1]) / zoomScale);
                var padding = (width/3) / scale;
    
                if (x > (-padding) && x < ((width / zoomScale) + padding) && y + height < (height / zoomScale + padding) && y > (height * -1) - padding) {
                    d.visible = true;              
                } else {
                    d.visible = false;
                }
            });
    
            var visible = data.filter(function (d) {
                return d.visible;
            });
    
            console.log(visible.length)
    
            if (visible.length < 200) {
                data.forEach(function (d) {
                    if (d.visible && d.loaded && d.active) {d.alpha2 = 1 }
                    else if (d.visible && !d.loaded && d.active) loadImagesCue.push(d);
                    else {d.alpha2 = 0}
                })
            } 
            // else {
            //     data.forEach(function (d) {
            //         d.alpha2 = 0;
            //         // d.alpha = 1
            //         d.visible = true;
            //     })
            // }
        }
    
        function loadMiddleImage(d) {
            if (d.loaded) {
                d.alpha2 = 1;
                return;
            }
    
            // console.log("load", d)
            var url = config.loader.textures.detail.url + d.id + '.jpg'
            var texture = new PIXI.Texture.fromImage(url)
            var sprite = new PIXI.Sprite(texture);
    
            var update = function () {
                sleep = false
                d.visible = false
            }
    
            sprite.on('added', update)
            texture.once('update', update)
    
            sprite.scale.x = scaleFactor;
            sprite.scale.y = scaleFactor;
    
            sprite.anchor.x = 0.5;
            sprite.anchor.y = 0.5;
            sprite.position.x = d.x * scale2 + imageSize2 / 2;
            sprite.position.y = d.y * scale2 + imageSize2 / 2;
            sprite._data = d;
            stage4.addChild(sprite);
            d.sprite2 = sprite;
            d.alpha2 = d.highlight;
            d.loaded = true;
            sleep = false
        }
    
        function loadBigImage(d) {
            if (!config.loader.textures.big) {
                loadMiddleImage(d)
                return
            }
    
            state.lastZoomed = d.id;
            var page = d.page ? '_' + d.page : ''
            var url = config.loader.textures.big.url + d.id + page + ".jpg";
    
            console.log(url)
    
            var texture = new PIXI.Texture.from(url)
            var sprite = new PIXI.Sprite(texture);
            var res = config.loader.textures.big.size
    
            var updateSize = function () {
                var size = Math.max(texture.width, texture.height)
                sprite.scale.x = sprite.scale.y = (imageSize3 / size) * scaleFactor;
                sleep = false
            }
    
            sprite.on('added', updateSize)
            texture.once('update', updateSize)
    
            sprite.anchor.x = 0.5;
            sprite.anchor.y = 0.5;
            sprite.position.x = d.x * scale3 + imageSize3 / 2;
            sprite.position.y = d.y * scale3 + imageSize3 / 2;
            sprite._data = d;
            d.big = true;
            stage5.addChild(sprite);
            sleep = false
        }
    
        function clearBigImages() {
            while (stage5.children[0]) {
                stage5.children[0]._data.big = false;
                stage5.removeChild(stage5.children[0]);
                sleep = false
            }
        }
    
        function loadImages() {
            if (zooming) return;
            if (zoomedToImage) return;
    
            if (loadImagesCue.length) {
                var d = loadImagesCue.pop();
                // console.log(d.id)
                if (!d.loaded) {
                    loadMiddleImage(d);
                }
            }
            if (loadImagesCue.length) {
                var d = loadImagesCue.pop();
                // console.log(d.id)
                if (!d.loaded) {
                    loadMiddleImage(d);
                }
            }
            if (loadImagesCue.length) {
                var d = loadImagesCue.pop();
                // console.log(d.id)
                if (!d.loaded) {
                    loadMiddleImage(d);
                }
            }
            // if (loadImagesCue.length) {
            //     var d = loadImagesCue.pop();
            //     console.log(d.id)
            //     if (!d.loaded) {
            //         loadMiddleImage(d);
            //     }
            // }
        }
    
        function nearest(x, y, best, node) {
            // mike bostock https://bl.ocks.org/mbostock/4343214
            var x1 = node.x1,
                y1 = node.y1,
                x2 = node.x2,
                y2 = node.y2;
            node.visited = true;
            //console.log(node, x , x1 , best.d);
            //return;
            // exclude node if point is farther away than best distance in either axis
            if (x < x1 - best.d || x > x2 + best.d || y < y1 - best.d || y > y2 + best.d) {
                return best;
            }
            // test point if there is one, potentially updating best
            var p = node.point;
            if (p) {
                p.scanned = true;
                var dx = p.x - x,
                    dy = p.y - y,
                    d = Math.sqrt(dx * dx + dy * dy);
                if (d < best.d) {
                    best.d = d;
                    best.p = p;
                }
            }
            // check if kid is on the right or left, and top or bottom
            // and then recurse on most likely kids first, so we quickly find a 
            // nearby point and then exclude many larger rectangles later
            var kids = node.nodes;
            var rl = (2 * x > x1 + x2),
                bt = (2 * y > y1 + y2);
            if (kids[bt * 2 + rl]) best = nearest(x, y, best, kids[bt * 2 + rl]);
            if (kids[bt * 2 + (1 - rl)]) best = nearest(x, y, best, kids[bt * 2 + (1 - rl)]);
            if (kids[(1 - bt) * 2 + rl]) best = nearest(x, y, best, kids[(1 - bt) * 2 + rl]);
            if (kids[(1 - bt) * 2 + (1 - rl)]) best = nearest(x, y, best, kids[(1 - bt) * 2 + (1 - rl)]);
    
            return best;
        }
    
    
        return canvas;
    
    }