-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
152 lines (125 loc) · 7.84 KB
/
index.html
File metadata and controls
152 lines (125 loc) · 7.84 KB
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
<!DOCTYPE html>
<html>
<head>
<title>Andres Circle Algorithm</title>
</head>
<body style="font-family: sans-serif">
<canvas id="canvas" width="200" height="200" style="border: solid 1px grey"></canvas>
<form>
<div><label><input id="center_x" type="range" min="0" max="200" value="100" oninput="this.nextElementSibling.value = this.value; update_arc();"><input type="number" min="0" max="200" value="100" oninput="this.previousElementSibling.value = this.value; update_arc();"> : center_x</label></div>
<div><label><input id="center_y" type="range" min="0" max="200" value="100" oninput="this.nextElementSibling.value = this.value; update_arc();"><input type="number" min="0" max="200" value="100" oninput="this.previousElementSibling.value = this.value; update_arc();"> : center_y</label></div>
<div><label><input id="inner_radius" type="range" min="0" max="150" value="18" oninput="this.nextElementSibling.value = this.value; update_arc();"><input type="number" min="0" max="150" value="18" oninput="this.previousElementSibling.value = this.value; update_arc();"> : inner_radius</label></div>
<div><label><input id="outer_radius" type="range" min="0" max="150" value="20" oninput="this.nextElementSibling.value = this.value; update_arc();"><input type="number" min="0" max="150" value="20" oninput="this.previousElementSibling.value = this.value; update_arc();"> : outer_radius</label></div>
<div><label><input id="angle_start" type="range" min="0" max="255" value="0" oninput="this.nextElementSibling.value = this.value; update_arc();"><input id="angle_start_number" type="number" min="0" max="255" value="0" oninput="this.previousElementSibling.value = this.value; update_arc();"> : angle_start</label></div>
<div><label><input id="angle_end" type="range" min="0" max="255" value="220" oninput="this.nextElementSibling.value = this.value; update_arc();"><input id="angle_end_number" type="number" min="0" max="255" value="220" oninput="this.previousElementSibling.value = this.value; update_arc();"> : angle_end</label></div>
<div style="margin-top: 10px"><label><input id="animate" type="checkbox" onclick="toggle_animation()"> Loader animation</label></div>
<div style="margin-top: 10px"><label><input id="fast_approx" type="checkbox" onchange="update_arc()" checked> Fast arctan approximation (no float, no trigonometric)</label></div>
</form>
<script>
const M_PI_2 = 1.57079632679489661923;
const M_PI_4 = 0.78539816339744830962;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function drawArc(center_x, center_y, inner_radius, outer_radius, angle_start, angle_end, atan2f_func) {
// Declare variables
let x, y, d, r, ratio;
// Manage angle inputs
const inverted = (angle_start > angle_end);
const full = (angle_start == angle_end);
const a_start = inverted ? angle_end : angle_start;
const a_end = inverted ? angle_start : angle_end;
// Trace each arc radius with the Andres circle algorithm
for(r = inner_radius; r <= outer_radius; r++) {
x = 0;
y = r;
d = r - 1;
// Process each pixel of a 1/8th circle of radius r
while (y >= x) {
// Get the percentage of 1/8th circle drawn with arctan(y/x)
if(!atan2f_func) { // if no arctan function is provided, process a fast approximation of arctan
let val = Math.floor(x * 255 / y); // 0 - 255
ratio = Math.floor(val * (770195 - (val - 255) * (val + 941)) / 6137491); // 0 - 32
}
else ratio = Math.floor((M_PI_2 - atan2f_func(y, x)) * 32 / M_PI_4); // 0 - 32
// Fill the pixels of the 8 sections of the circle, but only on the arc defined by the angles (start and end)
if(full || ((ratio >= a_start && ratio < a_end) ^ inverted)) ctx.fillRect(center_x + y, center_y - x, 1, 1);
if(full || ((ratio > (63 - a_end) && ratio <= (63 - a_start)) ^ inverted)) ctx.fillRect(center_x + x, center_y - y, 1, 1);
if(full || ((ratio >= (a_start - 64) && ratio < (a_end - 64)) ^ inverted)) ctx.fillRect(center_x - x, center_y - y, 1, 1);
if(full || ((ratio > (127 - a_end) && ratio <= (127 - a_start)) ^ inverted)) ctx.fillRect(center_x - y, center_y - x, 1, 1);
if(full || ((ratio >= (a_start - 128) && ratio < (a_end - 128)) ^ inverted)) ctx.fillRect(center_x - y, center_y + x, 1, 1);
if(full || ((ratio > (191 - a_end) && ratio <= (191 - a_start)) ^ inverted)) ctx.fillRect(center_x - x, center_y + y, 1, 1);
if(full || ((ratio >= (a_start - 192) && ratio < (a_end - 192)) ^ inverted)) ctx.fillRect(center_x + x, center_y + y, 1, 1);
if(full || ((ratio > (255 - a_end) && ratio <= (255 - a_start)) ^ inverted)) ctx.fillRect(center_x + y, center_y + x, 1, 1);
// Run Andres circle algorithm to get to the next pixel
if (d >= 2 * x) {
d = d - 2 * x - 1;
x = x + 1;
} else if (d < 2 * (r - y)) {
d = d + 2 * y - 1;
y = y - 1;
} else {
d = d + 2 * (y - x - 1);
y = y - 1;
x = x + 1;
}
}
}
}
function update_arc() {
const center_x = parseInt(document.forms[0].center_x.value);
const center_y = parseInt(document.forms[0].center_y.value);
const inner_radius = parseInt(document.forms[0].inner_radius.value);
const outer_radius = parseInt(document.forms[0].outer_radius.value);
const angle_start = parseInt(document.forms[0].angle_start.value);
const angle_end = parseInt(document.forms[0].angle_end.value);
const fast_approx = document.forms[0].fast_approx.checked;
// Clear canvas and set color for the arc
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
// Draw arc
drawArc(center_x, center_y, inner_radius, outer_radius, angle_start, angle_end, fast_approx ? null : Math.atan2);
}
// Initialize arc
update_arc();
function toggle_animation() {
if(document.forms[0].animate.checked) {
// Initialize variables
this.small_increment = 3;
this.big_increment = 6;
this.grow = false;
this.prev_arc_length = -1;
// Start animation
this.anim_itv = setInterval(() => {
// Get start and end angles from form
let a_start = parseInt(document.forms[0].angle_start.value);
let a_end = parseInt(document.forms[0].angle_end.value);
// Decrement angles
a_start = (256 + a_start - (this.grow ? this.big_increment : this.small_increment)) % 256;
a_end = (256 + a_end - (this.grow ? this.small_increment : this.big_increment)) % 256;
// Get arc length
let arc_length = (256 + a_end - a_start) % 256;
if(this.prev_arc_length < 0) this.prev_arc_length = arc_length; // initialize this.prev_arc_length
// Check if arc overflows (grown above 255 or shrunk below 0)
if((this.grow && arc_length < this.prev_arc_length) || (!this.grow && arc_length > this.prev_arc_length)) {
// invert start and end angles, and invert animation
let tmp = a_start;
a_start = a_end;
a_end = tmp;
this.grow = !this.grow;
arc_length = this.grow ? 0 : 255;
}
// Update arc length for next iteration
this.prev_arc_length = arc_length;
// Update form and draw arc
document.forms[0].angle_start.value = document.forms[0].angle_start_number.value = a_start;
document.forms[0].angle_end.value = document.forms[0].angle_end_number.value = a_end;
// Draw arc (prevent drawing when angles are equal)
if(a_start != a_end) update_arc();
}, 15);
}
else clearInterval(this.anim_itv);
}
</script>
</body>
</html>