Фрактал — различия между версиями

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
Строка 27: Строка 27:
 
Также, нарисовав красивый фрактал, можно сохранить его в "jpg" формате на компьютер.
 
Также, нарисовав красивый фрактал, можно сохранить его в "jpg" формате на компьютер.
 
{{#widget:Iframe |url = http://tm.spbstu.ru/htmlets/js2020/Borisenkov/Fractals25_kopia05_11_2.html | width =1500 | height = 1300| border = 0}}
 
{{#widget:Iframe |url = http://tm.spbstu.ru/htmlets/js2020/Borisenkov/Fractals25_kopia05_11_2.html | width =1500 | height = 1300| border = 0}}
 +
==Другая версия программы==
 
<br><b>http://tm.spbstu.ru/Фрактал(2-ая_версия_программы)
 
<br><b>http://tm.spbstu.ru/Фрактал(2-ая_версия_программы)
 
==Код программы==
 
==Код программы==

Версия 23:19, 12 ноября 2020

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

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

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

Пользователю предлагается задать коэффициенты рекурсивной функции вычисления новых координат и координаты "x" и "y" начальной точки, количество итераций, которое произведет программа и способ вычисления новых точек. Начальная точка задается кликом левой кнопки мыши по холсту. Программа получает на вход "x"(0<x<1) и "y"(0<y<1) и строит N точек, координаты которых вычисляются по формулам:


1-ый способ:

2-ой способ:

где F - функция дробной части. Сразу можно сказать, что все точки, вычисленные по этим формулам, попадут в единичный квадрат (вследствие использования функции дробной части). Эти рекуррентные формулы эквивалентны системе дифференциальных уравнений с одним отличием: функция дробной части позволяет получить фрактал с паттерном, повторяющим интегральные кривые эквивалентной системы дифференциальных уравнений.

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


Пусть a11 = 1, a12 = 1, a21 = -0.9, a22 = 1. Если убрать функцию дробной части, точки будут заполнять эллипс:

Получив несколько начальных точек, программа построит такую картину при данных коэффициентах:

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

Задание коэффициентов

Коэффициенты a11,a12,a21,a22 можно задать с помощью полей ввода:

или же используя угол поворота:

Программа, получая на вход угол в радианах, сопоставляет коэффициенты a11,a12,a21,a22 коэффициентам матрицы поворота 2x2 на данный угол. Также, нарисовав красивый фрактал, можно сохранить его в "jpg" формате на компьютер.

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


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