Playing with trigonometry to draw stars.
See The crooked star show, for crookedness tweaking.
| license: gpl-3.0 |
Playing with trigonometry to draw stars.
See The crooked star show, for crookedness tweaking.
| <!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; | |
| } | |
| #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> |