-
Notifications
You must be signed in to change notification settings - Fork 0
/
rotating_square.html
239 lines (193 loc) · 6.77 KB
/
rotating_square.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Disegnare un Punto</title>
</head>
<body>
<canvas id="myCanvas" width="800" height="800"></canvas>
</body>
<script>
// getting canvas
const canvas = document.getElementById("myCanvas");
//getting rendering 2d context
const ctx = canvas.getContext("2d")
function setCanvasSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
setCanvasSize();
window.addEventListener("resize", setCanvasSize);
// defining constants
const L = 100 // square side lenght
const CENTER = [window.innerWidth / 2, window.innerHeight / 2] // square center
const [x0, y0] = CENTER // square center
const A = [x0 - L, y0 - L] // square vertices A
const B = [x0 + L, y0 - L] // square vertices B
const C = [x0 - L, y0 + L] // square vertices C
const D = [x0 + L, y0 + L] // square vertices D
// creating vertices array
const VERTICES = []
VERTICES.push(A, B, C, D)
// given a figure( list of points) draw it
const drawPoints = async function(figure, fill = 2) {
let i = 0
for (const point of figure) {
const [x, y] = point
ctx.fillRect(x, y, fill, fill)
// console.log(`x:${x} - y:${y}`)
}
}
/**
* get line between 2 points
*
* using the line properties
* 1) y-y0 = m(x-x0) => m = (x-x0)/y-y0
*
* to get q ( y = mx +q)
* just resolve the 1 eq considering 1 x0 and y0 known
*
* y-y0 = m*(x-x0)
* y = m*x + (y0 - m*x0)
* => q = y0-m*x0
*/
const getTwoPointsLine = async function(a, b) {
let [x1, y1] = a
let [x2, y2] = b
if (x1 > x2) {
[x1, x2] = [x2, x1];
[y1, y2] = [y2, y1];
}
// calculating m having a and b
let m = (y2 - y1) / (x2 - x1)
// calculating q having m adn a or b
const q = (y1 - m * x1)
// set are faster than array and also we don't need duplicate values and we don't care about order
const line = new Set()
/**
* let step = 1 / Math.abs(m) (below)
*
* As m increases, the generated segment will be longer, so the larger m grows,
* the smaller the step with which I generate the x must be, otherwise we would have
* empty spaces between one point and another on the line. ques
*
* To understand why using 1/|m| as a step during the creation of the line is important,
* let's consider the following situation:
*
* Imagine having a line with a very high slope, for example m = 10, and two points A and B:
* - A(0, 0)
* - B(1, 10)
*
* If we use a fixed step of 1 for x, we will get the following points on the line:
* - x = 0, y = 0
* - x = 1, y = 10
*
* However, between these two points, we will have a series of x positions that do not
* correspond to integer values. For example, if we consider x = 0.1, we get:
* - x = 0.1, y = 1
* And if we consider x = 0.2, we get:
* - x = 0.2, y = 2
*
* Continuing this way, we see that many integer values of y are missed because we are
* advancing with steps of 1 on x, and the line has a very steep slope.
*
* If, on the other hand, we use a step of 1/|m| (in our example, 1/10), we get a higher
* density of points because we are considering smaller steps on x to capture finer details
* along the line. This helps to reduce the loss of points when the slope is high, improving
* the representation of the line on a discrete grid.
*
* However, suing 1/|m| can lead to very large step when m is very samll, this can cause the loss of point as wellwe so we set a maximum
* limit of 1 for the step. This ensures that even for shallow slopes, the step remains within a
* reasonable range to maintain a visually coherent representation of the line.
*/
let step = 1 / Math.abs(m)
// setting maximum value for the step to avoid too large steps
if (step > 1) {
step = 1;
}
// when the step is too small whe tend to have infinite values => we need an inferior limit
if (step < 0.1) {
step = 0.1
}
// m = infinite is a limit case, we must consider this as a separate case
if (!isFinite(m)) {
if (y1 > y2)[y1, y2] = [y2, y1]
for (y = y1; y < y2; y++) {
line.add([x1, y])
}
} else {
// generating point for every m != infinite
for (let x = x1; x <= x2; x += step) {
let y = m * x + q
y = Math.round(y)
line.add([Math.round(x), y])
}
}
return line;
}
/**
* given a point, it calculate the rotaion matrix and gives back the rotated point
* by theta radiants
*
* figure is a list of points[[x,y],[x1,y1]....]
* keepCenter = true mantains the original center of the figure, if keepCenter = false
* the figure is traslated to the origin
*/
const rotationMatrix2D = async function(figure, center, theta, keepCenter = true) {
const figure1 = []
for (point of figure) {
let [x, y] = point
const [xc, yc] = center
// getting point coordinates around the origin
if (keepCenter)[x, y] = [x - xc, y - yc]
// calculating nthe new point
x1 = x * Math.cos(theta) - y * Math.sin(theta)
y1 = x * Math.sin(theta) + y * Math.cos(theta)
// traslating point around the original center
if (keepCenter)[x1, y1] = [x1 + xc, y1 + yc]
figure1.push([x1, y1])
}
return figure1
}
const drawSquare = async function(vertices, fill = 2) {
const a = vertices[0]
const b = vertices[1]
const c = vertices[2]
const d = vertices[3]
// pushing all the lines calculation inside the array in order to execute it asyncronously w
const linesPromises = [
getTwoPointsLine(a, b),
getTwoPointsLine(a, c),
getTwoPointsLine(b, d),
getTwoPointsLine(c, d)
];
// waiting for calculation to be done
const lines = await Promise.all(linesPromises);
// pushing all the lines calculation inside the array in order to execute it asyncronously w
const drawsPromises = []
for (line of lines) {
drawsPromises.push(drawPoints(line, fill))
}
// waiting for calculation to be done
Promise.all(drawsPromises)
}
// degrees to radiants conversion factor
const oneDegree = (Math.PI / 180)
let frame = 0
let rotatedVertices = VERTICES
const animate = async function() {
const id = setInterval(async function() {
if (frame > 359) {
frame = 0
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
await drawSquare(rotatedVertices, 5);
rotatedVertices = []
rotatedVertices = await rotationMatrix2D(VERTICES, CENTER, frame * oneDegree)
frame++
}, 1000 / 60)
}
animate()
</script>
</html>