From 26a00fb355854b6dd7bd33e5915315662574f952 Mon Sep 17 00:00:00 2001 From: Christopher Pietsch <cpietsch@gmail.com> Date: Thu, 23 Apr 2020 16:03:38 +0200 Subject: [PATCH] formating --- js/canvas.js | 1887 +++++++++++++++++++++++++------------------------- 1 file changed, 957 insertions(+), 930 deletions(-) diff --git a/js/canvas.js b/js/canvas.js index 628248b..fb2e10e 100644 --- a/js/canvas.js +++ b/js/canvas.js @@ -2,950 +2,977 @@ // 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() ? 5000 : 2500; - - 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 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() ? 5000 : 2500; + + 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 = 117; + 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 = {}; + + 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.8 / (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; + PIXI.settings.SPRITE_MAX_TEXTURES = Math.min( + PIXI.settings.SPRITE_MAX_TEXTURES, + 16 + ); + + var renderOptions = { + resolution: 1, + antialiasing: false, }; - - var zoomedToImage = false; - var zoomedToImageScale = 117; - 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 = {} - - 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.8 / (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; + renderer = new PIXI.WebGLRenderer( + width + margin.left + margin.right, + height, + 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 = d.scaleFactor; + sprite.scale.y = d.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; } - PIXI.settings.SCALE_MODE = 1; - PIXI.settings.SPRITE_MAX_TEXTURES = Math.min(PIXI.settings.SPRITE_MAX_TEXTURES , 16); - - var renderOptions = { - resolution: 1, - antialiasing: false - }; - renderer = new PIXI.WebGLRenderer(width + margin.left + margin.right, height, 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 = d.scaleFactor; - sprite.scale.y = d.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))); - } - - }) - - 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) - ] - }) - - console.timeEnd("tsne") - } - - - function mousemove(d) { + if (selectedImage && !selectedImage.id) return; + if (drag) return; + if (selectedImageDistance > cursorCutoff) return; + if (selectedImage && !selectedImage.active) return; if (timelineHover) return; + // console.log(selectedImage) - 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 (bottomZooming && best.p && best.p.ii < 3 && selectedImageDistance > 7) { - selectedImage = null; - zoom.center(null); - container.style("cursor", "default"); + if (Math.abs(zoomedToImageScale - scale) < 0.1) { + canvas.resetZoom(); } else { - if (best.p && !zoomedToImage) { - 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"; - }); + zoomToImage(selectedImage, 1400 / Math.sqrt(Math.sqrt(scale))); } - - } - - - 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.keywords.length - a.keywords.length; - }) - - 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; - - 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.sprite.alpha > 0.1; - - 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.8 / (x.rangeBand() / collumns / width); - var translateNow = [(-scale * (d.x - padding / 2)) - sidbar, -scale * (height + d.y)]; - - 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] + }); + + 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)]; + }); + + 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 (bottomZooming && best.p && best.p.ii < 3 && selectedImageDistance > 7) { + selectedImage = null; + zoom.center(null); + container.style("cursor", "default"); + } else { + if (best.p && !zoomedToImage) { + 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.keywords.length - a.keywords.length; + }); + + 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; + + if (d.sprite.position.x == 0) { + d.sprite.position.x = d.x1; + d.sprite.position.y = d.y1; } - 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) { - 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 (d.sprite2) { + d.sprite2.position.x = d.x * scale2 + imageSize2 / 2; + d.sprite2.position.y = d.y * scale2 + imageSize2 / 2; } - if (zoomedToImageScale != 0 && scale > zoomedToImageScale*0.9 && !zoomedToImage && selectedImage && selectedImage.type == "image") { - - zoomedToImage = true; - zoom.center(null); - zoomedToImageScale = scale; - hideTheRest(selectedImage); - showDetail(selectedImage) + 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.sprite.alpha > 0.1; + + if (d.sprite2) { + diff = d.alpha2 - d.sprite2.alpha; + if (Math.abs(diff) > 0.01) { + d.sprite2.alpha += diff * 0.2; + sleep = false; } - 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; - } - - function zoomend(d) { - drag = startTranslate && translate !== startTranslate; - zooming = false; - filterVisible(); - - 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") { - canvas.projectTSNE(); - } else { - 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; - - 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] - bottomPadding; - y = (extent[1] / -3) - bottomPadding - - vizContainer - .call(zoom.translate(translate).event) - .transition().duration(duration) - .call(zoom.translate([0, y]).scale(1).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 = 5; - - 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; - }); - - if (visible.length < 40) { - 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; - }) - } - } - - 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, true) - var sprite = new PIXI.Sprite(texture); - - var update = function () { - sleep = false - } - - sprite.on('added', update) - texture.once('update', update) - - sprite.scale.x = d.scaleFactor; - sprite.scale.y = d.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"; - - var texture = new PIXI.Texture.fromImage(url, true) - 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) * d.scaleFactor; - sleep = false - } - - sprite.on('added', updateSize) - texture.once('update', updateSize) - - if (d.imagenum) { - sprite.on("mousemove", function (s) { - var pos = s.data.getLocalPosition(s.currentTarget) - s.currentTarget.cursor = pos.x > 0 ? "e-resize" : "w-resize" - }) - sprite.on("click", function (s) { - if (drag) return - - s.stopPropagation() - spriteClick = true - var pos = s.data.getLocalPosition(s.currentTarget) - var dir = pos.x > 0 ? 1 : -1 - var page = d.page + dir - var nextPage = page - if (page > d.imagenum - 1) nextPage = 0 - if (page < 0) nextPage = d.imagenum - 1 - - canvas.changePage(d.id, nextPage) - }) - sprite.interactive = true; - } - - 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(); - 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; - } - + 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.8 / (x.rangeBand() / collumns / width); + var translateNow = [ + -scale * (d.x - padding / 2) - sidbar, + -scale * (height + d.y), + ]; + + 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) { + 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; + } + + function zoomend(d) { + drag = startTranslate && translate !== startTranslate; + zooming = false; + filterVisible(); + + 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") { + canvas.projectTSNE(); + } else { + 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; + + 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] - bottomPadding; + y = extent[1] / -3 - bottomPadding; + + vizContainer + .call(zoom.translate(translate).event) + .transition() + .duration(duration) + .call(zoom.translate([0, y]).scale(1).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 = 5; + + 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; + }); + + if (visible.length < 40) { + 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; + }); + } + } + + 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, true); + var sprite = new PIXI.Sprite(texture); + + var update = function () { + sleep = false; + }; - return canvas; + sprite.on("added", update); + texture.once("update", update); + + sprite.scale.x = d.scaleFactor; + sprite.scale.y = d.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"; + + var texture = new PIXI.Texture.fromImage(url, true); + 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) * d.scaleFactor; + sleep = false; + }; + sprite.on("added", updateSize); + texture.once("update", updateSize); + + if (d.imagenum) { + sprite.on("mousemove", function (s) { + var pos = s.data.getLocalPosition(s.currentTarget); + s.currentTarget.cursor = pos.x > 0 ? "e-resize" : "w-resize"; + }); + sprite.on("click", function (s) { + if (drag) return; + + s.stopPropagation(); + spriteClick = true; + var pos = s.data.getLocalPosition(s.currentTarget); + var dir = pos.x > 0 ? 1 : -1; + var page = d.page + dir; + var nextPage = page; + if (page > d.imagenum - 1) nextPage = 0; + if (page < 0) nextPage = d.imagenum - 1; + + canvas.changePage(d.id, nextPage); + }); + sprite.interactive = true; + } + + 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(); + 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; } -- GitLab