// Generated by CoffeeScript 1.10.0 (function() { var data, fx, fy, get_main_object, height, lod, n_columns, n_rows, redraw, side, svg, to_bounding_box, transform, width, zoom, zoomable_layer; svg = d3.select('svg'); width = svg.node().getBoundingClientRect().width; height = svg.node().getBoundingClientRect().height; zoomable_layer = svg.append('g'); zoom = d3.zoom().scaleExtent([1, 1000]).on('zoom', function() { lod(d3.event.transform); return zoomable_layer.attrs({ transform: d3.event.transform }); }); svg.call(zoom); /* Data */ side = 5; n_columns = 5; n_rows = 2; fx = side * 2 + 30; fy = side * 2 + 30; data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(d, i) { return { i: i, s1: side + Math.floor(Math.random() * 20), s2: side + Math.floor(Math.random() * 20), x: fx * (i % n_columns), y: fy * (i % n_rows) }; }); /* Visualization */ redraw = function(x1, x2, y1, y2) { var all_items, en_items, in_viewport, items; in_viewport = function(d) { return d.x < x2 && d.x + d.s1 > x1 && d.y < y2 && d.y + d.s2 > y1; }; items = zoomable_layer.selectAll('.item').data(data.filter(function(d) { return in_viewport(d); })); en_items = items.enter().append('rect').attrs({ "class": 'item' }); all_items = en_items.merge(items); all_items.attrs({ width: function(d) { return d.s1; }, height: function(d) { return d.s2; }, x: function(d) { return d.x; }, y: function(d) { return d.y; } }).styles({ fill: '#f2f2f2' }).on('click', function(d) { var center, transform; center = { x: d.x + d.s1 / 2, y: d.y + d.s2 / 2 }; transform = to_bounding_box(width, height, center, d.s1, d.s2, height / 10); return svg.transition().duration(2000).call(zoom.transform, transform); }); return items.exit().remove(); }; /* */ lod = function(transform) { var dx, dy, h_margin, v_margin, x, y; v_margin = 200 / transform.k; h_margin = 300 / transform.k; dx = width / transform.k; dy = height / transform.k; x = -transform.x / transform.k; y = -transform.y / transform.k; redraw(x, x + dx, y, y + dy); if (transform.k > 22) { get_main_object(x, x + dx, y, y + dy, h_margin, v_margin); } d3.select('.viewport').attrs({ x: x, y: y, width: dx, height: dy }); return d3.select('.m_viewport').attrs({ x: x + h_margin, y: y + v_margin, width: dx - h_margin * 2, height: dy - v_margin * 2 }); }; get_main_object = function(x1, x2, y1, y2, h_margin, v_margin) { var index, items, max; x1 += h_margin; x2 -= h_margin; y1 += v_margin; y2 -= v_margin; max = 0; index = null; items = zoomable_layer.selectAll('.item'); items.each(function(d, i) { var x_overlap, y_overlap; x_overlap = Math.max(0, Math.min(x2, d.x + d.s1) - Math.max(x1, d.x)); y_overlap = Math.max(0, Math.min(y2, d.y + d.s2) - Math.max(y1, d.y)); d.overlap = x_overlap * y_overlap; if (d.overlap > max) { max = d.overlap; return index = i; } }); return items.filter(function(d, i) { return i === index; }).styles({ fill: '#fb8072' }); }; /* Returns a transform for center a bounding box in the browser viewport - W and H are the witdh and height of the window - w and h are the witdh and height of the bounding box - center cointains the coordinates of the bounding box center - margin defines the margin of the bounding box once zoomed */ to_bounding_box = function(W, H, center, w, h, margin) { var k, kh, kw, x, y; kw = (W - margin) / w; kh = (H - margin) / h; k = d3.min([kw, kh]); x = W / 2 - center.x * k; y = H / 2 - center.y * k; return d3.zoomIdentity.translate(x, y).scale(k); }; /* Init */ redraw(); zoomable_layer.append('rect').attrs({ "class": 'viewport' }).styles({ fill: 'transparent', stroke: '#80b1d3', 'stroke-width': 8, 'vector-effect': 'non-scaling-stroke' }); zoomable_layer.append('rect').attrs({ "class": 'm_viewport' }).styles({ fill: 'transparent', stroke: '#fb8072', 'stroke-width': 5, 'vector-effect': 'non-scaling-stroke' }); transform = to_bounding_box(width, height, { x: 170 / 2, y: 70 / 2 }, 170, 70, 150); svg.call(zoom.transform, transform); }).call(this);