Фрактал
Программа позволяет строить фрактальные и простые структуры при заданных начальных условиях.
Содержание
Начальные условия и принцип работы
В системе координат Oxy дан единичный квадрат, который отображается на экран в масштабе 1000:1
Пользователю предлагается задать коэффициенты рекурсивной функции вычисления новых координат и координаты "x" и "y" начальной точки, количество итераций, которое произведет программа и способ вычисления новых точек. Начальная точка задается кликом левой кнопки мыши по холсту.
Программа получает на вход "x"(0<x<1) и "y"(0<y<1) и строит N точек, координаты которых вычисляются по формулам:
\begin{equation}
\begin{cases}
x_{k} = F(a_{11}*x_{k-1} + a_{12}*y_{k-1})\\
y_{k} = F(a_{21}*x_{k} + a_{22}*y_{k-1})
\end{cases}
\end{equation}
где - функция дробной части.
Сразу можно сказать, что все точки, вычисленные по этим формулам, попадут в единичный квадрат (вследствие использования функции дробной части).
Эти рекуррентные формулы эквивалентны системе дифференциальных уравнений с одним отличием: функция дробной части позволяет получить фрактал с паттерном, повторяющим интегральные кривые эквивалентной системы дифференциальных уравнений.
Пример работы программы
Зададим коэффициенты:
\begin{equation} a_{11} = 1, a_{12} = 1, a_{21} = -0.9, a_{22} = 1.\end{equation} Если убрать функцию дробной части, точки будут заполнять эллипс:
Получив несколько начальных точек, программа построит такую картину при данных коэффициентах:
Другие примечательные наборы начальных условий можно посмотреть, выбрав номер эксперимента
Задание коэффициентов
Коэффициенты \begin{equation}a_{11},a_{12},a_{21},a_{22}\end{equation} можно задать с помощью полей ввода:
Два режима работы программы
В данной программе можно переключаться между двумя режимами:
1) Режим задания начальных данных (стоит по умолчанию). Кликом мыши по холсту добавляем начальную точку.
2) Режим "ZOOM'а". Включается нажатием на соответствующий "checkbox". Позволяет подробнее рассмотреть уже полученный рисунок.
Чтобы программа лучше прорисовала картину в режиме "ZOOM" необходимо задать большее количество точек в поле задания количества итераций, используя при этом кнопку "Обновить рисунок"
Другая версия программы
http://tm.spbstu.ru/Фрактал(2-ая_версия_программы)
Код программы
1 window.addEventListener('load',main,false);
2 function main () {
3 var x; var y;
4 var ctx = cnv.getContext('2d');
5 var h = cnv.height;
6 var w = cnv.width;
7 var scale = 1000;
8 var a_11; var a_12; var a_21; var a_22;
9 var numb1; var numb2; var decim;
10 var interv;
11 var clear;
12 var butt = document.getElementById('downloadimg');
13 var update = document.getElementById('refresh');
14 X = []; // массив начальных иксов
15 Y = []; // массив начальных игриков
16 var move = false;
17 var numberof;
18 var z = 1;
19 var RGB = [];
20 var z2 = 1;
21 var iterations = document.getElementById('number_it');
22 // начальные условия
23 a_11 = 1;
24 a_12 = 1;
25 a_21 = 1;
26 a_22 = 1;
27 var N = 200000;
28 var x_min = 0; var y_min = 0;
29 var x_max = 1; var y_max = 1;
30 var x_move; var y_move;
31 var x_oldmin = 0;
32 var x_oldmax = scale;
33 var y_oldmin = 0;
34 var y_oldmax = scale;
35 var intermid;
36 var imageData = ctx.createImageData(w,h);
37 var imageData2;
38 var upd = false;
39 x_minim.innerHTML = x_min.toFixed(2);
40 y_minim.innerHTML = y_min.toFixed(2);
41 x_maxim.innerHTML = x_max.toFixed(2);
42 y_maxim.innerHTML = y_max.toFixed(2);
43
44 // заполняем холст непрозрачными белыми пикселями
45 for (i = 0; i<1000; i++) {
46 for (j = 0; j<1000; j++) {
47 index = parseInt((i*w + j)*4);
48 imageData.data[index+0] = 255;
49 imageData.data[index+1] = 255;
50 imageData.data[index+2] = 255;
51 imageData.data[index+3] = 255;
52 }
53 }
54 ctx.putImageData(imageData,0,0);
55
56 iterations.onchange = function () {
57 N = document.getElementById('number_it').value;
58 }
59
60 butt.onclick = function () {
61 var dataURL = cnv.toDataURL("image/jpeg");
62 var link = document.createElement("a");
63 document.body.appendChild(link);
64 link.href = dataURL;
65 link.download = "my-image-name.jpg";
66 link.click();
67 document.body.removeChild(link);
68 }
69 update.onclick = function () { clearcanv(); upd = true; control(); }
70 a11.onchange = function() {
71 a_11 = parseFloat(document.getElementById('a11').value);
72 document.getElementById('num').value = 0;
73 angle = 0;
74 }
75 a12.onchange = function() {
76 a_12 = parseFloat(document.getElementById('a12').value);
77 document.getElementById('num').value = 0;
78 angle = 0;
79 }
80 a21.onchange = function() {
81 a_21 = parseFloat(document.getElementById('a21').value);
82 document.getElementById('num').value = 0;
83 angle = 0;
84 }
85 a22.onchange = function() {
86 a_22 = parseFloat(document.getElementById('a22').value);
87 document.getElementById('num').value = 0;
88 angle = 0;
89 }
90
91 cnv.onmousedown = function() {
92 var zoom = document.getElementById('zoom_check');
93 if (!zoom.checked) {
94 var rect = cnv.getBoundingClientRect();
95 console.log((event.clientX - rect.left)/scale,(event.clientY - rect.top)/scale);
96 if (z != 1) {
97 z2 = x_max - x_min;
98 x = x_min + (event.clientX - rect.left)*z2/scale;
99 y = y_min + (event.clientY - rect.top)*z2/scale;
100 } else {
101 x = (event.clientX - rect.left)/scale;
102 y = (event.clientY - rect.top)/scale;
103 }
104 clear = document.getElementsByName('clear_rect');
105 if (clear[1].checked == true) {
106 r = Math.floor(Math.random()*256);
107 g = Math.floor(Math.random()*256);
108 b = Math.floor(Math.random()*256);
109 } else {
110 clearcanv ();
111 some_span.innerHTML = "";
112 z = 1;
113 z2 = 1;
114 RGB = [];
115 X = [];
116 Y = [];
117 r = Math.floor(Math.random()*256);
118 g = Math.floor(Math.random()*256);
119 b = Math.floor(Math.random()*256);
120 ctx.beginPath();
121 ctx.arc(x*scale,y*scale,3,0,2*Math.PI);
122 ctx.strokeStyle = 'blue';
123 ctx.stroke();
124 }
125 X.push(x);
126 Y.push(y);
127 RGB.push(r);
128 RGB.push(g);
129 RGB.push(b);
130 control();
131 } else {
132 var rect = cnv.getBoundingClientRect();
133 z2 = x_max - x_min;
134 x_oldmin = x_min*scale;
135 y_oldmin = y_min*scale;
136 x_oldmax = x_max*scale;
137 y_oldmax = y_max*scale;
138 x_min = (event.clientX - rect.left)*z2 + x_oldmin;
139 y_min = (event.clientY - rect.top)*z2 + y_oldmin;
140 x_relativemin = event.clientX - rect.left;
141 y_relativemin = event.clientY - rect.top;
142 ctx.beginPath();
143 ctx.rect(event.clientX - rect.left,event.clientY - rect.top,1,1);
144 ctx.fillStyle = 'blue';
145 ctx.fill();
146 move = true;
147 imageData2 = ctx.getImageData(0,0,w,h);
148 }
149
150 }
151
152 cnv.onmousemove = function () {
153 if (move) {
154 ctx.putImageData(imageData2,0,0);
155 var rect = cnv.getBoundingClientRect();
156 y_move = (event.clientY - rect.top);
157 x_move = (y_move-y_relativemin) + x_relativemin;
158 ctx.beginPath();
159 ctx.rect(x_relativemin,y_relativemin,x_move-x_relativemin,y_move-y_relativemin);
160 ctx.stroke();
161 }
162 }
163
164 cnv.onmouseup = function () {
165 zoom = document.getElementById('zoom_check');
166 if (zoom.checked) {
167 x_relativemax = x_move;
168 y_relativemax = y_move;
169 y_max = y_move*z2 + y_oldmin;
170 x_max = x_move*z2 + x_oldmin;
171 x_min = x_min/scale;
172 y_min = y_min/scale;
173 x_max = x_max/scale;
174 y_max = y_max/scale;
175 if (x_min > x_max) {
176 intermid = x_min;
177 x_min = x_max;
178 x_max = intermid;
179 }
180 if (y_min > y_max) {
181 intermid = y_min;
182 y_min = y_max;
183 y_max = intermid;
184 }
185 z = z*scale/(x_relativemax - x_relativemin);
186 some_span.innerHTML = "Увеличение в "+z.toFixed(2)+" раз";
187 clearcanv ();
188 move = false;
189 control();
190 }
191 x_minim.innerHTML = x_min.toFixed(2);
192 y_minim.innerHTML = y_min.toFixed(2);
193 x_maxim.innerHTML = x_max.toFixed(2);
194 y_maxim.innerHTML = y_max.toFixed(2);
195 }
196
197 function Func (numb) {
198 decim = parseFloat(numb) - Math.floor(numb);
199 return(decim);
200 }
201
202 function coord() {
203 numb1 = a_11*x+a_12*y;
204 x = Func(numb1);
205 numb2 = a_21*x+a_22*y;
206 y = Func(numb2);
207 }
208
209 function draw() {
210 if ((x>=x_min)&&(x<=x_max)&&(y>=y_min)&&(y<=y_max)) {
211 index = (Math.floor((y-y_min)*z*scale)*w + Math.floor((x-x_min)*z*scale))*4;
212 imageData.data[index+0] = r;
213 imageData.data[index+1] = g;
214 imageData.data[index+2] = b;
215 }
216 }
217 // функция отчистки канваса
218 function clearcanv () {
219 for (i = 0; i<1000; i++) {
220 for (j = 0; j<1000; j++) {
221 index = parseInt((i*w + j)*4);
222 imageData.data[index+0] = 255;
223 imageData.data[index+1] = 255;
224 imageData.data[index+2] = 255;
225 }
226 }
227 ctx.putImageData(imageData,0,0);
228 }
229
230 function control () {
231 zoom = document.getElementById('zoom_check');
232 if ((zoom.checked)||(upd == true)) {
233 numberof = X.length;
234 for (m = 0; m<numberof; m++) {
235 x = X[m];
236 y = Y[m];
237 r = Number(RGB[m*3]);
238 g = Number(RGB[m*3+1]);
239 b = Number(RGB[m*3+2]);
240 for (j = 0;j<=N; j++){
241 coord();
242 draw();
243 }
244 upd = false;
245 }
246 } else {
247 for (j = 0;j<=N; j++){
248 coord();
249 draw();
250 }
251 }
252 ctx.putImageData(imageData,0,0);
253
254 }
255
256 function set_exp(N_exp) {
257 var k = Number(N_exp);
258 clearcanv ();
259 X = [];
260 Y = [];
261 RGB = [];
262 z = 1;
263 z2 = 1;
264 x_min = 0; y_min = 0; x_max = 1; y_max = 1; x_oldmin = 0; y_oldmin = 0; x_oldmax = scale; y_oldmax = scale;
265 if (N_exp == 1) {x = 0.31; y = 0.32; a_11 = 1; a_12 = 0.5; a_21 = -0.5; a_22 = 1; }
266 if (N_exp == 2) {x = 0.46; y = 0.63; a_11 = 1; a_12 = 1; a_21 = -0.9; a_22 = 1; }
267 if (N_exp == 3) {x = 1/4; y = 1/4; a_11 = 1; a_12 = 0.01; a_21 = 0.01; a_22 = -1; }
268 if (N_exp == 4) {x = 1/2; y = 1/2; a_11 = 1; a_12 = 0.32; a_21 = 0.32; a_22 = -1; }
269 if (N_exp == 5) {x = 1/2; y = 1/2; a_11 = 0.9; a_12 = 0.1; a_21 = -0.1; a_22 = 1.2; }
270 if (N_exp == 6) {x = 1/4; y = 1/4; a_11 = Math.cos(angle); a_12 = Math.sin(angle); a_21 = -a_12; a_22 = a_11; }
271 if (N_exp == 7) {x = 1/2; y = 1/2; a_11 = 1; a_12 = 1.5; a_21 = 0.01; a_22 = -1; }
272 if (N_exp == 8) {x = 1/2; y = 1/2; a_11 = 1; a_12 = 0.1296; a_21 = -0.1296; a_22 = 1; }
273 if (N_exp == 9) {x = 1/4; y = 1/4; a_11 = -0.05; a_12 = -0.95; a_21 = 1.1; a_22 = 0.65; }
274 if (N_exp) {
275
276 a11.value = a_11;
277 a12.value = a_12; X.push(x); Y.push(y);
278 a21.value = a_21;
279 a22.value = a_22;
280 }
281 r = Math.floor(Math.random()*256);
282 g = Math.floor(Math.random()*256);
283 b = Math.floor(Math.random()*256);
284 RGB.push(r);
285 RGB.push(g);
286 RGB.push(b);
287 }
288 num.onchange = function() { set_exp(document.getElementById('num').value); control();}
289 }
1 <html>
2 <head>
3 <meta charset="UTF-8">
4 <title> Fractals </title>
5 <script src = 'Fractals25_kopia17_11_20.js'>
6 </script>
7 </head>
8 <body>
9 <p align = 'left'><canvas id = 'cnv' width = 1000 height = 1000 style='border: 1px solid black;'></canvas></p>
10 <span id=some_span></span>
11 <br><b>Рассматриваемая область: x_min = <span id =x_minim></span> , y_min = <span id =y_minim></span>, x_max = <span id=x_maxim></span>, y_max = <span id=y_maxim></span>
12 <br><b>(*) Задайте коэффициенты:</b>
13                 <label><input type='text' id='a11' value='1' ><b>A_11</b></label>
14 <label><input type='text' id='a12' value='1' ><b>A_12</b></label>
15 <br><b>(*) Включить очистку холста после клика? </b>
16 <label><input type = 'radio' name = 'clear_rect' value = 'yes'><b>Да</b></label>
17 <label><input type = 'radio' name = 'clear_rect' value = 'no' checked><b>Нет</b></label>
18     <label><input type='text' id='a21' value='1' ><b>A_21</b></label>
19 <label><input type='text' id='a22' value='1' ><b>A_22</b></label>
20 </br>
21     <label><b>Режим <I>ZOOM'а</I><input type = 'checkbox' id = 'zoom_check'></b></label>
22     <label><input type='text' id='number_it' value ='200000'><b>   Количество итераций</b></label>
23 <br><b> Выберите номер эксперимента:</b>
24 <input type='number' size='1' id = 'num' min='1' max='9' value='0' step='1'>
25 <br><b> <input type='button' id='downloadimg' value='Сохранить рисунок'>
26 <input type = 'button' style="width:200px;height:40px" id = 'refresh' value = 'Обновить рисунок'></b></br>
27 </body>
28 </html>