Created by Christopher Manning
I created this to generate a serialized octocat grid for an upcoming art project. You could use this with any SVG that's only composed of individual path elements.
This is not affiliated with or endorsed by GitHub. The Octocat is a registered trademark of GitHub.
xxxxxxxxxx
<html>
<head>
<title>Octocat Grid</title>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
<script src="/christophermanning/4450188/example/javascript.util.min.js"></script>
<script src="/christophermanning/4450188/example/jsts.min.js"></script>
<!--<script type="text/javascript" src="/js/d3/d3.min.js"></script>-->
<!--<script type="text/javascript" src="/js/dat-gui/build/dat.gui.min.js"></script> -->
<!--<script type="text/javascript" src="/js/jsts/lib/javascript.util.js"></script>-->
<!--<script type="text/javascript" src="/js/jsts/lib/jsts.js"></script>-->
<style type="text/css">
body {
padding: 0
margin: 0
}
</style>
</head>
<body>
<script type="text/javascript">
config = {"svg": true, "grid": true}
gui = new dat.GUI()
svgChanger = gui.add(config, "svg")
svgChanger.onChange(function(value) {
d3.select("#octocat").attr("display", value ? "block" : "none")
});
gridChanger = gui.add(config, "grid")
gridChanger.onChange(function(value) {
d3.select("#grid").attr("display", value ? "block" : "none")
});
config.grid_to_console = function(){
console.log(JSON.stringify(rects[0].map(function(d) { return d.__data__ }).filter(function(d) { return d.color })))
}
gui.add(config, "grid_to_console")
margin = {top: 10, left: 10, bottom: 10}
colors = [null]
size = 8
height = window.innerHeight
ocHeight = height - margin.top - margin.bottom
geometryFactory = new jsts.geom.GeometryFactory()
rTree = new jsts.index.strtree.STRtree()
d3.xml(window.location.hostname == "localhost" ? "/gists/octocat.svg/octocat.svg" : "https://bl.ocks.org/d/4460135/octocat.svg", "image/svg+xml", function(xml) {
viewBox = d3.select(xml).selectAll("svg").attr("viewBox").split(" ")
w = parseInt(viewBox[2])
h = parseInt(viewBox[3])
width = (ocHeight/h)*w
x = d3.scale.linear().domain([0, w]).range([0, width])
y = d3.scale.linear().domain([0, h]).range([0, ocHeight])
line = d3.svg.line()
//.interpolate(config["interpolation"])
//.tension(config["tension"])
.x(function(d, i) { return d.x })
.y(function(d, i) { return d.y })
svg = d3.select("body").append("svg")
.attr("fill", "none")
.attr("width", width)
.attr("height", height)
g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
oc = g.append("g").attr("id", "octocat")
path = oc.selectAll("path")
.data(d3.select(xml).selectAll("path")[0])
.enter().append("path")
.each(function(d, i) {
// convert path to polygon
var samples = 200
var ring = []
var len = d.getTotalLength();
var step = step=len/samples;
for (var j=0;j<=len;j+=step){
var p=d.getPointAtLength(j);
ring.push(new jsts.geom.Coordinate(x(p.x), y(p.y)))
}
// close the polygon
ring.push(ring[0])
var shell = geometryFactory.createLinearRing(ring)
var p = geometryFactory.createPolygon(shell)
d.polygon = p
d.color = d3.select(d).attr("fill")
d.id = d3.select(d).attr("id")
d.zindex = i
rTree.insert(p.getEnvelopeInternal(), d)
if(colors.indexOf(d.color) == -1) colors.push(d.color)
})
.attr("id", function(d) { return d3.select(d).attr("id") })
.attr("fill", function(d) { return d3.select(d).attr("fill") })
.attr("stroke", "black")
.attr("d", function(d) { return line(d.polygon.shell.points) })
// grid
var rectangles = []
for (var x = 0; x <= width; x+=size+1) {
for (var y = 0; y <= height; y+=size+1) {
rectangles.push({x: x, y: y})
}
}
grid = g.append("g").attr("id", "grid")
rects = grid.selectAll("rect")
.data(rectangles)
.enter().append("rect")
.attr("x", function(d) { return d.x })
.attr("y", function(d) { return d.y })
.attr("width", function(d) { return size })
.attr("height", function(d) { return size })
.attr("stroke", function(d) {
var c1 = new jsts.geom.Coordinate(d.x, d.y)
var c2 = new jsts.geom.Coordinate(d.x+size, d.y)
var c3 = new jsts.geom.Coordinate(d.x+size, d.y+size)
var c4 = new jsts.geom.Coordinate(d.x, d.y+size)
var shell = geometryFactory.createLinearRing([c1,c2,c3,c4,c1])
var searchPolygon = geometryFactory.createPolygon(shell)
d.color = ""
rTree.query(searchPolygon.getEnvelopeInternal()).sort(function compare(a, b) {
// sort by z-index so the top most intersection is filled
if (a.zindex > b.zindex) return -1
if (a.zindex < b.zindex) return 1
return 0
}).some(function(p) {
intersection = searchPolygon.intersection(p.polygon)
if(searchPolygon.intersects(p.polygon)) {
d.color = p.color
d.id = p.id
return true
}
})
return d.color
})
.on("mousedown", function(d) {
newColor = colors[(colors.indexOf(d.color)+1) % colors.length]
d.color = newColor
if(newColor == null) {
d3.select(this).attr("stroke", "red")
} else {
d3.select(this).attr("stroke", newColor)
}
})
// so mousedown works
.attr("fill", function(d) { return "transparent" })
})
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
Updated missing url http://bl.ocks.org/d/4450188/javascript.util.min.js to /christophermanning/4450188/example/javascript.util.min.js
Updated missing url http://bl.ocks.org/d/4450188/jsts.min.js to /christophermanning/4450188/example/jsts.min.js
https://d3js.org/d3.v3.min.js
https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js
https://bl.ocks.org/d/4450188/javascript.util.min.js
https://bl.ocks.org/d/4450188/jsts.min.js