]> git.r.bdr.sh - rbdr/r.bdr.sh/blob - jekyll/js/breadsticks.js
Add new projects
[rbdr/r.bdr.sh] / jekyll / js / breadsticks.js
1 // App Config
2
3 const kEntrypoint = "breadsticks";
4 const kGap = 50;
5 const kDrawDelay = 100;
6 const kDarkDrawingColor = '#fff';
7 const kLightDrawingColor = '#000';
8 const kMaxVectorMagnitude = 150; // max size of a vector
9 const kMagnitudeColorMultiplier = 2.5; // how large should the magnitude be before it's completely transparent
10
11 /***********************************************
12 * Breadstick generates a matrix of points
13 * and assigns a vector to each entry
14 * and then draws them
15 ***********************************************/
16
17 /*
18 * Generate field of points in which to draw
19 */
20
21 // Updates the field object using a canvas
22
23 const updateFieldFromCanvas = (field, canvas) => {
24 updateFieldFromRect(field, canvas.getBoundingClientRect());
25
26 canvas.width = window.innerWidth;
27 canvas.height = window.innerHeight;
28
29 window.addEventListener('resize', () => {
30 canvas.width = window.innerWidth;
31 canvas.height = window.innerHeight;
32 updateFieldFromRect(field, canvas.getBoundingClientRect());
33 });
34 };
35
36 // Updates a field object using a rect
37
38 const updateFieldFromRect = (field, rect) => {
39 field.width = rect.width;
40 field.height = rect.height;
41 }
42
43 /*
44 * Generate the matrix of vectors
45 */
46
47 // Updates a matrix for a given field using the mouse
48
49
50 const updateFieldMatrixFromMouse = (field, matrix, gap = 50) => {
51 const mousePosition = {
52 x: 0,
53 y: 0
54 };
55
56 fillMatrix(matrix, mousePosition.x, mousePosition.y, field.width, field.height, gap);
57
58 const updateMousePosition = document.addEventListener('mousemove', (event) => {
59 mousePosition.x = event.clientX;
60 mousePosition.y = event.clientY;
61 fillMatrix(matrix, mousePosition.x, mousePosition.y, field.width, field.height, gap);
62 });
63 };
64
65 // Gets a matrix by calculating the offset between position and field.
66
67 const fillMatrix = (matrix, x, y, w, h, gap = 50) => {
68 for (let i = 0; i < w / gap; ++i) {
69 matrix[i] = [];
70 for (let j = 0; j < h / gap; ++j) {
71 let targetX = i * gap;
72 let targetY = j * gap;
73 matrix[i][j] = calculateOffsetVector(x, y, targetX, targetY, w, h);
74 }
75 }
76 };
77
78 // Calculates the offset vector of a point
79 const calculateOffsetVector = (sourceX, sourceY, targetX, targetY, w, h) => {
80 const xOffset = targetX - sourceX;
81 const yOffset = targetY - sourceY;
82
83 const calculatedMagnitude = Math.sqrt(Math.pow(xOffset, 2) + Math.pow(yOffset, 2));
84
85 return {
86 position: {
87 x: targetX,
88 y: targetY
89 },
90 color: getColor(calculatedMagnitude),
91 angle: Math.atan2(xOffset, yOffset),
92 magnitude: Math.min(calculatedMagnitude, kMaxVectorMagnitude)
93 };
94 };
95
96
97 /*
98 * Drawing
99 */
100
101 // draw a vector
102 const drawVector = (context, i, j, vector) => {
103 const x = vector.position.x - vector.magnitude * Math.sin(vector.angle);
104 const y = vector.position.y - vector.magnitude * Math.cos(vector.angle);
105 context.strokeStyle = vector.color;
106 context.beginPath();
107 context.moveTo(vector.position.x, vector.position.y);
108 context.lineTo(Math.round(x), Math.round(y));
109 context.stroke();
110 };
111
112 // Gets the color depending on the calculated magnitude
113 // as well as the color scheme
114
115 const getColor = (calculatedMagnitude) => {
116
117 let colorValue = Math.round(calculatedMagnitude * 255 / (kMaxVectorMagnitude * kMagnitudeColorMultiplier)) % 255;
118
119 if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
120 colorValue = 255 - colorValue % 255;
121 }
122
123 // Initialize an array with the value, converts it to a hex string and joins it.
124 return `#${Array(3).fill(colorValue.toString(16)).join('')}`;
125 };
126
127
128 // Draws field on canvas
129
130 const drawFieldMatrixOnCanvas = (matrix, context) => {
131
132 context.setLineDash([25, 10])
133 context.clearRect(0, 0, context.canvas.width, context.canvas.height);
134
135 for (let i = 0; i < matrix.length; ++i) {
136 const row = matrix[i];
137 for (let j = 0; j < row.length; ++j) {
138 const vector = row[j]
139
140 if (vector) {
141 drawVector(context, i, j, vector);
142 }
143 }
144 }
145 };
146
147 /*
148 * Entrypoint
149 */
150
151 const run = () => {
152
153 const field = {
154 width: 0,
155 height: 0
156 };
157 const canvas = document.getElementById(kEntrypoint);
158
159 updateFieldFromCanvas(field, canvas);
160
161 const matrix = [[]];
162 updateFieldMatrixFromMouse(field, matrix, kGap);
163
164 const context = canvas.getContext('2d');
165 const drawFunction = () => {
166 drawFieldMatrixOnCanvas(matrix, context);
167 setTimeout(drawFunction, kDrawDelay);
168 }
169
170 setTimeout(drawFunction, 0);
171 };
172
173 window.addEventListener('load', run);