Фрактал

Материал из Department of Theoretical and Applied Mechanics
Версия от 10:20, 25 января 2021; 81.200.120.44 (обсуждение) (Направление исследований)

Перейти к: навигация, поиск

Программа позволяет строить фрактальные и простые структуры при заданных начальных условиях.

Начальные условия и принцип работы

В системе координат Oxy дан единичный квадрат, который отображается на экран в масштабе 1000:1

Пользователю предлагается задать коэффициенты рекурсивной функции вычисления новых координат и координаты "x" и "y" начальной точки, количество итераций, которое произведет программа и способ вычисления новых точек. Начальная точка задается кликом левой кнопки мыши по холсту. Программа получает на вход "x"(0<x<1) и "y"(0<y<1) и строит N точек, координаты которых вычисляются по формулам:
\begin{equation} \begin{cases} x_{k+1} = F(a_{11}*x_{k} + a_{12}*y_{k})\\ y_{k+1} = F(a_{21}*x_{k+1} + a_{22}*y_{k}) \end{cases} \end{equation}
где [math]F[/math] - функция дробной части. Сразу можно сказать, что все точки, вычисленные по этим формулам, попадут в единичный квадрат (вследствие использования функции дробной части).

Направление исследований

Используя функцию [math]F[/math], программа строит регулярные и нерегулярные области:
1. Регулярной будем считать область, которая состоит из малого количества отдельных элементов (например: два больших эллипса).
2. Нерегулярной будем считать область, которая состоит из большого количества малых элементов (например: множество малых эллипсов в промежутках между регулярными областями).
Ниже изображен процесс вычисления точек и их отображения в случае нерегулярной и регулярной области при заданных коэффициентах (эксперимент 1): \begin{equation} a_{11} = 1, a_{12} = 1, a_{21} = -0.9, a_{22} = 1.\end{equation}
1. \begin{equation} x_{0} = 0.613, y_{0} = 0.582 \end{equation} Результат: регулярная область фрактала.

2. \begin{equation} x_{0} = 0.46, y_{0} = 0.63\end{equation} Результат: нерегулярная область фрактала.

Синим цветом обозначены точки, вычисленные по рекуррентным формулам без использования функции [math]F[/math].
Красным цветом обозначены точки, вычисленные по рекуррентным формулам с использованием функции [math]F[/math] (формулы даны выше). Программа вычисляет и отображает лишь эти точки.
Данные анимации призваны показать, что паттерн получаемого фрактала подобен кривой второго порядка, которую образуют точки, вычисленные по рекуррентным формулам без использования функции дробной части [math]F[/math].

Пример работы программы

Зададим такие же коэффициенты, как в примерах выше (эксперимент №1 в списке программы):
\begin{equation} a_{11} = 1, a_{12} = 1, a_{21} = -0.9, a_{22} = 1.\end{equation}
Получив несколько начальных точек, программа построит такую картину при данных коэффициентах (картина отзеркалена относительно оси Ox для одинаковой направленности осей):

Другие примечательные наборы начальных условий можно посмотреть, выбрав номер эксперимента.

Задание коэффициентов и начальных точек

Коэффициенты \begin{equation}a_{11},a_{12},a_{21},a_{22}\end{equation} задаются с помощью полей ввода:

Начальные точки задаются с помощью клика по холсту или с помощью полей ввода:

Два режима работы программы

Данная программа предусматривает два режима работы:
1) Режим задания начальных данных (стоит по умолчанию). Кликом мыши по холсту добавляем начальные данные.
2) Режим "ZOOM'а". Включается нажатием на соответствующий "checkbox". Позволяет подробнее рассмотреть уже полученный рисунок.
Чтобы программа лучше прорисовала картину в режиме "ZOOM", необходимо задать большее количество точек в поле задания количества итераций, используя при этом кнопку "Обновить рисунок"

Другая версия программы


http://tm.spbstu.ru/Фрактал(2-ая_версия_программы)

Код программы

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