I have used d3 to draw a sine wave going around a circle. This is the very first time I've used d3 or drawn a SVG and I'm fairly new to JS as well, so I don't know if I've overcomplicated it/if there is a simpler way to achieve this. I'd appreciate some feedback - especially if there's any way to make my code more concise.
See my codepen.
const svg = d3.select('svg');
const margin = { top: 50, right: 50, bottom: 50, left: 50 };
const width = +svg.attr('width') - margin.left - margin.right;
const height = +svg.attr('height') - margin.top - margin.bottom;
// content area of your visualization
const vis = svg
.append('g')
.attr('transform', `translate(${margin.left+width/2},${margin.top+height/2})`);
// show area inside of margins
const rect = vis
.append('rect')
.attr('class', 'content')
.attr('width', width)
.attr('height', height)
.attr('transform', `translate(${-width/2},${-height/2})`);
// show scales
const xScale = d3
.scaleLinear()
.domain([-100, 100])
.range([-width/2, width/2]);
const yScale = d3
.scaleLinear()
.domain([100, -100])
.range([-height/2, height/2]);
vis.append('g').call(d3.axisTop(xScale));
vis.append('g').call(d3.axisLeft(yScale));
// draw circle
const pi = Math.PI
const radius = 63.66
const circle = vis
.append('circle')
.style('stroke-dasharray', '3, 3')
.style('stroke', 'black')
.style("fill", "transparent")
.attr("r", xScale(radius))
.attr("cx", 0)
.attr("cy", 0)
// get coordinates for a sine wave
const getSineWave = ({
numWaves,
wavelength,
amplitude,
phase,
numPointsPerWave,
}) => {
return (
d3.range(numWaves*numPointsPerWave+1).map(function(k) {
const x = k * wavelength/numPointsPerWave
return [x, amplitude * Math.sin(phase + 2 * pi * x/wavelength)];
})
)
}
// tranform a coordinate from linear space to circular space
const rotate = (cx, cy, x, y, radius) => {
const theta = x/radius,
sin = Math.sin(theta),
cos = Math.cos(theta),
nx = cx + (radius + y) * sin,
ny = cy + (radius + y) * cos
return [nx, ny];
}
// generate sine wave
const numWaves = 4
const amplitude = 10
const phase = pi/2
const circumference = 2 * pi * radius
const wavelength = circumference / numWaves
const numPointsPerWave = 4
const sineWave = getSineWave({
numWaves,
numPointsPerWave,
wavelength,
amplitude,
phase,
wavelength
})
var rotatedSine = sineWave.map( d => {
const rotatedCoords = rotate(0, 0, d[0], d[1], radius)
return rotatedCoords
})
// remove the last point as it would overlap the first point of the circle
rotatedSine.pop()
// get Path commands for given coordinates
const getPath = d3.line()
.x(d => xScale(d[0]))
.y(d => yScale(d[1]))
.curve(d3.curveCardinalClosed)
// draw sine wave going around a circle
const wave = vis
.append('path')
.attr('d', getPath(rotatedSine))
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', '1px')
svg {
background-color: steelblue;
}
.content {
fill: lightsteelblue;
}
<script src="https://d3js.org/d3.v4.js"></script>
<svg width="1000" height="1000" </ svg>