Skip to content

Instantly share code, notes, and snippets.

@Kcnarf
Last active February 7, 2018 13:27
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/ff6214f1d23560874b6df5de9f9c46d9 to your computer and use it in GitHub Desktop.
Save Kcnarf/ff6214f1d23560874b6df5de9f9c46d9 to your computer and use it in GitHub Desktop.
2 times tables modulo
license: gpl-3.0
border: no
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Cardioïdes</title>
<meta name="description" content="Some fun at making visualization of times tables modulo, with D3js">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
#layouter {
text-align: center;
position: relative;
}
#wip {
display: none;
position: absolute;
top: 220px;
left: 110px;
font-size: 40px;
text-align: center;
}
svg {
margin: 1px;
border-radius: 1000px;
box-shadow: 2px 2px 6px grey;
}
.pin-path {
fill: none;
stroke: lightgrey;
}
.pin {
fill: lightgrey;
}
.chord {
fill: none;
stroke: lightgreen;
stroke-width: 1px;
}
</style>
</head>
<body>
<div id="layouter">
<svg></svg>
<div id="wip">
Work in progress ...
</div>
</div>
<script>
var _PI = Math.PI,
_2PI = 2*Math.PI;
//begin: layout conf.
var totalWidth = 500,
totalHeight = 500,
controlsHeight = 0,
svgbw = 1, // canvas border width
svgbs = 8, // canvas box-shadow
radius = (totalHeight-controlsHeight-svgbs-2*svgbw)/2,
width = 2*radius,
height = 2*radius,
halfRadius = radius/2
halfWidth = halfRadius,
halfHeight = halfRadius,
quarterRadius = radius/4;
quarterWidth = quarterRadius,
quarterHeight = quarterRadius;
//end: layout conf.
//begin: drawing conf.
var pinAmount = 2*3*4*5, // 120, divisible by 2,3,4,5,6,8,10
multiplier = 2, // an integer, preferably in [2,3,4,5,6]
startRadius = radius-20,
endRadius = startRadius,
startWHRatio = 1, // width/height ratio of starting pins shape
endWHRatio = 1, // width/height ratio of ending pins shape
// width/height ratio of pins shape :
// >1 => v-elipsis
// 1 => circle
// >0 && <1 => h-elipsis
// 0 => line
// <0 => curbersome !!!
drawPinPathes = true,
drawPins = true;
//end: drawing conf.
//begin: reusable d3 selection
var drawingArea, pathContainer, startPinContainer, endPinContainer, chordContainer;
//end: reusable d3 selection
// data
var startPins, endPins, chords, shuffleType = 'radius', shuffleCount = 0;
//begin: init layout
initLayout();
//end: init layout
computeData();
redraw();
d3.interval(function(elapsed) {
shuffleConf()
computeData();
redraw();
}, 3500);
function shuffleConf() {
var nextShuffleType, newRatio;
shuffleCount++;
if (shuffleCount%7 === 0) {
endRadius = startRadius;
startWHRatio = 1;
endWHRatio = 1;
} else {
if (shuffleType==='radius') {
endRadius = startRadius*(0.5+Math.random()/2);
} else if (shuffleType==='startWHRatio') {
newRatio = 5*Math.random();
if (startWHRatio>1) {
startWHRatio = 1/newRatio;
} else {
startWHRatio = newRatio;
}
} else {
newRatio = 5*Math.random();
if (endWHRatio>1) {
endWHRatio = 1/newRatio;
} else {
endWHRatio = newRatio;
}
}
}
nextShuffleType = shuffleType;
while(nextShuffleType === shuffleType) {
rand = Math.random();
if (rand<0.2) {
nextShuffleType = 'radius';
} else if (rand<0.6) {
nextShuffleType = 'startWHRatio';
} else {
nextShuffleType = 'endWHRatio';
}
}
shuffleType = nextShuffleType;
}
function computeData() {
var pinAngle = _2PI/pinAmount,
startWidth = (startWHRatio>1)? startRadius : startRadius*startWHRatio,
startHeight = (startWHRatio>1)? startRadius/startWHRatio : startRadius,
endWidth = (endWHRatio>1)? endRadius : endRadius*endWHRatio,
endHeight = (endWHRatio>1)? endRadius/endWHRatio : endRadius,
newStartPins = [],
newEndPins = [],
newChords = [];
var i, startAngle, endAngle;
for (i=0; i<pinAmount; i++) {
startAngle = pinAngle*i;
endAngle = startAngle;
newStartPins.push({
x: Math.cos(startAngle)*startWidth,
y: Math.sin(startAngle)*startHeight
});
newEndPins.push({
x: Math.cos(endAngle)*endWidth,
y: Math.sin(endAngle)*endHeight
});
newChords.push({
startIndex: i,
endIndex: (i*multiplier)%pinAmount
})
}
startPins = newStartPins;
endPins = newEndPins;
chords = newChords;
}
function redraw() {
if (drawPinPathes) {
redrawPinPathes();
}
if (drawPins) {
redrawPins();
}
redrawLines();
}
function redrawPinPathes() {
var pathLiner = d3.line().x(function(d){ return d.x; }).y(function(d){ return d.y; });
var pathes = pathContainer.selectAll("path")
.data([startPins, endPins]);
pathes.enter()
.append("path")
.classed("pin-path", true)
.attr("d", function(d){ return pathLiner(d)+"z"; })
.merge(pathes)
.transition()
.duration(3000)
.attr("d", function(d){ return pathLiner(d)+"z"; });
}
function redrawPins() {
var drawnStartPins = startPinContainer.selectAll(".pin").data(startPins);
var drawnEndPins = endPinContainer.selectAll(".pin").data(endPins);
drawnStartPins.enter()
.append("circle")
.classed("pin", true)
.attr("r", 1.5)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
.merge(drawnStartPins)
.transition()
.duration(3000)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
drawnEndPins.enter()
.append("circle")
.classed("pin", true)
.attr("r", 1.5)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
.merge(drawnEndPins)
.transition()
.duration(3000)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
}
function redrawLines() {
var drawnChords = chordContainer.selectAll(".chord").data(chords);
drawnChords.enter()
.append("line")
.classed("chord", true)
.attr("x1", function (d){ return startPins[d.startIndex].x; })
.attr("y1", function (d){ return startPins[d.startIndex].y; })
.attr("x2", function (d){ return endPins[d.endIndex].x; })
.attr("y2", function (d){ return endPins[d.endIndex].y; })
.merge(drawnChords)
.transition()
.duration(3000)
.attr("x1", function (d){ return startPins[d.startIndex].x; })
.attr("y1", function (d){ return startPins[d.startIndex].y; })
.attr("x2", function (d){ return endPins[d.endIndex].x; })
.attr("y2", function (d){ return endPins[d.endIndex].y; });
}
function initLayout() {
d3.select("#layouter").style("width", totalWidth+"px").style("height", totalHeight+"px");
drawingArea = d3.select("svg").attr("width", width).attr("height", height)
.append("g")
.classed("drawing-area", true)
. attr("transform", "translate("+[radius, radius]+")rotate(-90)");
pathContainer = drawingArea.append("g").attr("id", "path-container");
startPinContainer = drawingArea.append("g").attr("id", "pin-container");
endPinContainer = drawingArea.append("g").attr("id", "pin-container");
chordContainer = drawingArea.append("g").attr("id", "chord-container");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment