Skip to content

Instantly share code, notes, and snippets.

@gcalmettes
Last active September 28, 2017 01:48
Show Gist options
  • Save gcalmettes/6f702041888cdae19591694be1088760 to your computer and use it in GitHub Desktop.
Save gcalmettes/6f702041888cdae19591694be1088760 to your computer and use it in GitHub Desktop.
The crooked star show
license: gpl-3.0

Playing with trigonometry to draw stars

Based on Let's draw a star, added option to tweak the crookedness.

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
label {
display: inline-block;
width: 7em;
font-family: sans-serif;
font-size: 1.3em;
}
input {
width: 20em;
}
span {
font-size: 1.3em;
}
</style>
<body>
<div>
<div>
<label style="color: #F25754">Branches #:</label>
<input type="range" min="1" max="20" id="nSpikes" value=20>
<span id="nSpikes-value" style="color: #F25754;">20</span>
</div>
<div>
<label style="color: #EDCA3A">Inner Radius:</label>
<input type="range" min="0" max="190" id="innerRadius" value=95>
<span id="innerRadius-value" style="color: #EDCA3A;">95</span>
</div>
<div>
<label style="color: #1FBAD6">Outer Radius:</label>
<input type="range" min="0" max="190" id="outerRadius" value=135>
<span id="outerRadius-value" style="color: #1FBAD6;">135</span>
</div>
</div>
<label>Crookedness:</label>
<input type="range" min="0" id="crookedNess" step=0.01 value=3.78>
<span id="crookedNess-value">3.78</span>
</div>
</div>
<div>
<svg width="500" height="400"></svg>
</div>
<script>
const formatNumber = d3.format(",.2f")
d3.select("#crookedNess").attr("max", 2*Math.PI)
d3.select("#crookedNess-value").text(formatNumber(+d3.select("#crookedNess-value").text()))
svg = d3.select("svg")
gStar = svg.append("g")
.attr("transform", `translate(${+svg.attr('width')/2}, ${+svg.attr('height')/2})`)
//inner/outer points for spikes
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(20, 95, 135, 3.78)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
function updateStar(innerStarPoints, outerStarPoints, mergedStarPoints){
//draw the star
starPath = gStar.selectAll(".starPath")
.data([mergedStarPoints])
starPath.exit().remove()
enterStarPath = starPath.enter()
.append("path")
.attr("d", d => getStarPath(d))
.attr("class", "starPath")
.attr("stroke", "#F25754")
.attr("stroke-width", 2)
.attr("fill", "none")
.merge(starPath)
.attr("d", d => getStarPath(d))
outerPoints = gStar.selectAll(".outerPoints")
.data(outerStarPoints)
outerPoints.exit().remove()
enterOuterPoints = outerPoints.enter()
.append("circle")
.attr("class", "outerPoints")
.attr('r', 4)
.attr('fill', "#1FBAD6")
.attr('stroke', "black")
.attr('stroke-width', 1)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.merge(outerPoints)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
innerPoints = gStar.selectAll(".innerPoints")
.data(innerStarPoints)
innerPoints.exit().remove()
enterInnerPoints = innerPoints.enter()
.append("circle")
.attr("class", "innerPoints")
.attr('r', 4)
.attr('fill', "#EDCA3A")
.attr('stroke', "black")
.attr('stroke-width', 1)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.merge(innerPoints)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
}
function getStarPoints(numSpikes, innerRadius, outerRadius, crookedRad=0){
//outerRadius (spike points)
let outerPoints = d3.range(numSpikes).map(i => {
let angle = i*(2*Math.PI/numSpikes);
if (crookedRad != 0) {
//apply shift in radians
angle = angle + crookedRad
}
return {
x: Math.cos(angle)*outerRadius,
y: Math.sin(angle)*outerRadius
}
})
//innerRadius (spike points)
let innerPoints = d3.range(numSpikes).map(i => {
let angle = i*(2*Math.PI/(numSpikes));
return {
x: Math.cos(angle + Math.PI/numSpikes)*innerRadius,
y: Math.sin(angle + + Math.PI/numSpikes)*innerRadius
}
})
//merge points by alternating outer/inner points
let mergePoints = outerPoints.map((d,i) =>
[d, innerPoints[i]]).reduce((a,b) => a.concat(b))
return [innerPoints, outerPoints, mergePoints]
}
function getStarPath(points){
//just a closed line generator
const lineGenerator = d3.line()
.x(d => d.x)
.y(d => d.y)
.curve(d3.curveLinearClosed)
return lineGenerator(points)
}
// number of spikes
d3.select("#nSpikes")
.on("input", function () {
//update displayed value
d3.select("#nSpikes-value").text(+this.value);
//current inner/outer radius values
let innerRadius = +d3.select("#innerRadius-value").text()
let outerRadius = +d3.select("#outerRadius-value").text()
let crookedNess = +d3.select("#crookedNess-value").text()
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(+this.value, innerRadius, outerRadius, crookedNess)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
});
// inner Radius
d3.select("#innerRadius")
.on("input", function () {
//update displayed value
d3.select("#innerRadius-value").text(+this.value);
//currrent number of spikes and outerRadius
let nSpikes = +d3.select("#nSpikes-value").text()
let outerRadius = +d3.select("#outerRadius-value").text()
let crookedNess = +d3.select("#crookedNess-value").text()
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(nSpikes, +this.value, outerRadius, crookedNess)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
});
// outer Radius
d3.select("#outerRadius")
.on("input", function () {
//update displayed value
d3.select("#outerRadius-value").text(+this.value);
//currrent number of spikes and innerRadius
let nSpikes = +d3.select("#nSpikes-value").text()
let innerRadius = +d3.select("#innerRadius-value").text()
let crookedNess = +d3.select("#crookedNess-value").text()
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(nSpikes, innerRadius, +this.value, crookedNess)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
});
d3.select('#crookedNess')
.on("input", function () {
d3.select("#crookedNess-value").text(formatNumber(+this.value));
//currrent number of spikes and innerRadius
let nSpikes = +d3.select("#nSpikes-value").text()
let innerRadius = +d3.select("#innerRadius-value").text()
let outerRadius = +d3.select("#outerRadius-value").text()
let crookedNess = +d3.select("#crookedNess-value").text()
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(nSpikes, innerRadius, outerRadius, crookedNess)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
}
);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment