Playing with trigonometry to draw stars.
See The crooked star show, for crookedness tweaking.
xxxxxxxxxx
<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;
}
#crookedStar-checkbox {
position: absolute;
top: 20px;
right: 30px;
}
</style>
<body>
<div>
<div>
<label style="color: #F25754">Branches #:</label>
<input type="range" min="1" max="20" id="nSpikes" value=5>
<span id="nSpikes-value" style="color: #F25754;">7</span>
</div>
<div>
<label style="color: #EDCA3A">Inner Radius:</label>
<input type="range" min="0" max="190" id="innerRadius" value=50>
<span id="innerRadius-value" style="color: #EDCA3A;">50</span>
</div>
<div>
<label style="color: #1FBAD6">Outer Radius:</label>
<input type="range" min="0" max="190" id="outerRadius" value=100>
<span id="outerRadius-value" style="color: #1FBAD6;">100</span>
</div>
</div>
<div id="crookedStar-checkbox">
<label id="crookedStar-label" for="crookedStar">
Crooked star
<input type="checkbox" id="crookedStar" value="crookedValue">
</label>
</div>
<div>
<svg width="500" height="400"></svg>
</div>
<script>
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(7, 50, 100)
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, crooked=false){
//outerRadius (spike points)
let outerPoints = d3.range(numSpikes).map(i => {
let angle = i*(2*Math.PI/numSpikes);
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
}
})
let mergePoints;
if (crooked) {
//merge points by alternating inner/outer points
mergePoints = innerPoints.map((d,i) =>
[d, outerPoints[i]]).reduce((a,b) => a.concat(b))
} else {
//merge points by alternating outer/inner points
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 crooked = d3.select("#crookedStar").node().checked
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(+this.value, innerRadius, outerRadius, crooked)
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 crooked = d3.select("#crookedStar").node().checked
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(nSpikes, +this.value, outerRadius, crooked)
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 crooked = d3.select("#crookedStar").node().checked
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(nSpikes, innerRadius, +this.value, crooked)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
});
d3.select('#crookedStar')
.on("change", function () {
//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 crooked = d3.select("#crookedStar").node().checked
//update star
let [innerStarPoints, outerStarPoints, mergedStarPoints] = getStarPoints(nSpikes, innerRadius, outerRadius, crooked)
updateStar(innerStarPoints, outerStarPoints, mergedStarPoints)
}
);
</script>
</body>
https://d3js.org/d3.v4.min.js