]> git.lizzy.rs Git - lagrange-playground.git/blob - init.js
af902fde75ec56db76b1ee5cd018dc1fbf8622ed
[lagrange-playground.git] / init.js
1 const canvas = document.getElementsByTagName("canvas")[0];
2 const ctx = canvas.getContext("2d");
3
4 let center;
5 let centerF = [0.5, 0.5];
6 let scale = 50;
7 let positions = [];
8 let func = null;
9 let drag = false;
10 let dragPos = null;
11 let dragIndex = -1;
12
13 const coordinateToScreen = (x, y) => {
14         return [
15                 center[0] + x * scale,
16                 center[1] + y * scale,
17         ];
18 };
19
20 const screenToCoordinate = (x, y) => {
21         return [
22                 (x - center[0]) / scale,
23                 (y - center[1]) / scale,
24         ];
25 };
26
27 const evalPos = (a, b, c, x) => {
28     return a + (b + c * x) * x;
29 };
30
31 const lagrange = _ => {
32         func = x => {
33                 let sum = 0;
34
35                 for (let i = 0; i < positions.length; i++) {
36                         let prod = positions[i][1];
37
38                         for (let j = 0; j < positions.length; j++)
39                                 if (j != i)
40                                         prod *= (x - positions[j][0]) / (positions[i][0] - positions[j][0]);
41
42                         sum += prod;
43                 }
44
45                 return sum;
46         };
47 };
48
49 const draw = _ => {
50         ctx.clearRect(0, 0, innerWidth, innerHeight);
51
52         for (let x = center[0] % scale; x < innerWidth; x += scale) {
53                 ctx.lineWidth = x == center[0] ? 2 : 1;
54                 ctx.beginPath();
55                 ctx.moveTo(x, 0);
56                 ctx.lineTo(x, innerHeight);
57                 ctx.stroke();
58         }
59
60         for (let y = center[1] % scale; y < innerHeight; y += scale) {
61                 ctx.lineWidth = y == center[1] ? 2 : 1;
62                 ctx.beginPath();
63                 ctx.moveTo(0, y);
64                 ctx.lineTo(innerWidth, y);
65                 ctx.stroke();
66         }
67
68         ctx.lineWidth = 2;
69
70         ctx.beginPath();
71         let moved = false;
72
73         for (let x = 0; x < innerWidth; x++) {
74                 let y = Math.max(Math.min(center[1] + func((x - center[0]) / scale) * scale, +1e+37), -1e+37);
75
76                 if (x == 0)
77                         ctx.moveTo(x, y);
78                 else
79                         ctx.lineTo(x, y);
80         }
81         ctx.stroke();
82
83         ctx.fillStyle = "blue";
84
85         for (let circle of positions) {
86                 const [x, y] = coordinateToScreen(circle[0], circle[1]);
87                 ctx.beginPath();
88                 ctx.arc(x, y, scale * 0.1, 0, Math.PI * 2);
89                 ctx.fill();
90         }
91 };
92
93 const addPosition = pos => {
94         positions.push(pos);
95         lagrange();
96         draw();
97 };
98
99 const calculateCenter = _ => {
100         center = [
101                 centerF[0] * innerWidth,
102                 centerF[1] * innerHeight,
103         ];
104
105         draw();
106 };
107
108 const resize = _ => {
109         canvas.width = innerWidth;
110         canvas.height = innerHeight;
111
112         calculateCenter();
113 };
114
115 const enableDrag = evt => {
116         dragPos = [evt.clientX / innerWidth, evt.clientY / innerHeight];
117 };
118
119 const disableDrag = _ => {
120         drag = false;
121         dragPos = null;
122         dragIndex = -1;
123         canvas.style.cursor = "auto";
124 };
125
126 const init = _ => {
127         lagrange();
128         resize();
129 };
130
131 canvas.addEventListener("mousedown", evt => {
132         if (evt.button != 0)
133                 return;
134
135         enableDrag(evt);
136
137         for (let i = 0; i < positions.length; i++) {
138                 const [x, y] = coordinateToScreen(positions[i][0], positions[i][1]);
139
140                 if (Math.sqrt(Math.pow(evt.clientX - x, 2) + Math.pow(evt.clientY - y, 2)) < scale * 0.1) {
141                         dragIndex = i;
142                         break;
143                 }
144         }
145 });
146
147 canvas.addEventListener("mousemove", evt => {
148         if (evt.button != 0)
149                 return;
150
151         const oldDragPos = dragPos;
152
153         if (oldDragPos) {
154                 drag = true;
155
156                 enableDrag(evt);
157
158                 if (dragIndex == -1) {
159                         canvas.style.cursor = "move";
160
161                         centerF = [
162                                 centerF[0] + dragPos[0] - oldDragPos[0],
163                                 centerF[1] + dragPos[1] - oldDragPos[1],
164                         ];
165
166                         calculateCenter();
167                 } else {
168                         canvas.style.cursor = "grabbing";
169
170                         positions[dragIndex] = screenToCoordinate(dragPos[0] * innerWidth, dragPos[1] * innerHeight);
171
172                         lagrange();
173                         draw();
174                 }
175         }
176 });
177
178 canvas.addEventListener("mouseup", evt => {
179         if (evt.button != 0)
180                 return;
181
182         if (! drag)
183                 addPosition(screenToCoordinate(evt.clientX, evt.clientY));
184
185         disableDrag();
186 });
187
188 canvas.addEventListener("mouseleave", evt => {
189         if (evt.button != 0)
190                 return;
191
192         disableDrag();
193 });
194
195 canvas.addEventListener("wheel", evt => {
196         scale -= evt.deltaY * 0.05;
197         scale = Math.max(7, scale);
198
199         draw();
200 });
201
202 addEventListener("resize", _ => {
203         resize();
204 });
205
206 init();