1import * as _ from 'lodash-es' 4function drawCurve(ctx: CanvasRenderingContext2D, ptsa: number[], tension?: number, isClosed?: boolean, numOfSegments?: number, showPoints?: boolean) { 8 drawLines(ctx, getCurvePoints(ptsa, tension, isClosed, numOfSegments)); 12 for(var i=0;i<ptsa.length-1;i+=2) 13 ctx.rect(ptsa[i] - 2, ptsa[i+1] - 2, 4, 4); 19function getCurvePoints(pts: number[], tension?: number, isClosed?: boolean, numOfSegments?: number): number[] { 21 // use input value if provided, or use a default value 22 tension = (typeof tension != 'undefined') ? tension : 0.5; 23 isClosed = isClosed ? isClosed : false; 24 numOfSegments = numOfSegments ? numOfSegments : 16; 26 var _pts = [], res = [], // clone array 27 x, y, // our x,y coords 28 t1x, t2x, t1y, t2y, // tension vectors 29 c1, c2, c3, c4, // cardinal points 30 st, t, i; // steps based on num. of segments 32 // clone array so we don't change the original 36 // The algorithm require a previous and next point to the actual point array. 37 // Check if we will draw closed or open curve. 38 // If closed, copy end points to beginning and first points to end 39 // If open, duplicate first points to befinning, end points to end 41 _pts.unshift(pts[pts.length - 1]); 42 _pts.unshift(pts[pts.length - 2]); 43 _pts.unshift(pts[pts.length - 1]); 44 _pts.unshift(pts[pts.length - 2]); 49 _pts.unshift(pts[1]); //copy 1. point and insert at beginning 51 _pts.push(pts[pts.length - 2]); //copy last point and append 52 _pts.push(pts[pts.length - 1]); 57 // 1. loop goes through point array 58 // 2. loop goes through each segment between the 2 pts + 1e point before and after 59 for (i=2; i < (_pts.length - 4); i+=2) { 60 for (t=0; t <= numOfSegments; t++) { 62 // calc tension vectors 63 t1x = (_pts[i+2] - _pts[i-2]) * tension; 64 t2x = (_pts[i+4] - _pts[i]) * tension; 66 t1y = (_pts[i+3] - _pts[i-1]) * tension; 67 t2y = (_pts[i+5] - _pts[i+1]) * tension; 70 st = t / numOfSegments; 73 c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1; 74 c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); 75 c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st; 76 c4 = Math.pow(st, 3) - Math.pow(st, 2); 78 // calc x and y cords with common control vectors 79 x = c1 * _pts[i] + c2 * _pts[i+2] + c3 * t1x + c4 * t2x; 80 y = c1 * _pts[i+1] + c2 * _pts[i+3] + c3 * t1y + c4 * t2y; 82 //store points in array 92function drawLines(ctx: CanvasRenderingContext2D, pts: number[]) { 93 ctx.moveTo(pts[0], pts[1]); 94 for(let i=2;i<pts.length-1;i+=2) ctx.lineTo(pts[i], pts[i+1]); 97export const paintTrail = ({ctx, trail}: {ctx: CanvasRenderingContext2D, trail: Trail}) => { 98 const {points, color, cursorSize} = trail 99 if (!points || points.length == 0) return 101 const firstPoint = points[0] 102 ctx.moveTo(firstPoint[0], firstPoint[1]) 105 if (points.length == 1) { 106 ctx.arc(firstPoint[0], firstPoint[1], cursorSize, 0, Math.PI * 2) 107 ctx.fillStyle = color 110 ctx.strokeStyle = color 111 ctx.lineWidth = cursorSize * 2 112 ctx.lineCap = 'round' 113 ctx.lineJoin = 'round' 115 ctx.moveTo((points[0][0]), points[0][1]); 117 drawCurve(ctx, _.flatten(points))