Playing with trigonometry to draw stars
Based on Let's draw a star, added option to tweak the crookedness.
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> |