Фрактал

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск

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

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

В системе координат 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}
где [math]F[/math] - функция дробной части. Сразу можно сказать, что все точки, вычисленные по этим формулам, попадут в единичный квадрат (вследствие использования функции дробной части). Эти рекуррентные формулы эквивалентны системе дифференциальных уравнений с одним отличием: функция дробной части позволяет получить фрактал с паттерном, повторяющим интегральные кривые эквивалентной системы дифференциальных уравнений.

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

Зададим коэффициенты:
\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 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 }
html - файл:
 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 &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&hairsp;<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 &emsp;&emsp;&hairsp;&hairsp;<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 &emsp;&emsp;&hairsp;&hairsp;<label><b>Режим <I>ZOOM'а</I><input type = 'checkbox' id = 'zoom_check'></b></label>
22 &emsp;&emsp;&hairsp;&hairsp;<label><input type='text' id='number_it' value ='200000'><b>&hairsp;&hairsp;&hairsp;Количество итераций</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>