Skip to content
Snippets Groups Projects
Select Git revision
  • 8fce881334bee7626acdd215df6fa0fb27a0ec82
  • master default protected
  • updates
  • hierarchical-keywords
  • map
  • topoi
  • layer
  • tsne-layout
8 results

list.js

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    list.js 34.84 KiB
    // christopher pietsch
    // cpietsch@gmail.com
    // twitter @chrispiecom
    // 2015-2016
    
    
    function myListView() {
      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);
    
      // d3.select("body")
      //     .on("keydown", keydown);
    
      var canvas;
      var container;
      var entries;
      var years;
      var data;
      var rangeBand = 0;
      var rangeBandImage = 0;
      // var imageSize = 50;
      var imageSize = 256;
      var imageSize2 = 1024;
      var imageSize3 = 4000;
      var collumns = 4;
      var renderer, stage, stats;
      var svg, timeline;
      var svgscale, voronoi;
    
      var selectedImageDistance = 0;
      var selectedImage = null;
    
      var drag = 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",
        last: { mode: "time" },
        init: false,
        config: {
          hasNoBig: true
        }
      };
    
    
      var tsneGrid = false;
      var detailContainer = d3.select(".sidebar")
    
      var force = d3.layout.force()
          .size([width, height])
    
      var filter;
      var myTooltip;
    
      var timelineData;
    
      var stage, stage1, stage2, stage3, stage4, stage5;
    
      function chart() {}
    
      chart.setMode = function(name){
        timeline.classed("hide", function(d){ return name != "time" });
        state.last.mode = state.mode;
        state.mode = name;
        chart.project();
      }
    
      chart.setTsneGrid = function(d){
        tsneGrid = d;
        chart.initTSNE();
      }
      chart.resize = function() {
          if(!state.init) return;
          // console.log("resize")
          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);
    
          chart.makeScales();
          chart.project();
      }
    
      chart.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;
    
          timeDomain.forEach(function(d) {
              d.x = x(d.key);
          });
    
          zoomedToImageScale = 0.8 / (x.rangeBand() / collumns / width)
      }
    
      chart.init = function(_data,_timeline) {
          data = _data;
    
          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;
          }
          
          // obsolete ?
          PIXI.settings.SCALE_MODE = 1
    
          var renderOptions = {
              transparent: false,
              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));
    
          stats = new Stats();
          document.body.appendChild(stats.domElement);
    
          stage = new PIXI.Container();
          stage2 = new PIXI.Container();
          stage3 = new PIXI.Container();
          stage4 = new PIXI.Container();
          stage5 = new PIXI.Container();
          // stageBack = new PIXI.Container();
    
          // stage.addChild(stageBack);
          stage.addChild(stage2);
          stage2.addChild(stage3);
          stage2.addChild(stage4);
          stage2.addChild(stage5);
    
    
          //loadBigImage(data[parseInt(Math.random()*data.length)]);
    
          //console.log(_data[0])
    
          // timeline cleaning
          _timeline.forEach(function(d) {
              d.jahr = d.year;
              // d.jahr = d.jahr.split("-")[0];
              // d.jahr = d.jahr * 1;
              d.type = "timeline";
    
              if(lang == "en"){
                d.titel = d.titelEN;
                d.text = d.textEN;
                d.extra = d.extraEN;
              }
          });
    
          var chartDomain = d3.nest()
            .key(function(d){ return d.jahr; })
            .entries(_data.concat(_timeline))
            .sort(function(a, b) { return a.key - b.key; })
            // .sort(function(a, b) { return d3.descending(a.key, b.key) })
            .map(function(d){ return d.key; })
    
          timeDomain = chartDomain.map(function(d){
            return {
              key: d,
              values: _timeline.filter(function(e){ return d == e.jahr; })
            }
          })
          x.domain(chartDomain);
    
          // console.log(chartDomain)
    
          chart.makeScales();
    
    
          // add preview pics
          data.forEach(function(d, i) {
              var texture = imagesMap.get(d.id);
              var sprite = new PIXI.Sprite(texture);
              
              sprite.anchor.x = 0.5;
              sprite.anchor.y = 0.5;
    
              // sprite.scale.x = 1/scale1;
              // sprite.scale.y = 1/scale1;
    
              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");
                  // console.log("DRAG", drag)
                  if (spriteClick) { spriteClick = false; return; }
                  if (selectedImage && !selectedImage.id) return;
                  if (drag) return;
                  if (selectedImageDistance > 15) return;
                  if (selectedImage && !selectedImage.active) return;
                  if(timelineHover) return;
                  // console.log(selectedImage)
    
    
    
                  if (Math.abs(zoomedToImageScale - scale) < 0.1) {
                      logger.log({
                          action: "zoomback",
                          scale: scale,
                          target: selectedImage ? selectedImage.id : ""
                      });
                      chart.resetZoom();
                  } else {
                      logger.log({
                          action: "zoomto",
                          scale: scale,
                          target: selectedImage ? selectedImage.id : ""
                      });
                      zoomToImage(selectedImage, 1400 / Math.sqrt(Math.sqrt(scale)));
                  }
    
                  // if(zoomedToImage) zoomToImage(selectedImage, 500);
                  // if(!zoomedToImage) zoomToImage(selectedImage, 1000);
              })
    
          svg = renderElem;
              
          timeline = d3.select(".viz").append("div").classed("timeline", true)
              .style("transform", "translate(" + 0 + "px," + (height - 30) + "px)");
    
          chart.project();
    
          animate();
    
    
          state.init = true;
      };
    
    
      function mousemove(d) {
          if(timelineHover) return;
    
          var mouse = d3.mouse(vizContainer.node());
          var p = toScreenPoint(mouse);
    
          // console.time("search")
          var best = nearest(p[0] - imgPadding, p[1] - imgPadding, {
              d: 200,
              p: null
          }, quadtree);
          // console.timeEnd("search")
    
          selectedImageDistance = best.d;
          // console.log(selectedImageDistance);
    
          if(bottomZooming && best.p && best.p.ii < 3 && selectedImageDistance > 7){
            // console.log("bottom");
            selectedImage = null;
            zoom.center(null);
            container.style("cursor", "default");
          } else {
            if (best.p && !zoomedToImage) {
                var d = best.p;
                // todo iprove that bitch
                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 ((best.d < 5) && selectedImage.active) ? "pointer" : "default";
            });
          }
    
      } 
    
      var flipflop = false;
    
      function stackLayout(data, invert) {
          flipflop = !flipflop;
    
          var years = d3.nest()
              .key(function(d) {
                  return d.jahr;
              })
              // .sortKeys(d3.ascending)
              .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;
                // return b.scaleFactor - a.scaleFactor;
              })
              //console.log(year.values)
    
              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);
              })
          })
      }
    
      chart.distance = function(a, b) {
          return Math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]));
      }
    
    
      chart.click = function(d) {
          // c("click")
    
      }
    
      function toScreenPoint(p) {
          var p2 = [0, 0];
    
          // console.log("t",translate,scale)
    
          p2[0] = p[0] / scale - translate[0] / scale;
          p2[1] = (p[1] / scale - height) - translate[1] / scale;
    
          return p2;
      }
    
      chart.mousemove = function(d) {
          if (cloud.lock) return;
    
          var mouse = d3.mouse(this);
          var p = [d.point.x, d.point.y];
    
          var distance = chart.distance(mouse, p);
    
          //c("distance", distance);
    
          // c("cell", p);
          // c("mouse", mouse);
      }
    
    
    
      chart.mouseout = function(d) {
          // console.log("mouseout")
          if (cloud.lock) return;
    
          //d.target.alpha = 1;
          if (scale < zoomBarrier) {
              cloud.mouseleave();
          }
    
          myTooltip.hide();
          // svg.attr("cursor", "default");
    
      }
    
    
    
      chart.mousedown = function(d) {
          if (drag) return;
    
          var d = d.point;
          //var elm = d.target._data;
    
          if (!drag) {
              zoomToImage(d, 1000 / Math.sqrt(Math.sqrt(scale)));
          }
    
      }
    
    
      // function keydown(d) {
          // var key = d3.event.keyIdentifier;
          // var charkey = String.fromCharCode(d3.event.charCode || d3.event.keyCode);
    
          // if (d3.event.keyCode === 8) {
          //     d3.event.preventDefault();
          //     search = search.slice(0, -1);
              
          //     cloud.search(search);
          // }
    
    
          // if (!/[^a-zA-Z0-9]/.test(charkey)) {
          //     search += charkey;
          //     cloud.search(search);
          // }
    
          // if (key == "Right" || key == "Left") {
          //     var dir = key == "Right" ? -1 : 1;
          //     var next = getSiblingImage(selectedImage, dir);
    
          //     // c(dir, next)
          //     clearBigImages();
          //     zoomToImage(next, 500);
          // }
    
          // if (key == "Up" || key == "Down") {
          //     var dir = key == "Up" ? 1 : -1;
          //     translateUpDown(dir);
          // }
    
      // }
    
      function translateUpDown(dir) {
    
          var translateNow = [translate[0], translate[1] + dir * 10 * scale];
    
          svg
              .call(zoom.translate(translate).event)
              .transition().duration(1000)
              .call(zoom.translate(translateNow).event)
      }
    
      function getSiblingImage(active, dir) {
          if (!active) return;
    
          return data.filter(function(d) {
              return (d.order == active.order + dir && d.jahr == active.jahr);
          })[0];
    
      }
    
      function imageAnimation() {
    
          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;
    
              diff = (d.y1 - d.sprite.position.y);
              if (Math.abs(diff) > 0.1) d.sprite.position.y += diff * 0.1;
    
              diff = (d.alpha - d.sprite.alpha);
              if (Math.abs(diff) > 0.01) d.sprite.alpha += diff * 0.2;
    
              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;
    
                  d.sprite2.visible = d.sprite2.alpha > 0.1;
                  //else d.sprite2.visible = d.visible;
              }
          });
        
      }
    
      function animate(time) {
    
          requestAnimationFrame(animate);
    
          loadImages();
          imageAnimation();
    
          renderer.render(stage);
          stats.update();
      }
    
      function zoomToYear(d) {
    
          var xYear = x(d.jahr);
          var scale = 1 / (rangeBand / width);
          var translateNow = [-scale * xYear, -scale * (height + d.y)];
    
          svg
              .call(zoom.translate(translate).event)
              .transition().duration(2000)
              .call(zoom.scale(scale).translate(translateNow).event)
      }
    
    
      var zoomedToImage = false;
      var zoomedToImageScale = 117;
      var zoomBarrier = 2;
      // todo: zoombarrier as d3.scale.threshold()
    
      function zoomToImage(d, duration) {
    
          // console.log("detail", d)
    
          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 padding = 0;
          var scale = 0.8 / (x.rangeBand() / collumns / width);
          var translateNow = [(-scale * (d.x - padding/2)) - sidbar, -scale * (height + d.y)];
    
          //console.log(scale, translateNow);
    
          zoomedToImageScale = scale;
    
          setTimeout(function() {
              hideTheRest(d);
          }, duration / 2);
    
          svg
              .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")
          logger.log({
              action: "zoomToDetail",
              target: d.id
          });
    
          detailContainer
           .select(".outer")
           .node()
           .scrollTop = 0;
    
          detailContainer
              .classed("hide", false)
              .classed("sneak", (lang=="en" || utils.isMobile()) )
    
    
          // needs to be done better
          var detailData = {}
          for ( field in selectedImage ){
            if(field[0] === '_') detailData[field] = selectedImage[field]
          }
          detailData['_id'] = selectedImage.imageid
          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
    
          console.log(selectedImage, detailData)
      }
    
      function changePage(id, page){
        console.log("changePage", id, page, selectedImage);
        // var d = data.find(function (d) { d.imageid == id })
        // console.log(d)
        selectedImage.page = page
        detailVue._data.page = page
        clearBigImages();
        loadBigImage(selectedImage)
      }
      chart.changePage = changePage
    
    
      function hideTheRest(d) {
          // c("hide", d.id)
          data.forEach(function(d2) {
              if (d2.id !== d.id) {
                  // d2.sprite.alpha = 0;
                  // d2.sprite.visible = false;
                  d2.alpha = 0;
                  d2.alpha2 = 0;
    
              }
          })
      }
    
      function showAllImages() {
          data.forEach(function(d) {
              d.alpha = d.active ? 1 : 0.2;;
              //d.visible = d.active;
              d.alpha2 = d.visible ? 1 : 0;
              //d.sprite.visible = true;  
    
          })
      }
    
      var fontScale = d3.scale.linear()
          .domain([1, 9])
          .range([9, 20])
          .clamp(true)
    
      var timelineFontScale = d3.scale.linear()
          .domain([2, 8, 20])
          .range([9, 14, 19])
          .clamp(true)
    
      var timelineScale = d3.scale.threshold()
          .domain([3, 10, 20])
          .range(["none", "small", "middle", "large"])
    
      var timelineHover = false;
    
      function updateDomain(x1, x2) {
    
          timeDomain.forEach(function(d) {
              d.pos = ((d.x - x1) * scale);
              d.visible = (d.pos > (-rangeBand * scale) && d.pos < width + 100);
          })
    
          timeline.attr("class", "timeline " + timelineScale(scale))
    
          timeline.style("font-size", function() {
              return (2 * scale)+ "px";
          });
    
    
          var select = timeline.selectAll(".container")
              .data(timeDomain)
    
          var enter = select
              .enter()
              .append("div")
              .classed("container", true)
              .on("mouseenter", function(d){
                timelineHover = true;
                zoom.center(null);
                selectedImage = null;
                logger.log({
                  action: "enter timeline",
                  scale: scale,
                  translate: translate,
                  target: d.key,
                });
                // console.log("enter")
              })
              .on("mouseleave", function(d){
                timelineHover = false;
                logger.log({
                  action: "exit timeline",
                  scale: scale,
                  translate: translate,
                  target: d.key,
                });
              })
    
    
          enter.append("div")
              .classed("year", true)
              .text(function(d) {
                  return d.key;
              })
    
          var e = enter
              .append("div")
              .classed("entries", true)
              .selectAll(".entry")
              .data(function(d) {
                  return d.values;
              })
              .enter()
              .append("div")
              .classed("entry", true)
    
          e
              .append("div")
              .classed("small", true)
              .append("div")
              .classed("title", true)
              .text(function(d) {
                  return d.titel;
              })
    
          var m = e
              .append("div")
              .classed("middle", true)
    
          m
              .append("div")
              .classed("title", true)
              .text(function(d) {
                  return d.titel;
              })
    
          m
              .append("div")
              .classed("text", true)
              .text(function(d) {
                  return d.text + ".";
              }) //…
    
          var l = e
              .append("div")
              .classed("large", true)
    
          l
              .append("div")
              .classed("title", true)
              .text(function(d) {
                  return d.titel;
              })
    
          l
              .append("div")
              .classed("text", true)
              .html(function(d) {
                  return d.text + ".<br><br>" + d.extra;
              })
    
          select
              .style("transform", function(d) {
                  return "translate3d(" + parseInt(d.pos) + "px,0px,0px)";
              })
              .style("height", rangeBand * scale + "px")
              .style("width", rangeBand * scale + "px")
              .style("display", function(d) {
                  return d.visible ? "block" : "none";
              })
    
          select
              .select(".year")
              .style("font-size", fontScale(scale) + "px")
    
      
      }
    
      function zoomed() {
    
          translate = d3.event.translate;
          scale = d3.event.scale;
    
          // check borders
          // this shit cost me a lot of nerves...
          // to be refactored
    
          var x1 = -1 * translate[0] / scale;
          var x2 = (x1 + (widthOuter / scale));
    
          var y1 = (translate[1] + height * scale);
    
          var e = -extent[1] - bottomPadding;
          var y2 = (e - height) * scale + height;
    
          var e2 = extent[0] - bottomPadding;
          var y3 = (e2 + height) * -scale;
    
          // console.log(translate[1],e2, y3);
    
          if (d3.event.sourceEvent != null) {
              if (x1 < 0) {
                  translate[0] = 0;
              } else if (x2 > widthOuter) {
                  translate[0] = ((widthOuter * scale) - widthOuter) * -1;
              }
    
    
              if (translate[1] < y2) {
                  translate[1] = y2;
              }
    
              zoom.translate([translate[0], translate[1]]);
    
              x1 = -1 * translate[0] / scale;
              x2 = (x1 + (width / scale))
          }
    
    
    
          if (zoomedToImageScale != 0 && scale > zoomedToImageScale && !zoomedToImage && selectedImage && selectedImage.type == "image") {
              
              zoomedToImage = true;
              zoom.center(null);
              zoomedToImageScale = scale;
              hideTheRest(selectedImage);
              showDetail(selectedImage)
          }
    
    
          if (zoomedToImage && zoomedToImageScale - 20 > scale) {
              // c("clear")
              zoomedToImage = false;
              state.lastZoomed = 0;
              showAllImages();
              clearBigImages();
              detailContainer.classed("hide", true).classed("sneak", lang=="en")
          }
    
          updateDomain(x1, x2);
    
          var timeY = ((height) * scale - (-1 * translate[1]) - rangeBandImage * scale);
          timeline
              .style("transform", "translate(" + 0 + "px," + timeY + "px)");
          
    
          // 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];
    
      }
    
    
      var startTranslate = [0, 0];
      var startScale = 0;
    
      var zooming = false;
    
      function zoomstart(d) {
          zooming = true;
          startTranslate = translate;
          startScale = scale;
      }
    
      var thresholdScale = d3.scale.threshold()
        .domain([1.1,115,116])
        .range(["far","middle","close","detail"])
    
    
      function zoomend(d) {
          drag = translate !== startTranslate;
          zooming = false;
    
          filterVisible();
    
          logger.log({
              action: "zoomend",
              translate: translate,
              scale: scale,
              target: selectedImage ? selectedImage.id : ""
          });
    
          if (zoomedToImage && !selectedImage.big && state.lastZoomed != selectedImage.id && !state.zoomingToImage) {
              //c("loadbig after zoom")
              loadBigImage(selectedImage, "zoom");
          }
      }
    
      chart.highlight = function() {
          data.forEach(function(d, i) {
              d.alpha = d.highlight ? 1 : 0.2;
          });
      }
    
      chart.project = function(){
        if(state.mode == "tsne" || state.mode == "grid"){
          chart.projectTSNE();
        }
        else {
          chart.split();
        }
        chart.resetZoom();
      }
    
      chart.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 });
    
        inactive.forEach(function(d,i){
          var r = 300 + d.scaleFactor*100;
          var a =  -Math.PI/2+ (i/inactiveSize) * 2*Math.PI; 
          var factor = 10;
    
          d.x = r * Math.cos(a) +width/2;
          d.y = r * Math.sin(a) +marginBottom;
        });
    
        active.forEach(function(d){
    
          if(state.mode == "tsne"){
            var factor = 10;
            d.x = d.tsne[0]*factor +width/2;
            d.y = d.tsne[1]*factor +marginBottom;
          } else {
            var factor =8;
            d.x = d.grid[0]*factor +width/2 - 150;
            d.y = d.grid[1]*factor-150 +marginBottom;
          }
         
        })
    
        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();
    
    
      }
    
    
      chart.resetZoom = function() {
          var duration = 1400;
    
          extent = d3.extent(data, function(d) { return d.y; });
    
          var y = -extent[1] - bottomPadding;
          console.log(extent, y)
          y =  (extent[1] / -3) - bottomPadding
          // y =  - bottomPadding
          bottomZooming = (y<-30 && y>-40);      
    
          svg
              .call(zoom.translate(translate).event)
              .transition().duration(duration)
              .call(zoom.translate([0, y]).scale(1).event)
              //.each("end", chart.split)
      }
    
    
      chart.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);
    
          // console.time("Quadtree")
          quadtree = Quadtree(data);
          // console.timeEnd("Quadtree");
     
      }
    
      function nearest(x, y, best, node) {
          // mike bostocks code https://blocks...
          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;
      }
    
    
      function filterVisible() {
    
          var zoomScale = scale;
          // var translate = t;
    
          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;
    
              // c(x,y,p, translate, zoomScale, scale, height/zoomScale, y+height)
    
              if (x > (-padding) && x < ((width / zoomScale) + padding) && y + height < (height / zoomScale + padding) && y > (height * -1) - padding) {
                  //d.sprite.alpha = 1;
                  d.visible = true;
                  // d.alpha = 1;
              } else {
                  //d.sprite.alpha = 0.5;
                  d.visible = false;
                  // d.alpha = 0;
              }
          });
    
          var visible = data.filter(function(d) {
              return d.visible;
          });
          //c(visible.length);
    
    
          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;
                  //if(d.sprite2) d.sprite2.visible = false;
              })
          }
    
    
      }
    
      function loadMiddleImage(d) {
          if (d.loaded) {
              d.alpha2 = 1;
              return;
          }
          // if (!imagesMap2.get(d.id)) {
          //     return;
          // }
          // console.log("load", d)
          var texture = new PIXI.Texture.fromImage(config.loader.textures.detail.url + d.id + '.jpg', true)
          var sprite = new PIXI.Sprite(texture);
    
          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;
      }
    
      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;
          }
    
          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) {
    
              console.log("click sprite")
              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 = Math.min(Math.max(page, 0), d.imagenum-1)
              var nextPage = page
              if(page > d.imagenum-1) nextPage = 0
              if(page < 0) nextPage = d.imagenum-1
    
              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;
    
          console.log(sprite, "done")
    
          stage5.addChild(sprite);
    
      }
    
    
      function loadBigImage2(d, type) {
          console.log("loadBig", d.id, d.page);
    
          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";
    
          LoaderBlob(url).finished(function(blob){
            var sprite = new PIXI.Sprite.from(blob);
    
            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) {
                console.log("click sprite")
                // 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 = Math.min(Math.max(page, 0), d.imagenum-1)
                var nextPage = page
                if(page > d.imagenum-1) nextPage = 0
                if(page < 0) nextPage = d.imagenum-1
    
                changePage(d.id, nextPage)
              })
              sprite.interactive = true;
            }
    
            console.log(sprite.width, sprite.height)
    
            var maxRes = Math.max(sprite.width, sprite.height)
            
            // c(texture.baseTexture.hasLoaded, sprite);
            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 * scale3 + imageSize3 / 2;
            sprite.position.y = d.y * scale3 + imageSize3 / 2;
            sprite._data = d;
            d.big = true;
    
            stage5.addChild(sprite);
    
          })
    
      }
    
    
      function loadBigImage3(d, callback) {
          // c("loadBig", d.id);
    
          var img = new Image();
    
          img.addEventListener("load", function() {
              // console.log(img)
              var base = new PIXI.BaseTexture(img);
              var texture = new PIXI.Texture(base);
              // var texture = PIXI.Texture.fromImage("data/bilder_4000/" + d.id + ".jpg");
              var sprite = new PIXI.Sprite(texture);
    
              //c(texture.baseTexture.hasLoaded, sprite);
    
              sprite.scale.x = sprite.scale.y = 0.1;
    
              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);
          });
          // img.src = "data/bilder_4000/" + d.id + ".jpg";
          // img.src = "data/large/105599.jpg";
          img.crossOrigin = "";
          img.src = "https://s3.eu-central-1.amazonaws.com/fw4/large/" + d.id + ".jpg";
    
    
      }
    
      function clearBigImages() {
          while (stage5.children[0]) {
              stage5.children[0]._data.big = false;
              stage5.removeChild(stage5.children[0]);
          }
      }
    
      function loadImages() {
          if (zooming) return;
          if (zoomedToImage) return;
    
          if (loadImagesCue.length) {
              var d = loadImagesCue.pop();
              if (!d.loaded) {
                  loadMiddleImage(d);
              }
          }
      }
    
    
      return chart;
    
    }