/* GLOBAL SETTINGS, SVG and panels */ /* define a fixed random seed, to avoid to have a different layout on each page reload. change the string to randomize */ (function() { var LABEL_SCALE, defs, height, hierarchy, i, j, leaves, map, nodes, randint, randlen, randname, randsy, region_labels, scale, svg, svg_bbox, syllables, tree, vis, width, zoom; width = 960; height = 500; svg = d3.select('body').append('svg').attr('width', width).attr('height', height); svg_bbox = svg[0][0].getBoundingClientRect(); /* main visualization (map view from the top) */ vis = svg.append('g'); map = vis.append('g').attr('transform', "translate(" + (width / 2) + "," + (height / 2) + ")").on('click', function() { if (d3.event.defaultPrevented) return; return d3.select(this).classed('selected', !d3.select(this).classed('selected')); }); /* ZUI */ /* define a zoom behavior */ zoom = d3.behavior.zoom().scaleExtent([0.1, 10]).on('zoom', function() { /* whenever the user zooms, */ /* modify translation and scale of the zoom group accordingly */ var scale, translation; translation = zoom.translate(); scale = zoom.scale(); return vis.attr('transform', "translate(" + translation + ")scale(" + scale + ")"); }); /* bind the zoom behavior to the main SVG */ svg.call(zoom); /* DATA */ console.debug('Generating random data...'); syllables = ['bi', 'bo', 'bu', 'ta', 'se', 'tri', 'su', 'ke', 'ka', 'flo', 'ko', 'pi', 'pe', 'no', 'go', 'zo', 'fu', 'fo', 'si', 'pa', 'ar', 'es', 'i', 'kya', 'kyu', 'fle', 'o', 'ne', 'na', 'le', 'lu', 'ma', 'an']; randlen = function() { return 2 + Math.floor(Math.random() * 4); }; randsy = function() { return syllables[Math.floor(Math.random() * syllables.length)]; }; randname = function() { var i; return ((function() { var _ref, _results; _results = []; for (i = 0, _ref = randlen(); 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { _results.push(randsy()); } return _results; })()).join(''); }; randint = function(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }; tree = { children: (function() { var _ref, _results; _results = []; for (j = 0, _ref = randint(8, 32); 0 <= _ref ? j < _ref : j > _ref; 0 <= _ref ? j++ : j--) { _results.push({ label: randname(), children: (function() { var _ref2, _results2; _results2 = []; for (i = 0, _ref2 = randint(128, 1280); 0 <= _ref2 ? i < _ref2 : i > _ref2; 0 <= _ref2 ? i++ : i--) { _results2.push({}); } return _results2; })() }); } return _results; })() }; console.debug('Computing d3 hierarchy layout...'); hierarchy = d3.layout.hierarchy(); nodes = hierarchy(tree); /* this tree is unordered, we need a canonical ordering for it */ console.debug('Computing canonical sort...'); tree_utils.canonical_sort(tree); /* obtain the sequence of leaves */ leaves = tree_utils.get_leaves(tree); /* VISUALIZATION */ /* compute the space-filling curve layout */ console.debug('Computing the Space-Filling Curve layout...'); scale = 3; sfc_layout.displace(leaves, sfc_layout.HILBERT, scale, scale, 0); /* compute also the position of internal nodes */ console.debug('Computing the position of internal nodes...'); sfc_layout.displace_tree(tree); console.debug('Computing the jigsaw treemap...'); /* compute all the internal nodes regions */ jigsaw.treemap(tree, scale, jigsaw.SQUARE_CELL); console.debug('Computing hilbert label placement...'); jigsaw.hilbert_labels(tree, scale); console.debug('Drawing...'); /* define the level zero region (the land) */ defs = svg.append('defs'); defs.append('path').attr('id', 'land').attr('d', jigsaw.get_svg_path(tree.region)); /* faux land glow (using filters takes too much resources) */ map.append('use').attr('class', 'land-glow-outer').attr('xlink:href', '#land'); map.append('use').attr('class', 'land-glow-inner').attr('xlink:href', '#land'); /* draw the land border (behind boundaries) */ map.append('use').attr('class', 'land-fill').attr('xlink:href', '#land'); /* draw label bboxes */ map.selectAll('.bbox').data(nodes.filter(function(d) { return d.depth === 1; })).enter().append('rect').attr('class', 'bbox').attr('x', function(d) { return d.label_bbox.x; }).attr('y', function(d) { return d.label_bbox.y; }).attr('width', function(d) { return d.label_bbox.width; }).attr('height', function(d) { return d.label_bbox.height; }); /* draw boundaries */ map.selectAll('.region').data(nodes.filter(function(d) { return d.depth === 1; })).enter().append('path').attr('class', 'region').attr('d', function(d) { return jigsaw.get_svg_path(d.region); }).attr('stroke-width', '1px').attr('stroke', 'white'); /* draw region labels */ LABEL_SCALE = 0.8; region_labels = map.selectAll('.region_label').data(nodes.filter(function(d) { return d.depth === 1; })).enter().append('text').attr('class', 'region_label').attr('dy', '0.35em').text(function(d) { return d.label; }).attr('transform', function(d) { var bbox, bbox_aspect, h_ratio, lbbox, lbbox_aspect, lbbox_height, lbbox_width, ratio, rotate, w_ratio; bbox = this.getBBox(); bbox_aspect = bbox.width / bbox.height; lbbox = d.label_bbox; lbbox_aspect = lbbox.width / lbbox.height; rotate = bbox_aspect >= 1 && lbbox_aspect < 1 || bbox_aspect < 1 && lbbox_aspect >= 1; if (rotate) { lbbox_width = lbbox.height; lbbox_height = lbbox.width; } else { lbbox_width = lbbox.width; lbbox_height = lbbox.height; } w_ratio = lbbox_width / bbox.width; h_ratio = lbbox_height / bbox.height; ratio = Math.min(w_ratio, h_ratio) * LABEL_SCALE; return "translate(" + (d.label_bbox.x + d.label_bbox.width / 2) + "," + (d.label_bbox.y + d.label_bbox.height / 2) + "),scale(" + ratio + "),rotate(" + (rotate ? -90 : 0) + ")"; }); }).call(this);