Skip to content

Instantly share code, notes, and snippets.

@Kcnarf
Last active December 3, 2018 08:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Kcnarf/8a779dc8ebf5712fc8cb3ccdda37e912 to your computer and use it in GitHub Desktop.
Save Kcnarf/8a779dc8ebf5712fc8cb3ccdda37e912 to your computer and use it in GitHub Desktop.
Voronoï playground : Voronoï's heart
license: gpl-3.0
border: no

This block is a recreation inspired by parquet deformation, which consists on progressively transform a shape into another one (cf. www.theguardian.com/[...]/crazy-paving-the-twisted-world-of-parquet-deformations or http://www.tess-elation.co.uk/parquet-deformations).

In this block, the inital shape is the bottom most quazi-rectangle, and it is progressively transformed into the top most shape. The final result is obtained by using a Voronoï layout, with only few cells shown, and by placing seeds at adequate positions (click to show/hide seeds).

I came to parquet deformation because the Voronoï layout makes a tessellation of the 2D plane, as the parquet does. Parquet deformation is also closely linked to Escher's researches and amazing drawings (cf. http://en.tessellations-nicolas.com/method.php).

Acknowledgments to:

<meta charset="utf-8">
<title>Voronoï playground : parquet deformation</title>
<meta content="Using D3's Voronoï layout to simulate parquet deformation" name="description">
<style>
#under-construction {
display: none;
position: absolute;
top: 200px;
left: 300px;
font-size: 40px;
}
svg {
margin: 1px;
border-radius: 1000px;
box-shadow: 2px 2px 6px grey;
}
#hover-area {
fill: transparent;
stroke: none;
cursor: crosshair;
}
#heart {
fill: pink;
text-anchor: middle;
}
.seed {
fill: grey;
}
.cell {
fill: none;
stroke: grey;
}
.hide {
display: none;
}
</style>
<body>
<div id="under-construction">
UNDER CONSTRUCTION
</div>
<svg>
<g id="drawing-area">
<path id="wave-path"/>
<g id="voronoi-container"/>
<g id="seed-container"/>
<text id="heart">&hearts;</text>
<circle id="hover-area"></circle>
</g>
</svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="simplex-noise.min.js"></script>
<script>
var _2PI = 2*Math.PI;
//begin: layout conf.
var svgbw = 1, //svg border width
svgm = 10, //svg margin
totalWidth = 500,
totalHeight = 500,
width = totalWidth-(svgm+svgbw)*2,
height = totalHeight-(svgm+svgbw)*2,
midWidth = width/2,
midHeight = height/2;
//end: layout conf.
//begin: voronoi conf.
//we oragnize seeds on 3 cocentric rings;
//only cells from the middle ring will be shown
var visibleSeedCount = 48,
hiddenSeedCount = visibleSeedCount-1,
seedDistance = 3*midWidth/5,
interRingMaxDistance = midWidth/6,
interRingDistance = 0,
interRingDistanceObjective = interRingMaxDistance, // store next interRingDistance until the current frame is displayed
interRingHeartTreshold = 0.25,
hideSeeds = true,
seeds = [];
//end: voronoi conf.
var voronoiLayout = d3.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.extent([[-(midWidth+10), -(midHeight+10)], [midWidth+10, midHeight+10]]);
var svg, drawingArea, voronoiContainer, seedContainer;
initLayout();
d3.interval(function(elapsed) {
if (interRingDistance !== interRingDistanceObjective) {
interRingDistance = interRingDistanceObjective;
computeSeeds();
redrawVoronoi();
redrawSeeds();
seedContainer.classed("hide", hideSeeds);
}
});
function initLayout() {
svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
drawingArea = d3.select("#drawing-area")
.attr("transform", "translate("+[midWidth, midHeight]+")rotate(90)");
d3.select("#hover-area")
.attr("r", midWidth)
.on("touchmove mousemove", moved)
.on("mouseout", exited)
.on("click", clicked);
voronoiContainer = d3.select("#voronoi-container");
seedContainer = d3.select("#seed-container");
heart = d3.select("#heart").attr("transform", "rotate(-90)translate(0,6)")
}
function moved() {
var coords = d3.mouse(this);
var mouseRadius = Math.sqrt(Math.pow(coords[0],2)+Math.pow(coords[1],2));
var scaledMouseRadius = mouseRadius*interRingMaxDistance/midWidth;
if (scaledMouseRadius<interRingHeartTreshold) {
interRingDistanceObjective = interRingHeartTreshold;
} else {
interRingDistanceObjective = scaledMouseRadius;
}
}
function exited() {
interRingDistanceObjective = interRingMaxDistance;
}
function clicked() {
hideSeeds = !hideSeeds;
}
function computeSeeds() {
seeds = [];
var hidden = true;
//compute seeds for visible cells
seeds = seeds.concat(ringSeeds(visibleSeedCount, 0, !hidden));
//compute seeds for inner hidden cells
seeds = seeds.concat(ringSeeds(hiddenSeedCount, -interRingDistance, hidden));
//compute seeds for outer hidden cells
seeds = seeds.concat(ringSeeds(hiddenSeedCount, interRingDistance, hidden));
}
function ringSeeds(count, distanceDelta, hidden) {
var ringSeeds=[],
interRadAngle = _2PI/count,
interDegAngle = 360/count,
radAngle, cos, sin, distance;
for(var ai=0; ai<count; ai++) {
radAngle = interRadAngle*ai;
cos = Math.cos(radAngle);
sin = Math.sin(radAngle);
distance = seedDistance+distanceDelta*Math.cos(radAngle/2);
ringSeeds.push({
x: distance*cos,
y: distance*sin,
hidden: hidden,
strokeColor: d3.hsl(Math.abs(interDegAngle*ai-180), 1, 0.45)
});
}
return ringSeeds;
}
function redrawVoronoi() {
//begin: draw Voronoi cells
voronoiContainer.selectAll(".cell").remove();
var drawnCells = voronoiContainer.selectAll(".cell")
.data(voronoiLayout.polygons(seeds));
drawnCells.enter()
.append("path")
.classed("cell", true)
.classed("hide", function(d){ return d.data.hidden; })
.attr("d", function(d){ return d3.line()(d)+"z"; })
.style("fill", function(d){ return d.data.strokeColor; });
//end: draw Voronoi cells
}
function redrawSeeds() {
//begin: draw Voronoi cells
seedContainer.selectAll(".seed").remove();
var drawnSeeds = seedContainer.selectAll(".seed")
.data(seeds);
drawnSeeds.enter()
.append("circle")
.classed("seed", true)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
.attr("r", 1);
//end: draw Voronoi cells
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment