🌳
pt0/sharedF/canvasF/paintTrailF.mts
1import * as _ from 'lodash-es'
2import type { Trail } from './renderFinishedTrailsF.mts'
4function drawCurve(ctx: CanvasRenderingContext2D, ptsa: number[], tension?: number, isClosed?: boolean, numOfSegments?: number, showPoints?: boolean) {
6 ctx.beginPath();
8 drawLines(ctx, getCurvePoints(ptsa, tension, isClosed, numOfSegments));
9
10 if (showPoints) {
11 ctx.beginPath();
12 for(var i=0;i<ptsa.length-1;i+=2)
13 ctx.rect(ptsa[i] - 2, ptsa[i+1] - 2, 4, 4);
14 }
16 ctx.stroke();
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
33 //
34 _pts = pts.slice(0);
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
40 if (isClosed) {
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]);
45 _pts.push(pts[0]);
46 _pts.push(pts[1]);
47 }
48 else {
49 _pts.unshift(pts[1]); //copy 1. point and insert at beginning
50 _pts.unshift(pts[0]);
51 _pts.push(pts[pts.length - 2]); //copy last point and append
52 _pts.push(pts[pts.length - 1]);
53 }
55 // ok, lets start..
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;
69 // calc step
70 st = t / numOfSegments;
72 // calc cardinals
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
83 res.push(x);
84 res.push(y);
86 }
87 }
89 return res;
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])
104 ctx.beginPath()
105 if (points.length == 1) {
106 ctx.arc(firstPoint[0], firstPoint[1], cursorSize, 0, Math.PI * 2)
107 ctx.fillStyle = color
108 ctx.fill()
109 } else {
110 ctx.strokeStyle = color
111 ctx.lineWidth = cursorSize * 2
112 ctx.lineCap = 'round'
113 ctx.lineJoin = 'round'
114 ctx.stroke()
115 ctx.moveTo((points[0][0]), points[0][1]);
117 drawCurve(ctx, _.flatten(points))
118 }