Skip to content

Instantly share code, notes, and snippets.

@Kcnarf
Last active February 14, 2020 14:31
Show Gist options
  • Save Kcnarf/5dd3477485a9de119d9b1bf2b575e5c7 to your computer and use it in GitHub Desktop.
Save Kcnarf/5dd3477485a9de119d9b1bf2b575e5c7 to your computer and use it in GitHub Desktop.
Mathentine
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-width: 1px;
stroke: hotpink;
}
</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 = 121,
multiplier = 41,
pinRadius = radius-20,
drawPinPathes = true,
drawPins = true,
chordBaseDelay = 0;
//end: drawing conf.
//begin: noticeable pair between pinAmount and multiplier
//2 below: same figure by adding pA to m (due to modulo)
//pinAmount = 123, multiplier = 4; //
//pinAmount = 123, multiplier = 127; // pA = :4+:123
//3 below: same figure <== one pattern (pA = 3m-4); higher m => higher definition
//pinAmount = 89, multiplier = 31; // pA = 3m-4
//pinAmount = 119, multiplier = 41; // pA = 3m-4
//pinAmount = 149, multiplier = 51; // pA = 3m-4
//2 below: same figure <= one pattern (pA = 2m-3); higher m => higher definition
//pinAmount = 79, multiplier = 41; // pA = 2m-3
//pinAmount = 117, multiplier = 60; // pA = 2m-3
//4 below: same figure <= one pattern, last conf (see S. Plouffe) with distinct patern
//pinAmount = 121, multiplier = 31; // pA = 4m-3
//pinAmount = 161, multiplier = 41; // pA = 4m-3
//pinAmount = 345, multiplier = 87; // pA = 4m-3
//pinAmount = 257, multiplier = 87;
//8 below: same figure with a pair of ms, pA = (m1*m2)-1
//other pattern does not work (see 2 lasts, with pA = (m1*m2)-2)
//pinAmount = 29, multiplier = 2; // pA = (2*15)-1
//pinAmount = 29, multiplier = 15; // pA = (15*2)-1
//pinAmount = 50, multiplier = 3; // pA = (3*17)-1
//pinAmount = 50, multiplier = 17; // pA = (17*3)-1
//pinAmount = 123, multiplier = 4; // pA = (4*31)-1
//pinAmount = 123, multiplier = 31; // pA = (31*4)-1
//pinAmount = 121, multiplier = 41; // pA = 3*41-2; does not work
//pinAmount = 121, multiplier = 3; // pA = 41*3-2; does not work
//2 below: same as above, with pattern/2, pA = ((m1*m2)-1)/2
//pinAmount = 25, multiplier = 3; // pA = ((3*17)-1)/2
//pinAmount = 25, multiplier = 17; // pA = ((17*3)-1)/2
//2 below: same as above, with pattern/3, pA = ((m1*m2)-1)/3
//pinAmount = 41, multiplier = 4; // pA = ((4*31)-1)/3
//pinAmount = 41, multiplier = 31; // pA = ((31*4)-1)/3
//3 below: deriving petals
//pinAmount = 121, multiplier = 31; // base pattern
//pinAmount = 242, multiplier = 31; // 2*pA => 1 clone every 2PI/2
//pinAmount = 363, multiplier = 31; // 3*pA => 2 clones every 2PI/3
//pinAmount = 484, multiplier = 31; // 4*pA => 3 clones every 2PI/4
//7 below: single petal at different nested levels, same pA
//pinAmount = 121, multiplier = 11; // pA = 12m-11
//pinAmount = 121, multiplier = 13; // pA = 10m-9
//pinAmount = 121, multiplier = 16; // pA = 8m-7
//pinAmount = 121, multiplier = 21; // pA = 6m-5
//pinAmount = 121, multiplier = 25; // pA = 5m-4
//pinAmount = 121, multiplier = 31; // pA = 4m-3
//pinAmount = 121, multiplier = 41; // pA = 3m-2
//pinAmount = 121, multiplier = 61; // pA = 2m-1 ==> not a heart
//18 below: single petal at different nested levels, same m
//produces 2 chains, c1(x)=xm-(x+1) and c2(x)=xm-(x-1)
//same figure with c1(x) & c2(x+1) (eg, x=2, same figure with 2m-3 & 3m-2)
//pinAmount = 47, multiplier = 25; // pA = 2m-3 c1
//pinAmount = 49, multiplier = 25; // pA = 2m-1 | c2
//pinAmount = 71, multiplier = 25; // pA = 3m-4 c1 |
//pinAmount = 73, multiplier = 25; // pA = 3m-2 | c2
//pinAmount = 95, multiplier = 25; // pA = 4m-5 c1 |
//pinAmount = 97, multiplier = 25; // pA = 4m-3 | c2
//pinAmount = 119, multiplier = 25; // pA = 5m-6 c1 |
//pinAmount = 121, multiplier = 25; // pA = 5m-4 | c2
//pinAmount = 143, multiplier = 25; // pA = 6m-7 c1 |
//pinAmount = 145, multiplier = 25; // pA = 6m-5 | c2
//pinAmount = 167, multiplier = 25; // pA = 7m-8 c1 |
//pinAmount = 169, multiplier = 25; // pA = 7m-6 | c2
//pinAmount = 191, multiplier = 25; // pA = 8m-9 c1 |
//pinAmount = 193, multiplier = 25; // pA = 8m-7 | c2
//pinAmount = 215, multiplier = 25; // pA = 9m-10 c1 |
//pinAmount = 217, multiplier = 25; // pA = 9m-8 | c2
//pinAmount = 215, multiplier = 25; // pA = 10m-11 c1 |
//pinAmount = 217, multiplier = 25; // pA = 10m-9 c2
//2 below: same figure but changing pattern by adding (m+1) to pA
//comes from above 'same figure with c1(x) & c2(x+1)'
//c1(x)+(m+1) = c2(x+1)
//pinAmount = 79, multiplier = 41; // pA = 2m-3
//pinAmount = 121, multiplier = 41; // pA = 3m-2 = 2m-3+(m+1)
//2 below: same figure with c1(x) & c2(x+1), higher m => higher definition
//pinAmount = 121, multiplier = 25; // pA = 5*:25-4; c2(5)
//pinAmount = 163, multiplier = 42; // pA = 4*:42-5; c1(4)
//end: noticeable pair
//begin: reusable d3 selection
var drawingArea, pathContainer, startPinContainer, endPinContainer, chordContainer;
//end: reusable d3 selection
// data
var pins, chords;
//begin: init layout
initLayout();
//end: init layout
computeData();
redraw();
function computeData() {
var pinAngle = _2PI/pinAmount
newPins = [],
newChords = [];
var i, angle;
for (i=0; i<pinAmount; i++) {
angle = pinAngle*i;
newPins.push({
x: Math.cos(angle)*pinRadius,
y: Math.sin(angle)*pinRadius
});
newChords.push({
startIndex: i,
endIndex: (i*multiplier)%pinAmount
})
}
pins = newPins;
chords = newChords;
}
function redraw() {
if (drawPinPathes) {
redrawPinPathes();
}
if (drawPins) {
redrawPins();
}
redrawChords();
}
function redrawPinPathes() {
var pathLiner = d3.line().x(function(d){ return d.x; }).y(function(d){ return d.y; });
var pathes = pathContainer.selectAll("path")
.data([pins]);
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 drawnPins = startPinContainer.selectAll(".pin").data(pins);
drawnPins.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(drawnPins)
.transition()
.duration(3000)
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
}
function redrawChords() {
var drawnChords = chordContainer.selectAll(".chord").data(chords);
drawnChords.enter()
.append("line")
.classed("chord", true)
.attr("x1", function (d){ return pins[d.startIndex].x; })
.attr("y1", function (d){ return pins[d.startIndex].y; })
.attr("x2", function (d){ return pins[d.endIndex].x; })
.attr("y2", function (d){ return pins[d.endIndex].y; })
.style("stroke-opacity", 0)
.merge(drawnChords)
.transition()
.duration(50)
.delay(function(d,i){ return i*chordBaseDelay; })
.attr("x1", function (d){ return pins[d.startIndex].x; })
.attr("y1", function (d){ return pins[d.startIndex].y; })
.attr("x2", function (d){ return pins[d.endIndex].x; })
.attr("y2", function (d){ return pins[d.endIndex].y; })
.style("stroke-opacity", 1);
}
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