polar coords -> xy
10const xyOnSpiral = (angle, margin, cx, cy, rotate = 0) => fromPolar((angle + rotate) * 360, margin * angle, cx, cy); // TODO: logSpiral
draw spiral / segment of spiral
13function spiralSegment(options) { 14 let { 15 ctx, 16 from = 0, 17 to = 3, 18 margin = 50, 19 cx = 100, 20 cy = 100, 21 rotate = 0, 22 thickness = margin / 2, 23 color = getTheme().foreground, 24 cap = 'round', 25 stretch = 1, 26 fromOpacity = 1, 27 toOpacity = 1, 28 } = options; 29 from *= stretch; 30 to *= stretch; 31 rotate *= stretch; 32 ctx.lineWidth = thickness; 33 ctx.lineCap = cap; 34 ctx.strokeStyle = color; 35 ctx.globalAlpha = fromOpacity; 36 37 ctx.beginPath(); 38 let [sx, sy] = xyOnSpiral(from, margin, cx, cy, rotate); 39 ctx.moveTo(sx, sy); 40 41 const increment = 1 / 60; 42 let angle = from; 43 while (angle <= to) { 44 const [x, y] = xyOnSpiral(angle, margin, cx, cy, rotate); 45 //ctx.lineWidth = angle*thickness; 46 ctx.globalAlpha = ((angle - from) / (to - from)) * toOpacity; 47 ctx.lineTo(x, y); 48 angle += increment; 49 } 50 ctx.stroke(); 51}
53function drawSpiral(options) { 54 let { 55 stretch = 1, 56 size = 80, 57 thickness = size / 2, 58 cap = 'butt', // round butt squar, 59 inset = 3, // start angl, 60 playheadColor = '#ffffff', 61 playheadLength = 0.02, 62 playheadThickness = thickness, 63 padding = 0, 64 steady = 1, 65 activeColor = getTheme().foreground, 66 inactiveColor = getTheme().gutterForeground, 67 colorizeInactive = 0, 68 fade = true, 69 // logSpiral = true, 70 ctx, 71 time, 72 haps, 73 drawTime, 74 id, 75 } = options; 76 77 if (id) { 78 haps = haps.filter((hap) => hap.hasTag(id)); 79 } 80 81 const [w, h] = [ctx.canvas.width, ctx.canvas.height]; 82 ctx.clearRect(0, 0, w * 2, h * 2); 83 const [cx, cy] = [w / 2, h / 2]; 84 const settings = { 85 margin: size / stretch, 86 cx, 87 cy, 88 stretch, 89 cap, 90 thickness, 91 }; 92 93 const playhead = { 94 ...settings, 95 thickness: playheadThickness, 96 from: inset - playheadLength, 97 to: inset, 98 color: playheadColor, 99 }; 100 101 const [min] = drawTime; 102 const rotate = steady * time; 103 haps.forEach((hap) => { 104 const isActive = hap.whole.begin <= time && hap.endClipped > time; 105 const from = hap.whole.begin - time + inset; 106 const to = hap.endClipped - time + inset - padding; 107 const hapColor = hap.value?.color || activeColor; 108 const color = colorizeInactive || isActive ? hapColor : inactiveColor; 109 const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1; 110 spiralSegment({ 111 ctx, 112 ...settings, 113 from, 114 to, 115 rotate, 116 color, 117 fromOpacity: opacity, 118 toOpacity: opacity, 119 }); 120 }); 121 spiralSegment({ 122 ctx, 123 ...playhead, 124 rotate, 125 }); 126}
Displays a spiral visual.
@name spiral @tags visualization @param {Object} options Object containing all the optional following parameters as key value pairs: @param {number} stretch controls the rotations per cycle ratio, where 1 = 1 cycle / 360 degrees @param {number} size the diameter of the spiral @param {number} thickness line thickness @param {string} cap style of line ends: butt (default), round, square @param {string} inset number of rotations before spiral starts (default 3) @param {string} playheadColor color of playhead, defaults to white @param {number} playheadLength length of playhead in rotations, defaults to 0.02 @param {number} playheadThickness thickness of playheadrotations, defaults to thickness @param {number} padding space around spiral @param {number} steady steadyness of spiral vs playhead. 1 = spiral doesn't move, playhead does. @param {number} activeColor color of active segment. defaults to foreground of theme @param {number} inactiveColor color of inactive segments. defaults to gutterForeground of theme @param {boolean} colorizeInactive wether or not to colorize inactive segments, defaults to 0 @param {boolean} fade wether or not past and future should fade out. defaults to 1 @param {boolean} logSpiral wether or not the spiral should be logarithmic. defaults to 0 @example note("c2 a2 eb2") .euclid(5,8) .s('sawtooth') .lpenv(4).lpf(300) ._spiral({ steady: .96 })