Skip to content

Instantly share code, notes, and snippets.

@dianaow
Last active June 16, 2019 07:26
Show Gist options
  • Save dianaow/bd583ab0ad1153f33b9ee5d94831e931 to your computer and use it in GitHub Desktop.
Save dianaow/bd583ab0ad1153f33b9ee5d94831e931 to your computer and use it in GitHub Desktop.
D3 V4: Vertical Tree Diagram
license: mit

I wanted to visually keep track of all my personal bookmarks about things i love: cool and informative data viz, web animations, data science.

Hover of each dot to see title and click dot to open url in a new tab.

Built with blockbuilder.org

count name parent
59 bookmarks
20 bookmarks|blocks bookmarks
19 bookmarks|cool bookmarks
4 bookmarks|people bookmarks
16 bookmarks|tutorials bookmarks
8 bookmarks|cool|data science theory bookmarks|cool
1 bookmarks|cool|infographics bookmarks|cool
10 bookmarks|cool|interactive bookmarks|cool
5 bookmarks|tutorials|D3 bookmarks|tutorials
2 bookmarks|tutorials|Others bookmarks|tutorials
1 bookmarks|tutorials|Visualization bookmarks|tutorials
1 bookmarks|tutorials|WebGL bookmarks|tutorials
7 bookmarks|tutorials|tools bookmarks|tutorials
dateAddedLocal dateAddedUTC id index parentId category folder title url
20/04/2019, 21:19:49 2019-04-20T13:19:49.041Z 15 0 44 blocks Finding adjacent voronoi cells - bl.ocks.org https://bl.ocks.org/RobinL/76580040f9795503ae9586a9b367aa51
21/04/2019, 15:53:28 2019-04-21T07:53:28.366Z 16 1 44 blocks lars’s Blocks - bl.ocks.org https://bl.ocks.org/larsvers
21/04/2019, 16:52:47 2019-04-21T08:52:47.453Z 17 2 44 blocks Force layout on canvas with zoom/pan and drag - bl.ocks.org https://bl.ocks.org/jodyphelan/5dc989637045a0f48418101423378fbd
21/04/2019, 21:05:27 2019-04-21T13:05:27.483Z 18 3 44 blocks Zoom Transitions - bl.ocks.org https://bl.ocks.org/mbostock/b783fbb2e673561d214e09c7fb5cedee
24/04/2019, 11:07:04 2019-04-24T03:07:04.051Z 27 4 44 blocks Entropy - bl.ocks.org https://bl.ocks.org/vasturiano/2992bcb530bc2d64519c5b25201492fd
30/04/2019, 17:34:50 2019-04-30T09:34:50.772Z 28 5 44 blocks 3D Force-Directed Graph with Groups Dataset - bl.ocks.org https://bl.ocks.org/micahstubbs/a9eb7847c56c4c844d36cfbc5075be73
30/04/2019, 17:35:01 2019-04-30T09:35:01.453Z 29 6 44 blocks Force split and unite on canvas - bl.ocks.org http://bl.ocks.org/FrissAnalytics/72508b2eb569c054366eca921fa14ec4
30/04/2019, 18:01:18 2019-04-30T10:01:18.803Z 30 7 44 blocks Easing - bl.ocks.org https://bl.ocks.org/mbostock/248bac3b8e354a9103c4
03/05/2019, 20:04:46 2019-05-03T12:04:46.310Z 31 8 44 blocks Chris Tufts’s Blocks - bl.ocks.org https://bl.ocks.org/ctufts
06/05/2019, 13:46:52 2019-05-06T05:46:52.906Z 34 9 44 blocks KoGor’s Blocks - bl.ocks.org https://bl.ocks.org/KoGor
06/05/2019, 14:11:10 2019-05-06T06:11:10.049Z 35 10 44 blocks Clustered Interactive Force Layout v4 - bl.ocks.org https://bl.ocks.org/mcgovey/e59b5542b628fab4c356223b3505f0c0
06/05/2019, 14:11:27 2019-05-06T06:11:27.787Z 37 11 44 blocks Circle Packing Methods / Mike Bostock / Observable https://observablehq.com/@mbostock/circle-packing-methods
06/05/2019, 20:15:25 2019-05-06T12:15:25.790Z 38 12 44 blocks General Update Pattern in Observable / Chris Henrick / Observable https://observablehq.com/@clhenrick/general-update-pattern-in-observable
10/05/2019, 16:01:45 2019-05-10T08:01:45.326Z 40 13 44 blocks d3.v4 collapsible force zoomable draggable radial tree using svg - bl.ocks.org https://bl.ocks.org/anonymous/ca046ccc115520d501060e4256aa6ada
10/05/2019, 16:01:49 2019-05-10T08:01:49.563Z 41 14 44 blocks Collapsible Force Layout - bl.ocks.org https://bl.ocks.org/mbostock/1093130
13/05/2019, 10:32:54 2019-05-13T02:32:54.449Z 50 15 44 blocks Sundar Singh’s Blocks - bl.ocks.org https://bl.ocks.org/eesur
28/05/2019, 13:42:19 2019-05-28T05:42:19.151Z 102 16 44 blocks D3 Polar Scatter https://codepen.io/hey-nick/pen/NxqpVr
03/06/2019, 17:21:32 2019-06-03T09:21:32.138Z 113 17 44 blocks Force in a Box / John Alexis Guerra Gómez / Observable https://observablehq.com/@john-guerra/force-in-a-box
05/06/2019, 22:36:54 2019-06-05T14:36:54.293Z 118 18 44 blocks Linear-time force-directed graph layouts with random vertex sampling - bl.ocks.org https://bl.ocks.org/rpgove/14bf7407d66cd364ce399ea0540e67b9
05/06/2019, 22:37:15 2019-06-05T14:37:15.254Z 120 19 44 blocks d3 forceBoundary / John Alexis Guerra Gómez / Observable https://observablehq.com/@john-guerra/d3-forceboundary
06/05/2019, 14:11:23 2019-05-06T06:11:23.283Z 36 0 45 people Dueling Data: http://duelingdata.blogspot.com/p/projects.html
28/05/2019, 13:43:02 2019-05-28T05:43:02.952Z 105 1 45 people TULP interactive http://tulpinteractive.com/
06/06/2019, 09:54:00 2019-06-06T01:54:00.374Z 121 2 45 people romercreative | information design http://romercreative.com/
12/06/2019, 09:54:00 2019-06-12T01:54:00.206Z 131 3 45 people Lingdong Huang https://lingdong.works/
10/05/2019, 16:05:25 2019-05-10T08:05:25.172Z 43 0 141 cool infographics Where the Crime Ends Up | La Lettura 387# dataviz on Behance https://www.behance.net/gallery/79767529/Where-the-Crime-Ends-Up-La-Lettura-387-dataviz?tracking_source=search%7Ccrime
14/06/2019, 20:26:19 2019-06-14T12:26:19.153Z 138 0 142 cool interactive The Rhythm of Food — by Google News Lab and Truth & Beauty http://rhythm-of-food.net/radish
04/06/2019, 10:55:26 2019-06-04T02:55:26.497Z 116 1 142 cool interactive How will we fill 9 billion bowls by 2050? | #9billionbowls | Thomson Reuters http://reports.thomsonreuters.com/9billionbowls/
28/05/2019, 13:42:56 2019-05-28T05:42:56.627Z 104 2 142 cool interactive How Brexit revealed four new political factions | Politics | The Guardian https://www.theguardian.com/politics/ng-interactive/2019/feb/15/how-brexit-revealed-four-new-political-factions
28/05/2019, 13:42:50 2019-05-28T05:42:50.450Z 103 3 142 cool interactive How difficult, and how costly, is a hard Brexit? | UK news | The Guardian https://www.theguardian.com/uk-news/ng-interactive/2017/feb/20/how-difficult-and-how-costly-is-a-hard-brexit-leaving-eu
15/05/2019, 09:16:43 2019-05-15T01:16:43.958Z 63 4 142 cool interactive Parable of the Polygons - a playable post on the shape of society https://ncase.me/polygons/
14/05/2019, 20:20:01 2019-05-14T12:20:01.710Z 59 5 142 cool interactive Ost-West-Wanderung: Die Millionen, die gingen | ZEIT ONLINE https://www.zeit.de/politik/deutschland/2019-05/ost-west-wanderung-abwanderung-ostdeutschland-umzug
15/05/2019, 09:17:00 2019-05-15T01:17:00.815Z 64 6 142 cool interactive Premiership top 6 vs bottom 14 - Gwilym | Tableau Public https://public.tableau.com/profile/gwilym#!/vizhome/Premiershiptop6vsbottom14/ThegrowinggapbetweenthePremiershipstopsixandtherest
14/05/2019, 20:18:12 2019-05-14T12:18:12.328Z 56 7 142 cool interactive How Diverse Are US Newsrooms? https://googletrends.github.io/asne/?view=2
10/05/2019, 16:02:07 2019-05-10T08:02:07.554Z 42 8 142 cool interactive Brussels - A lovely melting-pot http://brussels-diversity.jetpack.ai/
06/05/2019, 11:44:35 2019-05-06T03:44:35.331Z 33 9 142 cool interactive Connected China http://china.fathom.info/
14/06/2019, 20:56:17 2019-06-14T12:56:17.173Z 139 0 143 cool data science theory Seeing Theory - Basic Probability https://seeing-theory.brown.edu/basic-probability/index.html
12/06/2019, 14:03:12 2019-06-12T06:03:12.084Z 132 1 143 cool data science theory The Myth of the Impartial Machine https://parametric.press/issue-01/the-myth-of-the-impartial-machine/
10/06/2019, 16:02:43 2019-06-10T08:02:43.405Z 128 2 143 cool data science theory An Interactive Introduction to Fourier Transforms http://www.jezzamon.com/fourier/index.html
10/06/2019, 16:02:51 2019-06-10T08:02:51.622Z 129 3 143 cool data science theory The State of the Art in Visualizing Multivariate Networks https://vdl.sci.utah.edu/publications/2019_eurovis_mvn/
10/06/2019, 14:20:38 2019-06-10T06:20:38.010Z 127 4 143 cool data science theory Language, trees, and geometry in neural networks https://pair-code.github.io/interpretability/bert-tree/
15/05/2019, 09:16:37 2019-05-15T01:16:37.443Z 62 5 143 cool data science theory Going Critical — Melting Asphalt https://meltingasphalt.com/interactive/going-critical/
15/05/2019, 09:16:28 2019-05-15T01:16:28.217Z 61 6 143 cool data science theory Distill — Latest articles about machine learning https://distill.pub/
05/06/2019, 22:37:07 2019-06-05T14:37:07.074Z 119 7 143 cool data science theory The Barnes-Hut Approximation https://jheer.github.io/barnes-hut/
21/04/2019, 22:03:48 2019-04-21T14:03:48.188Z 20 0 144 tutorials D3 Scatterplots in D3 with Voronoi Interaction - Peter Beshai https://peterbeshai.com/blog/2016-10-30-scatterplot-in-d3-with-voronoi-interaction/
21/04/2019, 22:04:00 2019-04-21T14:04:00.188Z 21 1 144 tutorials D3 The state of d3 Voronoi - Philippe Rivière - Visionscarto https://visionscarto.net/the-state-of-d3-voronoi
12/05/2019, 18:51:24 2019-05-12T10:51:24.721Z 49 2 144 tutorials D3 D3.js Timer API https://www.tutorialspoint.com/d3js/d3js_timer_api.htm
14/05/2019, 15:36:47 2019-05-14T07:36:47.019Z 53 3 144 tutorials D3 Using and Abusing the Force http://vallandingham.me/force_talk/#0
14/05/2019, 20:16:59 2019-05-14T12:16:59.373Z 54 4 144 tutorials D3 So you want to make a map? – Data Visualization Society – Medium https://medium.com/data-visualization-society/so-you-want-to-make-a-map-58c7f55f6b20
14/05/2019, 20:17:55 2019-05-14T12:17:55.912Z 55 0 145 tutorials Others Svelte 3: Rethinking reactivity https://svelte.dev/blog/svelte-3-rethinking-reactivity
29/05/2019, 22:42:38 2019-05-29T14:42:38.832Z 109 1 145 tutorials Others Gephi%20Handout%20Sunbelt%202016.pdf https://drive.google.com/viewerng/viewer?url=http://www.kateto.net/wp-content/uploads/2016/04/Gephi%2520Handout%2520Sunbelt%25202016.pdf
11/06/2019, 09:19:49 2019-06-11T01:19:49.636Z 130 0 146 tutorials WebGL Beautifully Animate Points with WebGL and regl - Peter Beshai https://peterbeshai.com/blog/2017-05-26-beautifully-animate-points-with-webgl-and-regl/
03/06/2019, 17:21:20 2019-06-03T09:21:20.761Z 112 0 147 tutorials Visualization ML_Visualization_NeurIPS_Tutorial.pdf https://static.googleusercontent.com/media/research.google.com/en//bigpicture/ML_Visualization_NeurIPS_Tutorial.pdf?fbclid=IwAR3B5XHXMQnqVLAbgycgEJmXSVJeQBxwLsxQ6K0Pt-Na7PAkHRiIqMJfuBY
29/05/2019, 00:03:14 2019-05-28T16:03:14.225Z 106 0 140 tutorials tools Zdog · Getting started https://zzz.dog/getting-started
29/05/2019, 00:04:09 2019-05-28T16:04:09.347Z 107 1 140 tutorials tools Metafizzy · Delightful JavaScript plugins & logos https://metafizzy.co/
03/06/2019, 17:20:26 2019-06-03T09:20:26.457Z 111 2 140 tutorials tools What-If Tool https://pair-code.github.io/what-if-tool/index.html#demos
08/06/2019, 01:42:21 2019-06-07T17:42:21.405Z 125 3 140 tutorials tools vasturiano/globe.gl: UI component for Globe Data Visualization using ThreeJS/WebGL https://github.com/vasturiano/globe.gl
06/06/2019, 14:33:14 2019-06-06T06:33:14.925Z 123 4 140 tutorials tools The Linked Open Data Cloud https://lod-cloud.net/
03/06/2019, 17:52:50 2019-06-03T09:52:50.036Z 114 5 140 tutorials tools Data Viz Project | Collection of data visualizations to get inspired and finding the right type. https://datavizproject.com/#
13/06/2019, 00:06:35 2019-06-12T16:06:35.949Z 134 6 140 tutorials tools Faster force-directed graph layouts by reusing force approximations - Two Six Labs | Advanced Analytics, Cyber Capabilities, Tactical Mobility Solutions for National Security https://www.twosixlabs.com/faster-force-directed-graph-layouts-by-reusing-force-approximations/
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="./js/chroma.min.js"></script>
<style>
body {
background-color: whitesmoke;
}
#vis-container {
text-align: center;
}
text {
font-family: 'Karla', sans-serif;
font-size: 10px;
}
#title {
font-family: 'Karla', sans-serif;
font-size: 9px;
}
.tooltip {
position: absolute;
}
</style>
</head>
<body>
<div id='vis-container'>
<div id="wrapper" class="wrapper">
<div id="tooltip" class="tooltip">
<div class="tooltip-title" id="title"></div>
</div>
</div>
</div>
<script>
loadData()
///////////////////////////////////////////////////////////////////////////
///////////////////////////////// Globals /////////////////////////////////
///////////////////////////////////////////////////////////////////////////
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = screen.width*0.9 - margin.left - margin.right,
height = screen.height*0.9 - margin.top - margin.bottom
var svg = d3.select("#wrapper").append('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var CIRCLE_RADIUS = 3
var STROKE_WIDTH = "2px"
///////////////////////////////////////////////////////////////////////////
/////////////////////////////// Data Processing ///////////////////////////
///////////////////////////////////////////////////////////////////////////
function loadData() {
d3.queue() // queue function loads all external data files asynchronously
.defer(d3.csv, 'agg.csv')
.defer(d3.csv, 'chromebookmarks.csv')
.await(processData);
}
function processData(error, csv1, csv2) {
if (error) throw error;
var data = csv1.map((d,i) => {
return {
parent: d.parent,
name: d.name,
value: +d.count
}
})
csv2.map((d,i) => {
d.label = d.folder!="" ? ('bookmarks|' + d.category + "|" + d.folder + "|" + d.index) : ('bookmarks|' + d.category + "|" + d.index)
//console.log(d.label)
})
custom = ['bookmarks|people', 'bookmarks|tutorials', 'bookmarks|blocks', 'bookmarks|cool']
data.sort(function(x, y){
return custom.indexOf(x.name) - custom.indexOf(y.name)
})
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Create scales ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
var level1_list = data.map(d => d.name.split("|",1)[0]).filter(onlyUnique).filter(e=>e != undefined)
var level2_list = data.map(d => d.name.split("|",2)[1]).filter(onlyUnique).filter(e=>e != undefined)
var level3_list = data.map(d => d.name.split("|",3)[2]).filter(onlyUnique).filter(e=>e != undefined)
//var level2Colors = chroma.scale(['navy','deepskyblue']).mode('lch').colors(level2_list.length)
var level2Colors = ['deepskyblue', 'navy']
var level2_link = d3.scaleOrdinal()
.domain(level2_list)
.range(level2Colors)
var radiusScale = d3.scaleLinear()
.range([2, 80])
.domain(d3.extent(data, d=>d.value))
var level2_node = "dimgray"
var level3_node = 'black'
///////////////////////////////////////////////////////////////////////////
///////////////////////// Render vertical tree ////////////////////////////
///////////////////////////////////////////////////////////////////////////
// declares a tree layout and assigns the size
var treemap = d3.tree()
.size([width*(3/4), height/2]);
update(data)
function update(flatData) {
svg.selectAll('.treeWrapper').remove()
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr('class', 'treeWrapper')
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function(d) { return d.name; })
.parentId(function(d) { return d.parent; })
(flatData);
treeData = treeData.sum(function (d) { return +d.value })
// assigns the data to a hierarchy using parent-child relationships
var nodes = d3.hierarchy(treeData, function(d) {
return d.children;
});
// maps the node data to the tree layout
nodes = treemap(nodes);
var group = g.selectAll('.group')
.data(nodes.descendants().slice(1))
.enter().append("g")
.attr('class', 'group')
// append a defs (for definition) element to your SVG
const defs = g.append('defs')
// adds the links between the nodes (render with gradient)
var link = group
.filter(function(d) { return d.depth < 3 })
.append("path")
.attr("id", function(d) { return "path-" + d.data.id })
.style("fill", "none")
.style("stroke-width", STROKE_WIDTH)
.attr("d", function(d) {
if(d.x == d.parent.x) {
var buffer=2
} else {
var buffer=0
}
return "M" + (d.x+buffer).toString() + "," + d.y
+ "C" + d.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + (d.y + d.parent.y) / 2
+ " " + d.parent.x + "," + d.parent.y;
})
link.style("stroke", function(d,i) {
var depth = d.depth
var l2 = d.data.id.split("|",3)[1]
const gradientID = `gradient${i}` // make unique gradient ids
const startColor = level2_node
const stopColor = level2_list.indexOf(l2) != -1 ? level2_link(l2) : level3_node
const linearGradient = defs.append('linearGradient')
.attr('id', gradientID)
.attr("gradientTransform", "rotate(90)");
var stops1 = [
{offset: '0%', color: startColor, opacity: 0 },
{offset: '100%', color: startColor, opacity: 0.2 }
]
var stops2 = [
{offset: '0%', color: startColor, opacity: 1 },
{offset: '25%', color: startColor, opacity: 0.75 },
{offset: '75%', color: stopColor, opacity: 0.5 }
]
linearGradient.selectAll('stop')
.data(depth==1 ? stops1 : stops2)
.enter().append('stop')
.attr('offset', d => d.offset)
.attr('stop-color', d => d.color)
.attr('stop-opacity', d => d.opacity)
return `url(#${gradientID})`;
})
// adds text along link for middle points
group
.filter(function(d) { return (d.depth < 2) & (radiusScale(d.data.value) < 5) })
.append("text")
.append("textPath")
.attr("xlink:href", function(d) { return "#path-" + d.data.id })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("startOffset", "5%")
.attr("transform", function(d) { return "rotate(" + (d.x > width/2 ? -180 : 0) + ")translate(" + d.x + "," + (d.y-5).toString() + ")" })
.text(function(d) { return d.data.id.split("|",2)[1] });
// add text above circles
group
.filter(function(d) { return (d.depth < 2) & (radiusScale(d.data.value) >= 5) })
.append("text")
.attr("x", function(d) { return d.x })
.attr("y", function(d) { return d.y - radiusScale(d.data.value) - 10 })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.id.split("|",2)[1] })
group
.filter(function(d) { return (d.depth == 2) })
.append("text")
.attr("x", function(d) { return d.x + radiusScale(d.data.value)/2 })
.attr("y", function(d) { return d.y - radiusScale(d.data.value) - 5})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.id.split("|",3)[2] })
// adds each node as a group
var node = group.append("g")
.filter(function(d) { return d.depth < 2 })
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
// adds the circle to the node (paths in later groups will overlap depth 2 circles (country nodes))
node.append("circle")
.attr("r", function(d) { return d.data.value === undefined ? 2 : radiusScale(d.data.value) })
.attr('fill', function(d) { return level2_node })
g.append('text')
.attr('x', width*(3/4)/2-80)
.attr('y', 20)
.style('font-size', '24px')
.style('font-family', 'Karla')
.text('My bookmarks')
/////////////////////// Calculate coordinates of dotted plot //////////////////
var scores = nodes.descendants().filter(d=>d.depth >= 1)
scores = scores.filter(d=>(d.data.id != 'bookmarks|cool') & (d.data.id != 'bookmarks|tutorials'))
var res_nested = d3.nest()
.key(d=>d.data.id)
.entries(scores)
var tiles = createDots(res_nested)
// adds each dot as a group
var dot = g.selectAll('.dotsG')
.data(tiles)
.enter().append("g")
.attr("class", 'dotsG')
.attr("id", d=>d.index)
.attr("transform", function(d,i) { return "translate(" + d.x + "," + d.y + ")" });
// adds the circle to the dot
dot
.append("a")
.attr("xlink:href", function(d) {
return csv2.find(e=>e.label == d.index).url
})
.attr('target', '_blank')
.append("circle")
.attr("r", function(d) { return d.r })
.attr('fill', function(d) { return d.color })
// add tooltip
const tooltip = d3.select("#tooltip")
dot.select("circle").on('mouseover', function(d){
var name = d3.select(this).attr('id')
var oneLink = csv2.find(e=>e.label == d.index)
tooltip.transition().duration(500).style("opacity", 1)
tooltip.select("#title").text(oneLink.title)
tooltip.style("transform", `translate(${d.x+25}px,${d.y+(d.r*2*1.75)}px)`)
}).on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
function createDots(data) {
var arrays
var nodeRadius = CIRCLE_RADIUS * 2
var tilesPerRow = 1
var tileSize = nodeRadius * 2
var barWidth = tileSize
var leftBuffer = 0
var bottomBuffer = 0
arrays = []
data.map((d,i) => {
arrays.push(getTilesBar(d.values)) // get x-y coordinates of all tiles first without rendering the dotted bar chart
})
var distributed = [].concat.apply([], arrays)
return distributed
function getTilesBar(d) {
var tiles = []
for(var i = 0; i < d.length; i++) {
for(var j = 0; j < d[i].value; j++) {
var rowNumber = Math.floor(j / tilesPerRow)
var Y = d[i].y + (rowNumber + 1) * tileSize
tiles.push({
x: d[i].x,
y: d[i].depth > 1 ? Y : Y + radiusScale(d[i].value), // stack nodes within same group
color: 'mediumvioletred',
//color: scoreColorScale(d[i].key),
index: d[i].data.id + "|" + j, // index each node
r: (tileSize/1.75)/2
});
}
}
return tiles
}
}
}
}
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment