Поперечные волны в струне — различия между версиями
Строка 26: | Строка 26: | ||
[[Медиа:OscSpring.rar|Скачать архив]] | [[Медиа:OscSpring.rar|Скачать архив]] | ||
+ | <br /> | ||
+ | <br /> | ||
+ | <div class="mw-collapsible mw-collapsed" style="width:100%" > | ||
+ | '''Текст программы на языке JavaScript (разработчик [[Александров Сергей]]):''' <div class="mw-collapsible-content"> | ||
+ | Файл '''"Test.js"''' | ||
+ | function Main_Spring(canvas, slider_g, text_g, slider_nu, text_nu, slider_A, text_A, sinbutton) { | ||
+ | var canvas, ctx, w, h, world, lineBody, lineBody1, mouseConstraint, s, s1; // переменные: канвас, содержимое канваса, ширина и высота канваса, создание "мира" p2, парвая и левая "стена", закрепление мыши, пружины для связи со стенами | ||
+ | var scaleX = 50, scaleY = -50; //масштаб | ||
+ | var circleBody2 = new Array(); //массив шариков | ||
+ | var circleShape2 = new Array(); //массив форм шариков | ||
+ | var ss = new Array();//массив пружин | ||
+ | var num = 100; //общее кол-во частиц | ||
+ | var k = 250; // жесткость пружин | ||
+ | var B = 4; // вязкость | ||
+ | var R = 0.05; //радиус шаров | ||
+ | var dx = 0.01; // начальная длина пружин | ||
+ | var g = 0; // сила тяжести | ||
+ | var dtt = 0; // время для колебаний по sin | ||
+ | var LA = 2; // смещение по вертикали для sin | ||
+ | var dt = 0.03; // "скорость" для sin | ||
+ | var sw = 0; // переключатель для кнопок | ||
+ | |||
+ | // Инициализируем канвал и действия маши | ||
+ | canvas = document.getElementById("myCanvas"); | ||
+ | w = canvas.width; | ||
+ | h = canvas.height; | ||
+ | ctx = canvas.getContext("2d"); | ||
+ | ctx.lineWidth = 0.05; | ||
+ | canvas.addEventListener("mousedown", doMouseDown, false); | ||
+ | canvas.addEventListener("mousemove", doMouseMove, false); | ||
+ | canvas.addEventListener("mouseup", doMouseUp, false); | ||
+ | |||
+ | // Ползунки и текстовые поля | ||
+ | this.setSlider_g = function(new_g){g = new_g; world.gravity[1] = -g;}; | ||
+ | this.setSlider_nu = function(new_nu){dt = new_nu/10;}; | ||
+ | this.setSlider_A = function(new_A){LA = new_A*4;}; | ||
+ | this.setSlider_B = function(new_B){B = new_B*12; s.damping = B; s1.damping = B; | ||
+ | for (var i = 0; i < num-1; i++){ss[i].damping = B;}; | ||
+ | for (var j = 0; j < num; j++){circleBody2[j].damping=B/24;};}; | ||
+ | |||
+ | slider_g.min = 0; slider_g.max = 1; | ||
+ | slider_g.step = 0.10; | ||
+ | slider_g.value = g; | ||
+ | text_g.value = g.toFixed(1); | ||
+ | |||
+ | slider_A.min = 0; slider_A.max = 1; | ||
+ | slider_A.step = 0.10; | ||
+ | slider_A.value = LA/4; | ||
+ | text_A.value = (LA/4).toFixed(1); | ||
+ | |||
+ | |||
+ | slider_B.min = 0; slider_B.max = 1; | ||
+ | slider_B.step = 0.10; | ||
+ | slider_B.value = B/12; | ||
+ | text_B.value = (B/12).toFixed(1); | ||
+ | |||
+ | slider_nu.min = 0; slider_nu.max = 1; | ||
+ | slider_nu.step = 0.10; | ||
+ | slider_nu.value = dt*10; // начальное значение ползунка должно задаваться после min и max | ||
+ | text_nu.value = (dt*10).toFixed(1); | ||
+ | |||
+ | init(); | ||
+ | animate(); | ||
+ | |||
+ | // Функция "переключения" кнопок | ||
+ | this.sw = function(a) | ||
+ | { | ||
+ | if (a==-1) sw = a; | ||
+ | else | ||
+ | sw += a; | ||
+ | } | ||
+ | |||
+ | // Функция движения левой границы по Sin | ||
+ | function FSin() | ||
+ | { | ||
+ | sinbutton.value = "Стоп"; | ||
+ | sw = 1; | ||
+ | dtt+=dt; | ||
+ | s.localAnchorB[0] = 4-LA * (Math.sin(dtt)/2); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | function Stop() | ||
+ | { | ||
+ | sw = 0; | ||
+ | sinbutton.value = "Sin"; | ||
+ | } | ||
+ | |||
+ | // Функция сброса | ||
+ | function Reset() | ||
+ | { | ||
+ | if (sinbutton.value != "Sin") sinbutton.value = "Sin"; | ||
+ | dtt = 0; | ||
+ | sw = 0; | ||
+ | world.clear(); | ||
+ | init(); | ||
+ | |||
+ | } | ||
+ | |||
+ | // Функция создания всех объектов | ||
+ | function init(){ | ||
+ | |||
+ | // Создание мира p2 | ||
+ | world = new p2.World(); | ||
+ | world.gravity[1] = -g; | ||
+ | world.solver.iterations = 10; | ||
+ | world.solver.frictionIterations = 0; | ||
+ | |||
+ | // Создаем левую границу | ||
+ | lineShape = new p2.Line({ length: 6 }); | ||
+ | lineBody = new p2.Body({ | ||
+ | position: [-6,-4], | ||
+ | angle: Math.PI/2 | ||
+ | }); | ||
+ | lineBody.addShape(lineShape, [0,0], Math.PI ); | ||
+ | world.addBody(lineBody); | ||
+ | |||
+ | // Создаем правую границу | ||
+ | lineShape1 = new p2.Line({ length: 6 }); | ||
+ | lineBody1 = new p2.Body({ | ||
+ | position: [6,-4], | ||
+ | angle: Math.PI/2 | ||
+ | }); | ||
+ | lineBody1.addShape(lineShape1, [0,0], Math.PI ); | ||
+ | world.addBody(lineBody1); | ||
+ | |||
+ | // Создаем все шарики | ||
+ | for (var i = 0; i < num; i++) | ||
+ | { | ||
+ | circleShape2[i] = new p2.Circle({ radius: R }); | ||
+ | circleBody2[i] = new p2.Body({ mass:0.5, position:[(-6 + (12*(i+1)/(num+1))) ,0] , angularDamping:0, damping:B/24}); | ||
+ | circleBody2[i].addShape(circleShape2[i]); | ||
+ | world.addBody(circleBody2[i]); | ||
+ | }; | ||
+ | |||
+ | //Создаем пружину для связи с левой границей | ||
+ | s = new p2.LinearSpring(circleBody2[0], lineBody, { | ||
+ | restLength : dx, | ||
+ | stiffness : k, | ||
+ | damping : B, | ||
+ | localAnchorB : [4,0], | ||
+ | }); | ||
+ | world.addSpring(s); | ||
+ | |||
+ | // Создаем пружину для связи с правой границей | ||
+ | s1 = new p2.LinearSpring(circleBody2[num-1], lineBody1, { | ||
+ | restLength : dx, | ||
+ | stiffness : k, | ||
+ | damping : B, | ||
+ | localAnchorB : [4,0], | ||
+ | }); | ||
+ | world.addSpring(s1); | ||
+ | |||
+ | // Создаем пружины для связи шариков вместе | ||
+ | for (var i = 0; i < num-1; i++) | ||
+ | { | ||
+ | ss[i] = new p2.LinearSpring(circleBody2[i], circleBody2[i+1], { | ||
+ | restLength : dx, | ||
+ | stiffness : k, | ||
+ | damping : B, | ||
+ | localAnchorA : [0,0], | ||
+ | localAnchorB : [0,0], | ||
+ | }); | ||
+ | world.addSpring(ss[i]); | ||
+ | }; | ||
+ | |||
+ | //Отключаем взаимодействие между пружинами и между шариками | ||
+ | for (var i = 0; i < num-1; i++) | ||
+ | { | ||
+ | for (var j = i; j < num-1; j++) | ||
+ | { | ||
+ | world.disableBodyCollision(ss[i], ss[j]); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | for (var i = 0; i < num; i++) | ||
+ | { | ||
+ | for (var j = i; j < num; j++) | ||
+ | { | ||
+ | world.disableBodyCollision(circleBody2[i], circleBody2[j]); | ||
+ | world.disableBodyCollision(circleBody2[i], lineBody1); | ||
+ | world.disableBodyCollision(circleBody2[i], lineBody); | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | // Создаем объект для мыши | ||
+ | mouseBody = new p2.Body(); | ||
+ | world.addBody(mouseBody); | ||
+ | |||
+ | } | ||
+ | |||
+ | // Функция нажатия мыши вниз | ||
+ | function doMouseDown(event){ | ||
+ | var position = getPhysicsCoord(event); | ||
+ | circleBody2.forEach(function(item, i, circleBody2) | ||
+ | { | ||
+ | var hitBodies = world.hitTest(position, [item]); | ||
+ | if(hitBodies.length){ | ||
+ | mouseBody.position[0] = position[0]; | ||
+ | mouseBody.position[1] = position[1]; | ||
+ | mouseConstraint = new p2.LockConstraint(mouseBody, item); | ||
+ | world.addConstraint(mouseConstraint); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | // Функция перемещения мыши | ||
+ | function doMouseMove(event){ | ||
+ | var position = getPhysicsCoord(event); | ||
+ | mouseBody.position[0] = position[0]; | ||
+ | mouseBody.position[1] = position[1]; | ||
+ | } | ||
+ | |||
+ | // Функция отпуская клавиши мыши | ||
+ | function doMouseUp(event){ | ||
+ | world.removeConstraint(mouseConstraint); | ||
+ | } | ||
+ | |||
+ | // Перевод координат мыши в текущие координаты | ||
+ | function getPhysicsCoord(mouseEvent){ | ||
+ | var rect = canvas.getBoundingClientRect(); | ||
+ | var x = mouseEvent.clientX - rect.left; | ||
+ | var y = mouseEvent.clientY - rect.top; | ||
+ | x = (x - w / 2) / scaleX; | ||
+ | y = (y - h / 2) / scaleY; | ||
+ | |||
+ | return [x, y]; | ||
+ | } | ||
+ | |||
+ | // Функция рисования всех шаров | ||
+ | function drawAllCircle(){ | ||
+ | for (var i = 0; i < num; i++) | ||
+ | { | ||
+ | ctx.beginPath(); | ||
+ | var x1 = circleBody2[i].position[0], | ||
+ | y1 = circleBody2[i].position[1], | ||
+ | radius1 = circleShape2[i].radius; | ||
+ | ctx.arc(x1,y1,radius1,0,2*Math.PI); | ||
+ | ctx.fill(); | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | ctx.strokeStyle = "black"; | ||
+ | } | ||
+ | |||
+ | // Рисуем пол | ||
+ | function drawPlane(){ | ||
+ | ctx.moveTo(-w, -4); | ||
+ | ctx.lineTo( w, -4); | ||
+ | ctx.strokeStyle = "black"; | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | |||
+ | // Рисуем правую стену | ||
+ | function drawRL(){ | ||
+ | ctx.beginPath(); | ||
+ | var x = lineBody.position[0], | ||
+ | y = lineBody.position[1]; | ||
+ | ctx.moveTo(x, -y); | ||
+ | ctx.lineTo(x, y); | ||
+ | ctx.strokeStyle = "black"; | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | |||
+ | |||
+ | // Рисуем левую стену | ||
+ | function drawLL(){ | ||
+ | ctx.beginPath(); | ||
+ | var x = lineBody1.position[0], | ||
+ | y = lineBody1.position[1]; | ||
+ | ctx.moveTo(x, -y); | ||
+ | ctx.lineTo(x, y); | ||
+ | ctx.strokeStyle = "black"; | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | |||
+ | // Рисуем все пружины | ||
+ | function drawAllSpring(){ | ||
+ | for (var i = 0; i < num-1; i++) | ||
+ | { | ||
+ | ctx.beginPath(); | ||
+ | var x = circleBody2[i].position[0], | ||
+ | y = circleBody2[i].position[1]; | ||
+ | var x1 = circleBody2[i+1].position[0], | ||
+ | y1 = circleBody2[i+1].position[1]; | ||
+ | ctx.moveTo(x, y); | ||
+ | ctx.lineTo(x1, y1); | ||
+ | ctx.strokeStyle = "black"; | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Рисуем пружину для связи первого шарика со стеной | ||
+ | function draw1S(){ | ||
+ | ctx.beginPath(); | ||
+ | var x = circleBody2[0].position[0], | ||
+ | y = circleBody2[0].position[1]; | ||
+ | ctx.moveTo(-6, (s.localAnchorB[0]-4)); | ||
+ | ctx.lineTo(x, y); | ||
+ | ctx.strokeStyle = "black"; | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | |||
+ | // Рисуем пружину для связи последнего шарика со стеной | ||
+ | function draw2S(){ | ||
+ | ctx.beginPath(); | ||
+ | var x = circleBody2[num-1].position[0], | ||
+ | y = circleBody2[num-1].position[1]; | ||
+ | ctx.moveTo(6, 0); | ||
+ | ctx.lineTo(x, y); | ||
+ | ctx.strokeStyle = "black"; | ||
+ | ctx.stroke(); | ||
+ | } | ||
+ | |||
+ | function render(){ | ||
+ | ctx.clearRect(0,0,w,h); | ||
+ | ctx.save(); | ||
+ | ctx.translate(w/2, h/2); // Переводим начало в центр | ||
+ | ctx.scale(50, -50); //Зумируем канвас | ||
+ | // Рисуем все тела на канвасе | ||
+ | drawAllCircle(); | ||
+ | drawRL(); | ||
+ | drawLL(); | ||
+ | drawPlane(); | ||
+ | drawAllSpring(); | ||
+ | draw1S(); | ||
+ | draw2S(); | ||
+ | ctx.restore(); | ||
+ | } | ||
+ | |||
+ | function animate(){ | ||
+ | requestAnimationFrame(animate); | ||
+ | if (sw == 1) FSin(); | ||
+ | if (sw == 2) Stop(); | ||
+ | if (sw == -1) Reset(); | ||
+ | world.step(1/60); | ||
+ | render(); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | </div> | ||
+ | </div> |
Версия 13:29, 22 декабря 2015
Виртуальная лаборатория > Динамика взаимодействия частиц произвольной формыКурсовой проект по механике дискретных сред
- разработчик Богданов Дмитрий
- руководитель Кузькин Виталий
Цель проекта
Разработка программы моделирующая колебания струны
Решение
r = r(x,y)
m - масса одной частицы
k - жесткость пружины
B - вязкость
Обе конца пружины закреплены
Результаты
Файл "Test.js" function Main_Spring(canvas, slider_g, text_g, slider_nu, text_nu, slider_A, text_A, sinbutton) { var canvas, ctx, w, h, world, lineBody, lineBody1, mouseConstraint, s, s1; // переменные: канвас, содержимое канваса, ширина и высота канваса, создание "мира" p2, парвая и левая "стена", закрепление мыши, пружины для связи со стенами var scaleX = 50, scaleY = -50; //масштаб var circleBody2 = new Array(); //массив шариков var circleShape2 = new Array(); //массив форм шариков var ss = new Array();//массив пружин var num = 100; //общее кол-во частиц var k = 250; // жесткость пружин var B = 4; // вязкость var R = 0.05; //радиус шаров var dx = 0.01; // начальная длина пружин var g = 0; // сила тяжести var dtt = 0; // время для колебаний по sin var LA = 2; // смещение по вертикали для sin var dt = 0.03; // "скорость" для sin var sw = 0; // переключатель для кнопок
// Инициализируем канвал и действия маши canvas = document.getElementById("myCanvas");
w = canvas.width; h = canvas.height; ctx = canvas.getContext("2d"); ctx.lineWidth = 0.05;
canvas.addEventListener("mousedown", doMouseDown, false); canvas.addEventListener("mousemove", doMouseMove, false); canvas.addEventListener("mouseup", doMouseUp, false);
// Ползунки и текстовые поля this.setSlider_g = function(new_g){g = new_g; world.gravity[1] = -g;}; this.setSlider_nu = function(new_nu){dt = new_nu/10;}; this.setSlider_A = function(new_A){LA = new_A*4;}; this.setSlider_B = function(new_B){B = new_B*12; s.damping = B; s1.damping = B; for (var i = 0; i < num-1; i++){ss[i].damping = B;}; for (var j = 0; j < num; j++){circleBody2[j].damping=B/24;};};
slider_g.min = 0; slider_g.max = 1;
slider_g.step = 0.10; slider_g.value = g; text_g.value = g.toFixed(1);
slider_A.min = 0; slider_A.max = 1;
slider_A.step = 0.10; slider_A.value = LA/4; text_A.value = (LA/4).toFixed(1);
slider_B.min = 0; slider_B.max = 1;
slider_B.step = 0.10; slider_B.value = B/12; text_B.value = (B/12).toFixed(1);
slider_nu.min = 0; slider_nu.max = 1;
slider_nu.step = 0.10; slider_nu.value = dt*10; // начальное значение ползунка должно задаваться после min и max text_nu.value = (dt*10).toFixed(1);
init(); animate();
// Функция "переключения" кнопок this.sw = function(a)
{
if (a==-1) sw = a; else sw += a; }
// Функция движения левой границы по Sin function FSin() { sinbutton.value = "Стоп"; sw = 1; dtt+=dt; s.localAnchorB[0] = 4-LA * (Math.sin(dtt)/2); return 0; }
function Stop() { sw = 0; sinbutton.value = "Sin"; }
// Функция сброса function Reset() { if (sinbutton.value != "Sin") sinbutton.value = "Sin"; dtt = 0; sw = 0; world.clear(); init();
}
// Функция создания всех объектов
function init(){
// Создание мира p2
world = new p2.World();
world.gravity[1] = -g; world.solver.iterations = 10; world.solver.frictionIterations = 0;
// Создаем левую границу lineShape = new p2.Line({ length: 6 });
lineBody = new p2.Body({ position: [-6,-4], angle: Math.PI/2 }); lineBody.addShape(lineShape, [0,0], Math.PI ); world.addBody(lineBody);
// Создаем правую границу lineShape1 = new p2.Line({ length: 6 });
lineBody1 = new p2.Body({ position: [6,-4], angle: Math.PI/2 }); lineBody1.addShape(lineShape1, [0,0], Math.PI ); world.addBody(lineBody1);
// Создаем все шарики for (var i = 0; i < num; i++) { circleShape2[i] = new p2.Circle({ radius: R }); circleBody2[i] = new p2.Body({ mass:0.5, position:[(-6 + (12*(i+1)/(num+1))) ,0] , angularDamping:0, damping:B/24}); circleBody2[i].addShape(circleShape2[i]); world.addBody(circleBody2[i]); };
//Создаем пружину для связи с левой границей s = new p2.LinearSpring(circleBody2[0], lineBody, {
restLength : dx, stiffness : k,
damping : B,
localAnchorB : [4,0], }); world.addSpring(s);
// Создаем пружину для связи с правой границей s1 = new p2.LinearSpring(circleBody2[num-1], lineBody1, {
restLength : dx, stiffness : k,
damping : B,
localAnchorB : [4,0], }); world.addSpring(s1);
// Создаем пружины для связи шариков вместе for (var i = 0; i < num-1; i++) { ss[i] = new p2.LinearSpring(circleBody2[i], circleBody2[i+1], {
restLength : dx, stiffness : k,
damping : B,
localAnchorA : [0,0], localAnchorB : [0,0], });
world.addSpring(ss[i]); };
//Отключаем взаимодействие между пружинами и между шариками for (var i = 0; i < num-1; i++) { for (var j = i; j < num-1; j++) { world.disableBodyCollision(ss[i], ss[j]); } };
for (var i = 0; i < num; i++) { for (var j = i; j < num; j++) { world.disableBodyCollision(circleBody2[i], circleBody2[j]); world.disableBodyCollision(circleBody2[i], lineBody1); world.disableBodyCollision(circleBody2[i], lineBody); } };
// Создаем объект для мыши
mouseBody = new p2.Body(); world.addBody(mouseBody);
}
// Функция нажатия мыши вниз function doMouseDown(event){
var position = getPhysicsCoord(event); circleBody2.forEach(function(item, i, circleBody2)
{ var hitBodies = world.hitTest(position, [item]);
if(hitBodies.length){ mouseBody.position[0] = position[0]; mouseBody.position[1] = position[1]; mouseConstraint = new p2.LockConstraint(mouseBody, item); world.addConstraint(mouseConstraint);
}
}); }
// Функция перемещения мыши function doMouseMove(event){
var position = getPhysicsCoord(event); mouseBody.position[0] = position[0]; mouseBody.position[1] = position[1]; }
// Функция отпуская клавиши мыши
function doMouseUp(event){ world.removeConstraint(mouseConstraint); }
// Перевод координат мыши в текущие координаты function getPhysicsCoord(mouseEvent){
var rect = canvas.getBoundingClientRect(); var x = mouseEvent.clientX - rect.left; var y = mouseEvent.clientY - rect.top; x = (x - w / 2) / scaleX; y = (y - h / 2) / scaleY;
return [x, y];
}
// Функция рисования всех шаров function drawAllCircle(){ for (var i = 0; i < num; i++) { ctx.beginPath();
var x1 = circleBody2[i].position[0], y1 = circleBody2[i].position[1], radius1 = circleShape2[i].radius; ctx.arc(x1,y1,radius1,0,2*Math.PI);
ctx.fill(); ctx.stroke(); } ctx.strokeStyle = "black";
}
// Рисуем пол
function drawPlane(){ ctx.moveTo(-w, -4); ctx.lineTo( w, -4);
ctx.strokeStyle = "black";
ctx.stroke(); }
// Рисуем правую стену function drawRL(){
ctx.beginPath(); var x = lineBody.position[0], y = lineBody.position[1]; ctx.moveTo(x, -y); ctx.lineTo(x, y); ctx.strokeStyle = "black";
ctx.stroke();
}
// Рисуем левую стену
function drawLL(){
ctx.beginPath(); var x = lineBody1.position[0], y = lineBody1.position[1]; ctx.moveTo(x, -y); ctx.lineTo(x, y);
ctx.strokeStyle = "black";
ctx.stroke(); }
// Рисуем все пружины function drawAllSpring(){ for (var i = 0; i < num-1; i++) { ctx.beginPath();
var x = circleBody2[i].position[0], y = circleBody2[i].position[1];
var x1 = circleBody2[i+1].position[0],
y1 = circleBody2[i+1].position[1]; ctx.moveTo(x, y);
ctx.lineTo(x1, y1); ctx.strokeStyle = "black";
ctx.stroke();
}
}
// Рисуем пружину для связи первого шарика со стеной function draw1S(){ ctx.beginPath();
var x = circleBody2[0].position[0], y = circleBody2[0].position[1];
ctx.moveTo(-6, (s.localAnchorB[0]-4));
ctx.lineTo(x, y); ctx.strokeStyle = "black";
ctx.stroke();
}
// Рисуем пружину для связи последнего шарика со стеной function draw2S(){
ctx.beginPath(); var x = circleBody2[num-1].position[0], y = circleBody2[num-1].position[1]; ctx.moveTo(6, 0); ctx.lineTo(x, y); ctx.strokeStyle = "black";
ctx.stroke();
}
function render(){ ctx.clearRect(0,0,w,h); ctx.save(); ctx.translate(w/2, h/2); // Переводим начало в центр ctx.scale(50, -50); //Зумируем канвас // Рисуем все тела на канвасе
drawAllCircle();
drawRL();
drawLL(); drawPlane(); drawAllSpring(); draw1S(); draw2S();
ctx.restore(); }
function animate(){ requestAnimationFrame(animate);
if (sw == 1) FSin(); if (sw == 2) Stop(); if (sw == -1) Reset();
world.step(1/60); render(); }
}
</syntaxhighlight>