КП: Многочастичный симулятор — различия между версиями

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
м (Элементы системы)
м
Строка 328: Строка 328:
 
addPoint (100,100) (0,0) 40 5---addPoint (250,100) (0,0) 50 10---addPoint (400,100) (0,0) 40 5---addSpring #0 #2 10---addSpring #0 #1 20---addSpring #1 #2 20---setVelocity #0 (1,0)---setVelocity #2 (-1,0)
 
addPoint (100,100) (0,0) 40 5---addPoint (250,100) (0,0) 50 10---addPoint (400,100) (0,0) 40 5---addSpring #0 #2 10---addSpring #0 #1 20---addSpring #1 #2 20---setVelocity #0 (1,0)---setVelocity #2 (-1,0)
 
{{конец цитаты}}
 
{{конец цитаты}}
 +
 +
== Выдержки кода решения ==
 +
 +
<div class="mw-collapsible-content">
 +
Файл '''"point.js"'''
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
function Point(mass, radius, coorXY, oldCoorXY, isExpToPot)
 +
{
 +
  this.mass = mass;
 +
    this.invmass = 1 / mass;
 +
    this.radius = radius;
 +
    this.coor = coorXY;
 +
    this.oldCoor = oldCoorXY || coorXY;
 +
    this.id = getId();
 +
    this.type = 'point';
 +
    this.isExpToPot = isExpToPot || false;
 +
   
 +
// log
 +
    console.log('Create: '+this.toString());
 +
}
 +
 +
 +
Point.prototype.checkCoor = function(){
 +
    if (this.coor.isNaN())
 +
    {
 +
        console.error('Coor is NaN: ' + this);
 +
        player.stop();
 +
    }
 +
    else
 +
    {
 +
        if (this.oldCoor.isNaN())
 +
        {
 +
            console.warning('OldCoor is NaN: ' + this);
 +
            this.oldCoor = new Coor(this.coor);
 +
        }
 +
        else
 +
        {
 +
            return ;
 +
        }
 +
    }
 +
}
 +
 +
Point.prototype.toString = function(type){
 +
    if (type === 'full')
 +
    {
 +
        return JSON.stringify(this, [ 
 +
                                        'type',
 +
                                        'id',
 +
                                        'coor',
 +
                                        'mass_',
 +
                                        'radius_',
 +
                                        'x',
 +
                                        'y',
 +
                                        'begin',
 +
                                        'end'
 +
                                ], 4)
 +
    }
 +
    else
 +
    {
 +
        return JSON.stringify(this, [ 
 +
                                        'type',
 +
                                        'id',
 +
                                        'coor',
 +
                                        'x',
 +
                                        'y',
 +
                                        'begin',
 +
                                        'end'
 +
                                ], 4)
 +
    }
 +
}
 +
 +
Point.prototype.move = function(coorXY){
 +
var delta = minusCoor(this.coor, this.oldCoor);
 +
    this.coor = coorXY;
 +
    this.oldCoor = minusCoor(coorXY, delta);
 +
// log
 +
    console.log('Move: '+this.toString());
 +
}
 +
 +
Point.prototype.moveEase = function(coorXY){
 +
    this.coor = coorXY;
 +
    this.oldCoor = coorXY;
 +
// log
 +
    console.log('Move: '+this.toString());
 +
}
 +
</syntaxhighlight>
 +
 +
Файл '''"player.js"'''
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
var step;
 +
 +
function Player(dt, phys, render, console)
 +
{
 +
    this.tick = 0;
 +
    this.dt = dt;
 +
    this.tickCount = 0;
 +
    this.physics = new Physics(phys, this);
 +
    this.renderer = new Renderer(render, this);
 +
    this.parser = new Parser(this);
 +
    this.console = new Console(console, this);
 +
    var checker = checkerPl.bind(this);
 +
   
 +
    this.stop = function(){
 +
        clearTimeout(this.tick);
 +
        this.tick = 0;
 +
       
 +
// log
 +
        this.console.logAns('Pause...');
 +
    }
 +
   
 +
    this.play = function(){
 +
        if (!this.tick)
 +
        {
 +
            this.tick = setTimeout(function stepTm(pl){
 +
                step(pl, checker);
 +
                pl.tick = setTimeout(stepTm, pl.dt, pl);
 +
            }, this.dt, this);
 +
// log
 +
            this.console.logAns('Play');
 +
        }
 +
    }
 +
   
 +
    this.shifter = function(){
 +
        if (this.tick)
 +
        {
 +
            this.stop();
 +
           
 +
            return 0;
 +
        }
 +
        else
 +
        {
 +
            this.play();
 +
         
 +
            return 1;
 +
        }
 +
    }
 +
   
 +
    this.nextStep = function(i){
 +
        if (!i)
 +
        {
 +
            step(this, checker);
 +
// log
 +
            this.console.logAns('step');
 +
        }
 +
        else
 +
        {
 +
            this.console.logAns('step x ' + i);
 +
           
 +
            while (i--)
 +
            {
 +
                step(this, checker);
 +
            }
 +
        }
 +
       
 +
    }
 +
   
 +
    this.getInfo = function(){
 +
        this.console.logAns('Tick count: '+this.tickCount);
 +
        this.console.logAns('Points count: '+this.physics.Points.length);
 +
        this.console.logAns('Walls count: '+this.physics.Walls.length);
 +
        this.console.logAns('Springs count: '+this.physics.Springs.length);
 +
        this.console.logAns('Sticks count: '+this.physics.Sticks.length);
 +
    }
 +
   
 +
    this.find = function(id){
 +
        if (id in this.renderer.arr)
 +
        {
 +
            return this.renderer.arr[id].ptr;
 +
        }
 +
       
 +
        return 0;
 +
    }
 +
   
 +
    this.addPoint = function(mass, radius, coorXY, oldCoorXY, isExpToPot){
 +
        var tmp = this.physics.addPoint(mass, radius, coorXY, oldCoorXY, isExpToPot);
 +
        this.renderer.addPoint(tmp);
 +
// log
 +
        this.console.logAns('Create point.');
 +
    }
 +
   
 +
    this.addWall = function(vectM, vectO){
 +
        var tmp = this.physics.addWall(vectM, vectO);
 +
        this.renderer.addWall(tmp);
 +
// log
 +
        this.console.logAns('Create wall.');
 +
    }
 +
   
 +
    this.addStick = function(elem1, elem2){
 +
        var tmp = this.physics.addStick(elem1, elem2);
 +
        this.renderer.addStick(tmp);
 +
// log
 +
        this.console.logAns('Create stick.');
 +
    }
 +
   
 +
    this.addSpring = function(elem1, elem2, k){
 +
        var tmp = this.physics.addSpring(elem1, elem2, k);
 +
        this.renderer.addSpring(tmp);
 +
// log
 +
        this.console.logAns('Create spring.');
 +
    }
 +
   
 +
    this.parse = function(str){
 +
        return this.parser.parse(str);
 +
    }
 +
   
 +
    this.log = function(str){
 +
        this.console.log(str);
 +
    }
 +
   
 +
    this.warn = function(str){
 +
        this.console.warn(str);
 +
    }
 +
   
 +
    this.error = function(str){
 +
        this.console.error(str);
 +
    }
 +
}     
 +
 +
step = function(pl, checker){
 +
    if (!checker())
 +
    {
 +
        pl.physics.step();
 +
        pl.renderer.draw();
 +
        pl.tickCount++;
 +
    }
 +
    else
 +
    {
 +
        pl.stop();
 +
        checker('delete');
 +
    } 
 +
 +
 +
function checkerPl(mode, vr, val)
 +
{
 +
    var st = checkerPl.state;
 +
   
 +
    if (arguments.length == 0) // check
 +
    {
 +
        for (var i in st)
 +
        {
 +
            if (player[st[i].vr] < st[i].val)
 +
            {
 +
                return 1;
 +
            }
 +
        }
 +
       
 +
        return 0;
 +
    }
 +
   
 +
    if (mode == 'delete') // delete
 +
    {
 +
        for (var i in st)
 +
        {
 +
            if (player[st[i].vr] < st[i].val)
 +
            {
 +
                delete st[i];
 +
            }
 +
        }
 +
       
 +
        return 0;
 +
    }
 +
   
 +
    if (mode == 'add') // add
 +
    {
 +
        st.push({'vr': vr, 'val': val});
 +
       
 +
        return 0;
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 +
Файл '''"renderer.js"'''
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
function Renderer(param, pl)
 +
{
 +
    this.scr = $('#'+param.idEl);
 +
    this.borderSize = param.borderSize;
 +
    this.arr = new Array();
 +
    this.player = pl;
 +
}
 +
 +
Renderer.prototype.addPoint = function(point){ 
 +
    var el = $('<div data-id="'+point.id+'" class="point"></div>');
 +
    el.css('width', 2 * point.radius - 2 * this.borderSize);
 +
    el.css('height', 2 * point.radius - 2 * this.borderSize);
 +
    el.css('border-radius', 2 * point.radius - 2 * this.borderSize);
 +
  el.css('font-size', 10 * (point.radius - this.borderSize) + '%');
 +
    this.scr.append(el);
 +
   
 +
    var tmp = {
 +
        'el': el,
 +
        'ptr': point,
 +
        'draw': function(){
 +
                var c = this.patch(this.ptr.coor);
 +
                this.el.css('top', c.y);
 +
                this.el.css('left', c.x);
 +
        },
 +
        'setCoor': function(c){
 +
                c = c || this.patch(this.ptr.coor);
 +
                this.el.css('top', c.y);
 +
                this.el.css('left', c.x);
 +
        },
 +
        'patch': function(c){
 +
            return new Coor(c.x - this.ptr.radius, c.y - this.ptr.radius);
 +
        },
 +
        'patchC': function(c){
 +
            return new Coor(c.x + this.ptr.radius, c.y + this.ptr.radius);
 +
        }
 +
    };
 +
    this.arr[point.id] = tmp;
 +
   
 +
    tmp.setCoor();
 +
}
 +
 +
 +
Renderer.prototype.log = function(str){
 +
    this.player.log(str);
 +
}
 +
 +
 +
Renderer.prototype.warn = function(str){
 +
    this.player.warn(str);
 +
}
 +
 +
 +
Renderer.prototype.error = function(str){
 +
    this.player.error(str);
 +
}
 +
 +
 +
Renderer.prototype.addWall = function(wall){
 +
    var el = $('<div data-id="'+wall.id+'" class="wall"></div>');
 +
    el.css('left', wall.main_.midX());
 +
    el.css('top', wall.main_.midY())
 +
    this.scr.append(el);
 +
   
 +
    var tmp = {
 +
        'el': el,
 +
        'ptr': wall
 +
    }
 +
   
 +
    this.arr[wall.id] = tmp;
 +
}
 +
 +
Renderer.prototype.addStick = function(stick){
 +
    var el = $('<div data-id="'+stick.id+'" class="stick"></div>');
 +
    this.scr.append(el);
 +
   
 +
    var tmp = {
 +
        'el': el,
 +
        'ptr': stick,
 +
        'setCoor': function(b, e, l){
 +
            setCoorLink.call(this, b, e, l);
 +
        },
 +
        'draw': function(){
 +
            this.setCoor(this.ptr.coorB, this.ptr.coorE, this.ptr.length_);
 +
        }
 +
    };
 +
 +
    this.arr[stick.id] = tmp;
 +
}
 +
 +
Renderer.prototype.addSpring = function(spring){
 +
    var el = $('<div data-id="'+spring.id+'" class="spring"></div>');
 +
    this.scr.append(el);
 +
   
 +
    var tmp = {
 +
        'el': el,
 +
        'ptr': spring,
 +
        'setCoor': function(b, e, l){
 +
            setCoorLink.call(this, b, e, l);
 +
        },
 +
        'draw': function(){
 +
            if (this.ptr.k == 0)
 +
            {
 +
                this.el.hide();
 +
                delete this.draw;
 +
            }
 +
            else
 +
            {
 +
                this.setCoor(this.ptr.coorB, this.ptr.coorE, length(this.ptr.coorB, this.ptr.coorE));
 +
            }
 +
        }
 +
    };
 +
 +
    this.arr[spring.id] = tmp;
 +
}
 +
 +
Renderer.prototype.draw = function(){
 +
    for (var key in this.arr)
 +
    {
 +
        var l = this.arr[key];
 +
       
 +
        if (l.draw)
 +
        {
 +
            l.draw();           
 +
        }
 +
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 +
Файл '''"func.js"'''
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
function Coor(e, y)
 +
{
 +
    if (e.pageX)
 +
    {
 +
        this.x = e.pageX;
 +
        this.y = e.pageY;
 +
       
 +
        return ;
 +
    }
 +
    if (typeof e == 'number')
 +
    {
 +
        this.x = e;
 +
        this.y = y;
 +
       
 +
        return ;
 +
    }
 +
    else
 +
    {
 +
        this.x = e.x;
 +
        this.y = e.y;
 +
       
 +
        return ;
 +
    }
 +
}
 +
 +
function createHTMLTable(id, numberOfCellsPerRow, numberOfRows, cellWidth, cellHeight)
 +
{
 +
var generatedHTMLTable = '<style>    \
 +
#table' + id + '.show{}        \
 +
#table' + id + '.hide{display:none;}    \
 +
#table' + id + '{display:table;position:fixed;width: 2000px;height:1000px;top:27px;border-collapse:collapse;left:0px;z-index:1;outline:2px solid gray;}  \
 +
#table' + id + ' > div{display:table-row;}                                                \
 +
#table' + id + ' > div > div{display:table-cell;width:' + cellWidth + 'px;height:' + cellHeight + 'px;outline:1px solid white;}    \
 +
</style>';
 +
for(var ii = 0; ii < numberOfRows; ++ii)
 +
{
 +
generatedHTMLTable += "<div>";
 +
 +
for(var i = 0; i < numberOfCellsPerRow; ++i)
 +
{
 +
generatedHTMLTable += "<div></div>";
 +
}
 +
 +
generatedHTMLTable += "</div>";
 +
}
 +
                                             
 +
var tableDiv = $('<div id="table'+id+'" class="show" data-type="grid">'+generatedHTMLTable+'</div>')
 +
 +
$('body').append(tableDiv);
 +
}
 +
 +
function colorHTMLTableCell(id, numberOfRows, numberOfCell)
 +
{
 +
  var fix = 1;
 +
  var rowBefore = Math.floor((fix + numberOfCell) / numberOfRows);
 +
var styleToColorDiv = $('<style>#table' + id + ' > div:nth-child(' + (rowBefore + 1 + fix) + ') > div:nth-child(' + (0 + Math.floor(fix + numberOfCell - rowBefore * numberOfRows)) +'){outline:3px dashed dodgerblue;}</style>');
 +
$('#table' + id).append(styleToColorDiv);
 +
}
 +
 +
 +
Coor.prototype.toString = function(type){
 +
    return JSON.stringify(this);
 +
}
 +
 +
Coor.prototype.plus = function(B){
 +
    this.x += B.x;
 +
    this.y += B.y;
 +
   
 +
    return this;
 +
}
 +
 +
Coor.prototype.isNaN = function(){
 +
    if (isNaN(this.x) || isNaN(this.y))
 +
        return 1;
 +
    return 0;
 +
}
 +
 +
function dot(A, B)
 +
{
 +
    return (A.x * B.x + A.y * B.y);
 +
}
 +
 +
function length(A, B)
 +
{
 +
    return Math.sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
 +
}
 +
 +
function sign(x)
 +
{
 +
    return (x > 0 ? 1 : x < 0 ? -1 : 0);
 +
}
 +
 +
function Vector(A, B)
 +
{
 +
    this.begin = A;
 +
    this.end = B;
 +
    this.length = length(A, B);
 +
}
 +
 +
Vector.prototype.midX = function(){
 +
    return (this.begin.x + this.end.x)/2;
 +
}
 +
 +
 +
Vector.prototype.midY = function(){
 +
    return (this.begin.y + this.end.y)/2;
 +
}
 +
 +
Vector.prototype.toString = function(type){
 +
    if (type === 'full')
 +
    {
 +
        return JSON.stringify(this, null, 4)
 +
    }
 +
    else
 +
    {
 +
        return JSON.stringify(this, [ 
 +
                                        'begin',
 +
                                        'end',
 +
                                        'length',
 +
                                        'x',
 +
                                        'y',
 +
                                ], 4)
 +
    }
 +
}
 +
 +
Vector.prototype.resize = function(){
 +
    this.length = length(A, B);
 +
}
 +
 +
Vector.prototype.plus = function(B){
 +
    this.begin.plus(B.begin);
 +
    this.end.plus(B.end);
 +
    this.resize();
 +
   
 +
    return this;
 +
}
 +
 +
function plusVector(A, B)
 +
{
 +
    return new Vector(plusCoor(A.begin, B.begin), plusCoor(A.end, B.end));
 +
}
 +
 +
function plusCoor(A, B)
 +
{
 +
    return new Coor(A.x + B.x, A.y + B.y);
 +
}
 +
         
 +
function minusCoor(A, B)
 +
{
 +
    return new Coor(A.x - B.x, A.y - B.y);
 +
}
 +
 +
function timesCoor(e, k)
 +
{
 +
    return new Coor(k * e.x, k * e.y);
 +
}   
 +
function deltaCoor(e)
 +
{
 +
    return Math.sqrt(dot(e,e));
 +
}
 +
 +
function setCoorLink(a, b, length)
 +
{
 +
 
 +
    this.el.css('top', a.y);
 +
    this.el.css('left', a.x);
 +
this.el.css('height', length);
 +
var angle = 0;
 +
  var deltaX = b.x - a.x;
 +
  var deltaY = b.y - a.y;
 +
 
 +
  if (deltaX == 0)
 +
angle = ((deltaY > 0) - (deltaY < 0)) * Math.PI / 2;
 +
else
 +
        if(deltaX < 0)
 +
        angle = ((deltaY >= 0) - (deltaY < 0)) * Math.PI + Math.atan(deltaY / deltaX);
 +
        else angle = Math.atan(deltaY / deltaX);
 +
 +
  angle -= Math.PI/2;
 +
  this.el.css('transform', 'rotate(' + angle + 'rad)');
 +
}
 +
</syntaxhighlight>
 +
 +
Файл '''"console.js"'''
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
function Console(par, pl)
 +
{
 +
    this.player = pl;
 +
    this.btn = $(par.btn);
 +
    this.input = $(par.inp);
 +
    this.txt = $(par.log);
 +
    this.history = new Array();
 +
    this.oHistory = new Array();
 +
    var exec = this.execute.bind(this);
 +
    var keydown = keydownCons.bind(this);
 +
   
 +
    this.btn.click(function(){
 +
        exec();
 +
    });
 +
   
 +
    this.input.keydown(keydown);
 +
}
 +
 +
function keydownCons(e)
 +
{
 +
    var e = e || window.event;
 +
    var key = e.which;
 +
       
 +
    if (key == 38)
 +
    {
 +
        this.input.val(this.getHist());
 +
    }
 +
   
 +
    if (key == 40)
 +
    {
 +
        this.input.val(this.getHistInv());
 +
    }
 +
   
 +
    if (e.ctrlKey && key == 13)
 +
    {
 +
        this.input.val(this.input.val() + '---');
 +
       
 +
        return ;
 +
    }
 +
       
 +
    if (key == 13)
 +
    {
 +
        this.btn.trigger('click');
 +
    }
 +
}
 +
 +
Console.prototype.log = function(str){
 +
    var el = $('<div></div>').addClass('log').html(str.toString().trim()).click(showAllLog);
 +
   
 +
    this.txt.prepend(el);
 +
};
 +
 +
function showAllLog()
 +
{
 +
    $(this).addClass('all');
 +
}
 +
 +
Console.prototype.warn = function(str){
 +
    var el = $('<div></div>').addClass('warn').html(str.toString().trim()).click(showAllLog);
 +
   
 +
    this.txt.prepend(el);
 +
};
 +
 +
Console.prototype.error = function(str){
 +
    var el = $('<div></div>').addClass('err').html(str.toString().trim()).click(showAllLog);
 +
   
 +
    this.txt.prepend(el);
 +
};
 +
 +
Console.prototype.logAns = function(str){
 +
    var el = $('<div></div>').addClass('log').html('> ' + str.toString().trim()).click(showAllLog);
 +
   
 +
    this.txt.prepend(el);
 +
};
 +
 +
Console.prototype.warnAns = function(str){
 +
    var el = $('<div></div>').addClass('warn').html('> ' + str.toString().trim()).click(showAllLog);
 +
   
 +
    this.txt.prepend(el);
 +
};
 +
 +
Console.prototype.errorAns = function(str){
 +
    var el = $('<div></div>').addClass('err').html('> ' + str.toString().trim()).click(showAllLog);
 +
   
 +
    this.txt.prepend(el);
 +
};
 +
 +
Console.prototype.saveHist = function(txt){
 +
    while(this.oHistory.length)
 +
    {
 +
        this.history.push(this.oHistory.shift());
 +
    }
 +
   
 +
    this.history.push(txt);
 +
};
 +
 +
Console.prototype.getHist = function(){
 +
    var tmp = this.history.pop();
 +
    if (!tmp || tmp.length < 3)
 +
    {
 +
        return '';
 +
    }
 +
    this.oHistory.unshift(tmp);
 +
   
 +
    return tmp; 
 +
};
 +
 +
Console.prototype.getHistInv = function(){
 +
    var tmp = this.oHistory.shift();
 +
    if (!tmp || tmp.length < 3)
 +
    {
 +
        return '';
 +
    }
 +
    this.history.push(tmp);
 +
   
 +
    return tmp; 
 +
};
 +
 +
Console.prototype.execute = function(){
 +
    var txt = this.input.val().trim();
 +
   
 +
    if (txt === '')
 +
    {
 +
        return ;
 +
    }
 +
   
 +
   
 +
    if (txt === 'getHistory')
 +
    {
 +
this.input.val('');
 +
 +
    var string = '';
 +
   
 +
    if(this.history.length > 0){
 +
    var historyLength = this.history.length;
 +
   
 +
    for(var i = 0; i < historyLength; ++i){
 +
    console.log(this.history[i] + '---');
 +
    if(this.history[i] != undefined)
 +
    string += this.history[i] + '---';
 +
    }
 +
string = string.slice(0,-3);
 +
    }
 +
   
 +
    if(this.history.length == 0)
 +
    string = 'History is empty';
 +
   
 +
    alert(string);
 +
 +
    return 0;
 +
    }
 +
   
 +
    this.log(txt);
 +
   
 +
    if(!this.player.parse(txt))
 +
    {
 +
        this.input.val('');
 +
       
 +
        this.saveHist(txt);
 +
    }
 +
    else
 +
    {
 +
        this.warnAns('Can not disassemble');
 +
    }
 +
};
 +
</syntaxhighlight>
 +
 +
Файл '''"phen.css"'''
 +
<syntaxhighlight lang="css" line start="1" enclose="div">
 +
*{margin:0;padding:0;}
 +
body{background: gainsboro;}
 +
.point{
 +
    position: absolute;
 +
    z-index: 20;
 +
    counter-increment: num;
 +
    text-align: center;
 +
    border: 4px solid white;
 +
    background: dodgerblue;
 +
}
 +
.point:before{
 +
    content: counter(num);
 +
    color: white;
 +
    -webkit-user-select: none;
 +
    -khtml-user-select: none;
 +
    -moz-user-select: none;
 +
    -ms-user-select: none;
 +
    -o-user-select: none;
 +
    user-select: none;
 +
}
 +
#screen{
 +
    position: fixed;
 +
    z-index: 2;
 +
    top:27px;
 +
    counter-reset: num;
 +
    display: block;
 +
    overflow-x: hidden;
 +
    width: 100%;
 +
    height: auto;
 +
    bottom:0;
 +
    background: transparent;
 +
}
 +
/*#screen:after{
 +
  position:absolute;
 +
content: '';
 +
    display: block;
 +
height:596px;
 +
width:1196px;
 +
border: 2px solid crimson;
 +
}*/
 +
#btns input
 +
{
 +
    font-size: 18px;
 +
    position: fixed;
 +
    z-index: 2;
 +
    overflow: hidden;
 +
    width: 25%;
 +
    white-space: nowrap;
 +
    text-overflow: ellipsis;
 +
    color: white;
 +
    border: 3px solid white;
 +
    border-right: none;
 +
    border-radius: none!important;
 +
    outline: none!important;
 +
    background: dodgerblue;
 +
}
 +
#btns input.hide{display:none;}
 +
#btns input:nth-child(2){left:25%;}
 +
#btns input:nth-child(3){left:50%;}
 +
#btns input:nth-child(4){left:50%;}
 +
#btns input:last-child
 +
{
 +
    left: 75%;
 +
    border: 3px solid white;
 +
}
 +
#btns input.notActive{}
 +
#btns input:active,
 +
#btns input.active
 +
{
 +
    background: crimson!important;
 +
}
 +
 +
#btns input[disabled],
 +
#btns input[disabled]:active
 +
{
 +
    background: gray!important;
 +
}
 +
.wall{
 +
    left: 252.5px;
 +
    top: 227.5px;
 +
    height:0px;
 +
    width:500px;
 +
    z-index:1000;
 +
    margin-top:-2px;
 +
    position: absolute;
 +
    /*  background: url('./wall.png') repeat-x;*/
 +
    -webkit-transform: rotate(180deg);
 +
    -khtml-transform: rotate(180deg);
 +
      -moz-transform: rotate(180deg);
 +
      -ms-transform: rotate(180deg);
 +
        -o-transform: rotate(180deg);
 +
          transform: rotate(180deg);
 +
    /*border-top: 2px solid crimson;*/
 +
}
 +
.stick,.spring{
 +
background: white;
 +
width: 2px;
 +
position:absolute;
 +
-webkit-transform-origin: 50% 0;
 +
-khtml-transform-origin: 50% 0;
 +
  -moz-transform-origin: 50% 0;
 +
    -ms-transform-origin: 50% 0;
 +
    -o-transform-origin: 50% 0;
 +
        transform-origin: 50% 0;
 +
}
 +
.spring{
 +
background: url('./spring.png') no-repeat;
 +
background-size: 10px 100%;
 +
  width:10px;margin-left:-5px;
 +
}
 +
input[disabled],
 +
input[disabled]:active{
 +
    background: gray!important;
 +
}
 +
#stack {
 +
    width: 130px;
 +
    height: 260px;
 +
    border: solid 1px black;
 +
    border-top: 0px;
 +
}
 +
 +
#stack .brick {
 +
    width: 11px;
 +
    height: 11px;
 +
    border: solid 1px white;
 +
    background: white;
 +
    float: left;
 +
}
 +
#stack .brick.on {
 +
    background: black;
 +
}
 +
#stack .brick.now {
 +
    background: green;
 +
}
 +
 +
#controls{
 +
    position: fixed;
 +
    z-index: 22;
 +
    bottom: 5px;
 +
    left: 5px;
 +
    width: 165px;
 +
    height: 30px;
 +
    padding: 15px;
 +
    -webkit-transition: all 400ms;
 +
    -khtml-transition: all 400ms;
 +
      -moz-transition: all 400ms;
 +
        -o-transition: all 400ms;
 +
            transition: all 400ms;
 +
    opacity: .5;
 +
    border: 3px solid #eee;
 +
    border-radius: 20px;
 +
    background-color: dodgerblue;
 +
}
 +
#controls.show{opacity: 1;}
 +
#controls:before{
 +
    position: absolute;
 +
    top: -2px;
 +
    left: -2px;
 +
    display: block;
 +
    width: 334px;
 +
    height: 65px;
 +
    content: '';
 +
}
 +
#playerBtns{
 +
    position: relative;
 +
    left: 30px;
 +
}
 +
#playerBtns span{
 +
    position: absolute;
 +
    top: 4px;
 +
}
 +
#controls[type='0'] #playerBtns span:nth-of-type(1){
 +
    top: 0;
 +
    width: 0;
 +
    height: 0;
 +
    border-width: 15px 0 15px 30px;
 +
    border-style: solid;
 +
    border-color: transparent transparent transparent white;
 +
}
 +
#controls[type='1'] #playerBtns span:nth-of-type(1){
 +
    top: 0;
 +
    right: 169px!important;
 +
    width: 11px;
 +
    height: 30px;
 +
    border-right: 8px solid crimson;
 +
    border-left: 8px solid crimson;
 +
}
 +
#playerBtns span:nth-of-type(2){
 +
    width: 0;
 +
    height: 0;
 +
    border-width: 10px 0 10px 20px;
 +
    border-style: solid;
 +
    border-color: transparent transparent transparent white;
 +
}
 +
#playerBtns span:nth-of-type(3){
 +
    width: 0;
 +
    height: 0;
 +
    border-width: 10px 0 10px 20px;
 +
    border-style: solid;
 +
    border-color: transparent transparent transparent white;
 +
}
 +
#playerBtns span:nth-of-type(3):after{
 +
    position: absolute;
 +
    top: -10px;
 +
    width: 0;
 +
    height: 0;
 +
    content: '';
 +
    border-width: 10px 0 10px 20px;
 +
    border-style: solid;
 +
    border-color: transparent transparent transparent white;
 +
}
 +
#playerBtns span:nth-of-type(4){
 +
    width: 0;
 +
    height: 0;
 +
    border-width: 10px 0 10px 20px;
 +
    border-style: solid;
 +
    border-color: transparent transparent transparent white;
 +
}
 +
#playerBtns span:nth-of-type(4):after{
 +
    position: absolute;
 +
    top: -10px;
 +
    width: 0;
 +
    height: 0;
 +
    content: '';
 +
    border-width: 10px 2px;
 +
    border-style: solid;
 +
    border-color: white;
 +
}
 +
#playerBtns span:nth-of-type(1){right: 165px;}
 +
#playerBtns span:nth-of-type(2){right: 130px;}
 +
#playerBtns span:nth-of-type(3){right: 90px;}
 +
#playerBtns span:nth-of-type(4){right: 35px;}
 +
#consoleWindow{
 +
position: fixed;
 +
top: 30px;
 +
z-index: 2;
 +
right: 0;
 +
width: 25%;
 +
font-weight: bold;
 +
font-size: 14px;
 +
overflow: hidden;
 +
max-height: 339px;
 +
background: white;
 +
}
 +
#consoleWindow.hide{height:0;}
 +
#consoleWindow.show{height: auto;}
 +
#consoleInput{border-bottom: 3px solid gainsboro;}
 +
#consoleInput:before{
 +
content: '>';
 +
font-size: 14px;
 +
position: absolute;
 +
top: 4px;
 +
left: 4px;
 +
}
 +
#consoleInput [type="text"]{
 +
border: none!important;
 +
outline: none!important;
 +
height: 20px;
 +
font-size: 14px;
 +
width: 100%;
 +
box-sizing:border-box;
 +
padding-left: 15px;
 +
}
 +
#consoleInput [type="button"]{
 +
font-size: 18px;
 +
    overflow: hidden;
 +
    width: 100%;
 +
    right: 0;
 +
    white-space: nowrap;
 +
    text-overflow: ellipsis;
 +
    color: white;
 +
    border: 3px solid white;
 +
    border-radius: none!important;
 +
    outline: none!important;
 +
    background: dodgerblue;
 +
}
 +
#consoleInput [type="button"]:active{background: crimson!important;}
 +
#consoleLog{
 +
width: 100%;
 +
max-height: 290px;
 +
box-sizing: border-box;
 +
height: auto;
 +
word-wrap: break-word;
 +
overflow-y: auto;
 +
overflow-x: hidden;
 +
background: white;
 +
padding-left: 3px;
 +
padding-bottom: 10px;
 +
}
 +
 +
#consoleLog div:not(.all){
 +
text-overflow: ellipsis;
 +
overflow: hidden;
 +
max-height: 37px;
 +
width: 100%;
 +
white-space: nowrap;
 +
}
 +
 +
#consoleLog .warn{color: #DAA520;}
 +
#consoleLog .err{color: crimson;}
 +
#consoleLog .log{color: grey;}
 +
</syntaxhighlight>
 +
 +
Файл '''"index.php"'''
 +
<syntaxhighlight lang="html" line start="1" enclose="div">
 +
<!DOCTYPE html>
 +
<html lang="ru">
 +
<head>
 +
  <title>Phen v2.0 a</title>
 +
  <link rel="stylesheet" href="./phen.css">
 +
    <script type="text/javascript" src="./jquery-2.1.3.min.js"></script>
 +
    <script type="text/javascript" src="./jquery-css-transform.js"></script>
 +
    <script type="text/javascript" src="./point.js"></script>
 +
    <script type="text/javascript" src="./spring.js"></script>
 +
    <script type="text/javascript" src="./stick.js"></script>
 +
    <script type="text/javascript" src="./func.js"></script>
 +
<script type="text/javascript" src="./renderer.js"></script>
 +
  <script type="text/javascript" src="./wall.js"></script>
 +
  <script type="text/javascript" src="./phen.js"></script>
 +
    <script type="text/javascript" src="./parser.js"></script>
 +
<script type="text/javascript" src="./console.js"></script>
 +
  <script type="text/javascript" src="./player.js"></script>
 +
  <script type="text/javascript" src="./cycle.js"></script>
 +
    <script type="text/javascript" src="./clean.js"></script>
 +
</head>
 +
<body>
 +
    <div id="screen"></div>
 +
    <div id="btns">
 +
        <input type="button" value="Open/Save" disabled="disabled">
 +
        <input id="getPoint" type="button" value="Get point's id">
 +
      <input id="movePoint" type="button" value="Move point">
 +
      <input id="demovePoint" type="button" class="active hide" value="Move point">
 +
      <input id="consoleOpen" class="active" type="button" value="Console">
 +
    </div>
 +
    <div id="consoleWindow" class="show">
 +
    <div id="consoleInput">
 +
    <input id="console" type="text" autofocus="autofocus">
 +
<input id="consoleBtn" type="button" value="Enter">
 +
    </div>
 +
    <div id="consoleLog">
 +
    </div>
 +
    </div>
 +
<div id="controls" class="show" type="0">
 +
      <div id="playerBtns">
 +
        <span id="stop"></span>
 +
        <span id="stepMin"></span>
 +
        <span id="stepMid"></span>
 +
        <span id="stepMax"></span>
 +
      </div>
 +
</div>
 +
</body>
 +
</html>
 +
</syntaxhighlight>
 +
</div>
  
 
== Обсуждение результатов и выводы ==
 
== Обсуждение результатов и выводы ==

Версия 23:44, 2 июня 2015

А.М. Кривцов > Теоретическая механика > Курсовые проекты ТМ 2015 > Многочастичный симулятор
IPhone image 2015-4-13-1431495674163 1.jpg

Курсовой проект по Теоретической механике

Исполнитель: Старобинский Егор

Группа: 09 (23604)

Семестр: весна 2015

Аннотация проекта

Формулировка задачи

Цель работы

Создание интернет-сайта, позволяющего пользователю моделировать многоточечную систему онлайн.

Решаемые задачи
  1. решение уравнения движения;
  2. визуализация.

Общие сведения по теме

Уравнение движения

Пусть мы наблюдаем тело в момент времени [math]t[/math].

Хотим знать, где окажется тело через малое изменение времени - [math]\Delta t[/math].

Рассмотрим базовый метод интегрирования Верле:

[math]\vec{x}(t + \Delta t) = 2\vec{x}(t) - \vec{x}(t - \Delta t) +\frac{ \vec{R}(t) \Delta t^2 }m[/math], где

[math]\vec{x}[/math] - позиция точки,

[math]\vec{R}[/math] - равнодействующая всех сил, действующих на тело,

[math]m[/math] - масса тела,

[math]t[/math] - текущий момент времени,

[math]\Delta t[/math] - малое изменение времени.

Метод Верле позволяет вычислять траекторию по упрощённой схеме: зная предыдущее и текущее положения ([math]\vec{x}(t - \Delta t)[/math] и [math]\vec{x}(t)[/math] соответственно)  и мгновенное значение равнодействующей приложенных сил в текущем положении [math]\vec{R}(t)[/math].

Достоинства метода: самокоррекция и бóльшая точность по сравнению с численным методом Эйлера.

Язык реализации: JavaScript.

Визуализация

Язык рализации: pure SCSS.

Обработка событий: JavaScript.

Отказ от WebGL продиктован выбором методов оптимизации для возможности работы с тысячами частиц.

Решение

Результат

Страница решения

Вы можете попробовать возможности симулятора прямо здесь, либо перейдя на полноразмерную страницу решения.

В пункте 4.7 представлен пример готовой команды для создания трёхатомной молекулы.

Нахождение периода в простом движении
Элементы системы
  • Частицы;
  • Стержни и пружины[1];
  • Стенки;
  • Поле сил;
  • Рабочее окно;
  • Сетки разметки;
  • Консоль;
  • Плеер.


Пример вывода консоли
Возможности консоли
  • Конфигурация начальной системы тел;
  • Изменение системы в процессе работы ("на лету");
  • Запуск алгоритмов анализа системы;
  • Распознавание и вывод ошибок в пользовательских запросах и в исходном коде;
  • Распознавание и вывод предупреждений в пользовательских запросах и в исходном коде;
  • Подключение/отключение сеток разметки, в том числе с пользовательскими размерами ячейки;
  • Тетрис.
Команды консоли

Координаты пишутся в декартовой системе (х,у), единица измерения - пиксели, орт х направлен от левого края к правому, орт у от верхнего края к нижнему. Пример: (0,100) - координаты точки, лежащей на левом краю экрана на 100 пикселей ниже верхней границы.

Консоль выводит каждое сообщение как одну строку. Если доступной длины строки не хватает, сообщение обрезается. Для отображения полной версии сообщения необходимо кликнуть по нему мышью.

Основные запросы

Очистить поле консоли

  • clrscr


Отобразить статистику элементов системы и чилто тиков:

  • getInfo


Создать частицу

  • addPoint (100,100) (0,10) 80 5[2][3], где

(100,100) - текущие координаты

(0,10) - вектор скорости относительно начала координат

80 - радиус частицы в пикселях

5 - масса частицы в у. е.


Задать вектор скорости (относительно начала координат)

  • setVelocity #0 (10,10), где

0 - id частицы[4]


Переместить частицу (относительно начала координат)

  • movePoint #3 (100,100) saveV[2][5], где

3 - id частицы[4]

(100,100) - новые координаты

saveV - флаг сохранения скорости. Если указан, частица после перемещения сохранит вектор своей скорости.


Задать массу

  • setProps #0 mass 10, где

0 - id частицы[4]

10 - новая масса

  • setProps #0 invmass 0.1, где

0 - id частицы[4]

0.1 - обратное значение новой массы


Задать радиус

  • setProps #0 radius 100, где

0 - id частицы[4]

100 - радиус частицы


Создать пружину между частицами

0, 1 - id частиц[4]

50 - жёсткость пружины


Изменить жёсткость пружины

  • changeSpring #5 10, где

5 - id пружины[4]

10 - новая жёсткость


Изменить жёсткость одинаковых пружин

  • changeSprings 10->15, где

10 - текущая жёсткость

15 - новая жёсткость


Изменить жёсткость всех пружин

  • changeSprings all->20, где

20 - новая жёсткость


Создать стержень между частицами

  • addStick #2 #1[5], где

2, 1 - id частиц[4]


Отключение гравитации

  • gravy disable


Задать вектор ускорения свободного падения (относительно начала координат)

  • gravy (0,10)


Отобразить сетку

  • showGrid type 0
  • showGrid type 1
  • showGrid type 2


Спрятать сетку

  • hideGrid


Запустить симуляцию

  • play


Остановить симуляцию

  • stop


"Промотать" симуляцию

  • step(100), где

100 - число пропускаемых отрисовкой тиков


Исполнить внутренний метод

  • execute nameMethod(params), где

nameMethod - название метода

params - сообщаемые параметры


Посмотреть историю запросов

  • getHistory [7]


Объединить команды в одном запросе

  • commandOne---commandTwo, где

commandOne, commandTwo - команды консоли, могут также состоять из объединённых команд

Кнопки интерфейса
  • Кнопка Get point's id

После нажатия на кнопку, а затем на частицу выводит id последней в консоль.

  • Кнопка Move point[5]

Эквивалентна команде консоли movePoint без флага saveV.

После нажатия на кнопку, а затем на частицу закрепляет управление положением последним за курсором мыши.

Дальнейшие нажатия на свободные участки поля переносят эту частицу в точку нажатия, при этом её скорость считается нулевой.

Для прекращения управления следует вновь нажать кнопку.

  • Клавиша клавиатуры ~ (также `, ё, Ё)

Эквивалентна командам консоли play/stop и крайней левой кнопке плеера.

  • Кнопки плеера

Слева направо:

Воспроизведение/остановка симуляции (эквивалентно командам консоли play/step), переход на тик вперёд (эквивалентно команде консоли step(1))переход на 50 тиков вперёд (эквивалентно команде консоли step(50)), переход на 100 тиков вперёд (эквивалентно команде консоли step(100)).

Возможности плеера
  • Воспроизведение/пауза симуляции с заданным [math]\Delta t[/math];
  • Скачок вперёд на кратное [math]\Delta t[/math] время;
  • "Замедление времени"[8].
Пример системы

Смоделируем молекулу из трёх атомов (подобную молекуле углекислого газа, рассматриваемой в курсовом проекте А. Смирнова).

PhenCO2Example.png

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

Создаём атомы кислорода, углерода и кислорода соответственно.

  • addPoint (100,100) (0,0) 40 5
  • addPoint (250,100) (0,0) 50 10
  • addPoint (400,100) (0,0) 40 5

Соединяем атомы пружинами для моделирования связей. Первая пружина обеспечивает устойчивость молекулы, не создавая внутреннего напряжения.

  • addSpring #0 #2 10
  • addSpring #0 #1 20
  • addSpring #1 #2 20

Дополнительно можем задать скорости у атомов кислорода для создания колебаний.

  • setVelocity #0 (1,0)
  • setVelocity #2 (-1,0)

Итоговый код (одна строка):

addPoint (100,100) (0,0) 40 5---addPoint (250,100) (0,0) 50 10---addPoint (400,100) (0,0) 40 5---addSpring #0 #2 10---addSpring #0 #1 20---addSpring #1 #2 20---setVelocity #0 (1,0)---setVelocity #2 (-1,0)

Выдержки кода решения

Файл "point.js"

 1 function Point(mass, radius, coorXY, oldCoorXY, isExpToPot)
 2 {
 3   	this.mass = mass;
 4     this.invmass = 1 / mass;
 5     this.radius = radius;
 6     this.coor = coorXY;
 7     this.oldCoor = oldCoorXY || coorXY;
 8     this.id = getId();
 9     this.type = 'point';
10     this.isExpToPot = isExpToPot || false;
11     
12 // log
13     console.log('Create: '+this.toString());
14 }
15 
16 
17 Point.prototype.checkCoor = function(){
18     if (this.coor.isNaN())
19     {
20         console.error('Coor is NaN: ' + this);
21         player.stop();
22     }
23     else
24     {
25         if (this.oldCoor.isNaN())
26         {
27             console.warning('OldCoor is NaN: ' + this);
28             this.oldCoor = new Coor(this.coor);
29         }
30         else
31         {
32             return ;
33         }
34     }
35 }
36 
37 Point.prototype.toString = function(type){
38     if (type === 'full')
39     {
40         return JSON.stringify(this, [   
41                                         'type',
42                                         'id',
43                                         'coor',
44                                         'mass_',
45                                         'radius_',
46                                         'x',
47                                         'y',
48                                         'begin',
49                                         'end'
50                                 ], 4)
51     }
52     else
53     {
54         return JSON.stringify(this, [   
55                                         'type',
56                                         'id',
57                                         'coor',
58                                         'x',
59                                         'y',
60                                         'begin',
61                                         'end'
62                                 ], 4)
63     }
64 }
65 
66 Point.prototype.move = function(coorXY){
67 	var delta = minusCoor(this.coor, this.oldCoor);
68     this.coor = coorXY;
69     this.oldCoor = minusCoor(coorXY, delta);
70 // log
71     console.log('Move: '+this.toString());
72 }
73 
74 Point.prototype.moveEase = function(coorXY){
75     this.coor = coorXY;
76     this.oldCoor = coorXY;
77 // log
78     console.log('Move: '+this.toString());
79 }

Файл "player.js"

  1 var step;
  2 
  3 function Player(dt, phys, render, console)
  4 {
  5     this.tick = 0;
  6     this.dt = dt;
  7     this.tickCount = 0;
  8     this.physics = new Physics(phys, this);
  9     this.renderer = new Renderer(render, this);
 10     this.parser = new Parser(this);
 11     this.console = new Console(console, this);
 12     var checker = checkerPl.bind(this);
 13     
 14     this.stop = function(){
 15         clearTimeout(this.tick);
 16         this.tick = 0;
 17         
 18 // log
 19         this.console.logAns('Pause...');
 20     }
 21     
 22     this.play = function(){
 23         if (!this.tick)
 24         {
 25             this.tick = setTimeout(function stepTm(pl){
 26                 step(pl, checker);
 27                 pl.tick = setTimeout(stepTm, pl.dt, pl);
 28             }, this.dt, this);
 29 // log
 30             this.console.logAns('Play');
 31         }
 32     }
 33     
 34     this.shifter = function(){
 35         if (this.tick)
 36         {
 37             this.stop();
 38             
 39             return 0;
 40         }
 41         else
 42         {
 43             this.play();
 44           	
 45             return 1;
 46         }
 47     }
 48     
 49     this.nextStep = function(i){
 50         if (!i)
 51         {
 52             step(this, checker);
 53 // log
 54             this.console.logAns('step');
 55         }
 56         else
 57         {
 58             this.console.logAns('step x ' + i);
 59             
 60             while (i--)
 61             {
 62                 step(this, checker);
 63             }
 64         }
 65         
 66     }
 67     
 68     this.getInfo = function(){
 69         this.console.logAns('Tick count: '+this.tickCount);
 70         this.console.logAns('Points count: '+this.physics.Points.length);
 71         this.console.logAns('Walls count: '+this.physics.Walls.length);
 72         this.console.logAns('Springs count: '+this.physics.Springs.length);
 73         this.console.logAns('Sticks count: '+this.physics.Sticks.length);
 74     }
 75     
 76     this.find = function(id){
 77         if (id in this.renderer.arr)
 78         {
 79             return this.renderer.arr[id].ptr;
 80         }
 81         
 82         return 0;
 83     }
 84     
 85     this.addPoint = function(mass, radius, coorXY, oldCoorXY, isExpToPot){
 86         var tmp = this.physics.addPoint(mass, radius, coorXY, oldCoorXY, isExpToPot);
 87         this.renderer.addPoint(tmp);
 88 // log
 89         this.console.logAns('Create point.');
 90     }
 91     
 92     this.addWall = function(vectM, vectO){
 93         var tmp = this.physics.addWall(vectM, vectO);
 94         this.renderer.addWall(tmp);
 95 // log
 96         this.console.logAns('Create wall.');
 97     }
 98     
 99     this.addStick = function(elem1, elem2){
100         var tmp = this.physics.addStick(elem1, elem2);
101         this.renderer.addStick(tmp);
102 // log
103         this.console.logAns('Create stick.');
104     }
105     
106     this.addSpring = function(elem1, elem2, k){
107         var tmp = this.physics.addSpring(elem1, elem2, k);
108         this.renderer.addSpring(tmp);
109 // log
110         this.console.logAns('Create spring.');
111     }
112     
113     this.parse = function(str){
114         return this.parser.parse(str);
115     }
116     
117     this.log = function(str){
118         this.console.log(str);
119     }
120     
121     this.warn = function(str){
122         this.console.warn(str);
123     }
124     
125     this.error = function(str){
126         this.console.error(str);
127     }
128 }      
129 
130 step = function(pl, checker){
131     if (!checker())
132     {
133         pl.physics.step();
134         pl.renderer.draw();
135         pl.tickCount++;
136     }
137     else
138     {
139         pl.stop();
140         checker('delete');
141     }  
142 }   
143 
144 function checkerPl(mode, vr, val)
145 {
146     var st = checkerPl.state;
147     
148     if (arguments.length == 0) // check
149     {
150         for (var i in st)
151         {
152             if (player[st[i].vr] < st[i].val)
153             {
154                 return 1;
155             }
156         }
157         
158         return 0;
159     }
160     
161     if (mode == 'delete') // delete
162     {
163         for (var i in st)
164         {
165             if (player[st[i].vr] < st[i].val)
166             {
167                 delete st[i];
168             }
169         }
170         
171         return 0;
172     }
173     
174     if (mode == 'add') // add
175     {
176         st.push({'vr': vr, 'val': val});
177         
178         return 0;
179     }
180 }

Файл "renderer.js"

  1 function Renderer(param, pl)
  2 {
  3     this.scr = $('#'+param.idEl);
  4     this.borderSize = param.borderSize;
  5     this.arr = new Array();
  6     this.player = pl;
  7 }
  8 
  9 Renderer.prototype.addPoint = function(point){   
 10     var el = $('<div data-id="'+point.id+'" class="point"></div>');
 11     el.css('width', 2 * point.radius - 2 * this.borderSize);
 12     el.css('height', 2 * point.radius - 2 * this.borderSize);
 13     el.css('border-radius', 2 * point.radius - 2 * this.borderSize);
 14   	el.css('font-size', 10 * (point.radius - this.borderSize) + '%');
 15     this.scr.append(el);
 16     
 17     var tmp = {
 18         'el': el,
 19         'ptr': point,
 20         'draw': function(){
 21                 var c = this.patch(this.ptr.coor);
 22                 this.el.css('top', c.y);
 23                 this.el.css('left', c.x);
 24         },
 25         'setCoor': function(c){
 26                 c = c || this.patch(this.ptr.coor);
 27                 this.el.css('top', c.y);
 28                 this.el.css('left', c.x);
 29         },
 30         'patch': function(c){
 31             return new Coor(c.x - this.ptr.radius, c.y - this.ptr.radius);
 32         },
 33         'patchC': function(c){
 34             return new Coor(c.x + this.ptr.radius, c.y + this.ptr.radius);
 35         }
 36     };
 37     this.arr[point.id] = tmp;
 38     
 39     tmp.setCoor();
 40 }
 41 
 42 
 43 Renderer.prototype.log = function(str){
 44     this.player.log(str);
 45 }
 46 
 47 
 48 Renderer.prototype.warn = function(str){
 49     this.player.warn(str);
 50 }
 51 
 52 
 53 Renderer.prototype.error = function(str){
 54     this.player.error(str);
 55 }
 56 
 57 
 58 Renderer.prototype.addWall = function(wall){
 59     var el = $('<div data-id="'+wall.id+'" class="wall"></div>');
 60     el.css('left', wall.main_.midX());
 61     el.css('top', wall.main_.midY())
 62     this.scr.append(el);
 63     
 64     var tmp = {
 65         'el': el,
 66         'ptr': wall
 67     }
 68     
 69     this.arr[wall.id] = tmp;
 70 }
 71 
 72 Renderer.prototype.addStick = function(stick){
 73     var el = $('<div data-id="'+stick.id+'" class="stick"></div>');
 74     this.scr.append(el);
 75     
 76     var tmp = {
 77         'el': el,
 78         'ptr': stick,
 79         'setCoor': function(b, e, l){
 80             setCoorLink.call(this, b, e, l);
 81         },
 82         'draw': function(){
 83             this.setCoor(this.ptr.coorB, this.ptr.coorE, this.ptr.length_);
 84         }
 85     };
 86 
 87     this.arr[stick.id] = tmp;
 88 }
 89 
 90 Renderer.prototype.addSpring = function(spring){
 91     var el = $('<div data-id="'+spring.id+'" class="spring"></div>');
 92     this.scr.append(el);
 93     
 94     var tmp = {
 95         'el': el,
 96         'ptr': spring,
 97         'setCoor': function(b, e, l){
 98             setCoorLink.call(this, b, e, l);
 99         },
100         'draw': function(){
101             if (this.ptr.k == 0)
102             {
103                 this.el.hide();
104                 delete this.draw;
105             }
106             else
107             {
108                 this.setCoor(this.ptr.coorB, this.ptr.coorE, length(this.ptr.coorB, this.ptr.coorE));
109             }
110         }
111     };
112 
113     this.arr[spring.id] = tmp;
114 }
115 
116 Renderer.prototype.draw = function(){
117     for (var key in this.arr)
118     {
119         var l = this.arr[key];
120         
121         if (l.draw)
122         {
123             l.draw();            
124         }
125 
126     }
127 }

Файл "func.js"

  1 function Coor(e, y)
  2 {
  3     if (e.pageX)
  4     {
  5         this.x = e.pageX;
  6         this.y = e.pageY;
  7         
  8         return ;
  9     }
 10     if (typeof e == 'number')
 11     {
 12         this.x = e;
 13         this.y = y;
 14         
 15         return ;
 16     }
 17     else
 18     {
 19         this.x = e.x;
 20         this.y = e.y;
 21         
 22         return ;
 23     }
 24 }
 25 
 26 function createHTMLTable(id, numberOfCellsPerRow, numberOfRows, cellWidth, cellHeight)
 27 {
 28 	var generatedHTMLTable = '<style>    \
 29 		#table' + id + '.show{}        \
 30 		#table' + id + '.hide{display:none;}     \
 31 		#table' + id + '{display:table;position:fixed;width: 2000px;height:1000px;top:27px;border-collapse:collapse;left:0px;z-index:1;outline:2px solid gray;}   \
 32 		#table' + id + ' > div{display:table-row;}                                                \
 33 		#table' + id + ' > div > div{display:table-cell;width:' + cellWidth + 'px;height:' + cellHeight + 'px;outline:1px solid white;}    \
 34 		</style>';
 35 	for(var ii = 0; ii < numberOfRows; ++ii)
 36 	{
 37 		generatedHTMLTable += "<div>";
 38 		
 39 		for(var i = 0; i < numberOfCellsPerRow; ++i)
 40 		{
 41 			generatedHTMLTable += "<div></div>";
 42 		}
 43 		
 44 		generatedHTMLTable += "</div>";
 45 	}
 46 	                                              
 47 	var tableDiv = $('<div id="table'+id+'" class="show" data-type="grid">'+generatedHTMLTable+'</div>')
 48 	
 49 	$('body').append(tableDiv);
 50 }
 51 
 52 function colorHTMLTableCell(id, numberOfRows, numberOfCell)
 53 {
 54   	var fix = 1;
 55   	var rowBefore = Math.floor((fix + numberOfCell) / numberOfRows);
 56 	var styleToColorDiv = $('<style>#table' + id + ' > div:nth-child(' + (rowBefore + 1 + fix) + ') > div:nth-child(' + (0 + Math.floor(fix + numberOfCell - rowBefore * numberOfRows)) +'){outline:3px dashed dodgerblue;}</style>');
 57 	$('#table' + id).append(styleToColorDiv);
 58 }
 59 
 60 
 61 Coor.prototype.toString = function(type){
 62     return JSON.stringify(this);
 63 }
 64 
 65 Coor.prototype.plus = function(B){
 66     this.x += B.x;
 67     this.y += B.y;
 68     
 69     return this;
 70 }
 71 
 72 Coor.prototype.isNaN = function(){
 73     if (isNaN(this.x) || isNaN(this.y))
 74         return 1;
 75     return 0;
 76 }
 77 
 78 function dot(A, B)
 79 {
 80     return (A.x * B.x + A.y * B.y);
 81 }
 82 
 83 function length(A, B)
 84 {
 85     return Math.sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
 86 }
 87 
 88 function sign(x)
 89 {
 90     return (x > 0 ? 1 : x < 0 ? -1 : 0);
 91 }
 92  
 93 function Vector(A, B)
 94 {
 95     this.begin = A;
 96     this.end = B;
 97     this.length = length(A, B);
 98 }
 99 
100 Vector.prototype.midX = function(){
101     return (this.begin.x + this.end.x)/2;
102 }
103 
104 
105 Vector.prototype.midY = function(){
106     return (this.begin.y + this.end.y)/2;
107 }
108 
109 Vector.prototype.toString = function(type){
110     if (type === 'full')
111     {
112         return JSON.stringify(this, null, 4)
113     }
114     else
115     {
116         return JSON.stringify(this, [   
117                                         'begin',
118                                         'end',
119                                         'length',
120                                         'x',
121                                         'y',
122                                 ], 4)
123     }
124 }
125 
126 Vector.prototype.resize = function(){
127     this.length = length(A, B);
128 }
129 
130 Vector.prototype.plus = function(B){
131     this.begin.plus(B.begin);
132     this.end.plus(B.end);
133     this.resize();
134     
135     return this;
136 }
137 
138 function plusVector(A, B)
139 {
140     return new Vector(plusCoor(A.begin, B.begin), plusCoor(A.end, B.end));
141 }
142 
143 function plusCoor(A, B)
144 {
145     return new Coor(A.x + B.x, A.y + B.y);
146 }
147           
148 function minusCoor(A, B)
149 {
150     return new Coor(A.x - B.x, A.y - B.y);
151 }
152 
153 function timesCoor(e, k)
154 {
155     return new Coor(k * e.x, k * e.y);
156 }    
157 function deltaCoor(e)
158 {
159     return Math.sqrt(dot(e,e));
160 } 
161 
162 function setCoorLink(a, b, length)
163 {
164   	
165     this.el.css('top', a.y);
166     this.el.css('left', a.x);
167 	this.el.css('height', length);
168 	var angle = 0;
169   	var deltaX = b.x - a.x;
170   	var deltaY = b.y - a.y;
171   
172   	if (deltaX == 0)
173 		angle = ((deltaY > 0) - (deltaY < 0)) * Math.PI / 2;
174 	else
175         if(deltaX < 0) 
176         	angle = ((deltaY >= 0) - (deltaY < 0)) * Math.PI + Math.atan(deltaY / deltaX);
177         else angle = Math.atan(deltaY / deltaX);
178 
179    angle -= Math.PI/2;
180    this.el.css('transform', 'rotate(' + angle + 'rad)');
181 }

Файл "console.js"

  1 function Console(par, pl)
  2 {
  3     this.player = pl;
  4     this.btn = $(par.btn);
  5     this.input = $(par.inp);
  6     this.txt = $(par.log);
  7     this.history = new Array();
  8     this.oHistory = new Array();
  9     var exec = this.execute.bind(this);
 10     var keydown = keydownCons.bind(this);
 11     
 12     this.btn.click(function(){
 13         exec();
 14     });
 15     
 16     this.input.keydown(keydown);
 17 }
 18 
 19 function keydownCons(e)
 20 {
 21     var e = e || window.event;
 22     var key = e.which;
 23         
 24     if (key == 38)
 25     {
 26         this.input.val(this.getHist());
 27     }
 28     
 29     if (key == 40)
 30     {
 31         this.input.val(this.getHistInv());
 32     }
 33     
 34     if (e.ctrlKey && key == 13)
 35     {
 36         this.input.val(this.input.val() + '---');
 37         
 38         return ;
 39     }
 40         
 41     if (key == 13)
 42     {
 43         this.btn.trigger('click');
 44     }
 45 }
 46 
 47 Console.prototype.log = function(str){
 48     var el = $('<div></div>').addClass('log').html(str.toString().trim()).click(showAllLog);
 49     
 50     this.txt.prepend(el);
 51 };
 52 
 53 function showAllLog()
 54 {
 55     $(this).addClass('all');
 56 }
 57 
 58 Console.prototype.warn = function(str){
 59     var el = $('<div></div>').addClass('warn').html(str.toString().trim()).click(showAllLog);
 60     
 61     this.txt.prepend(el);
 62 };
 63 
 64 Console.prototype.error = function(str){
 65     var el = $('<div></div>').addClass('err').html(str.toString().trim()).click(showAllLog);
 66     
 67     this.txt.prepend(el);
 68 };
 69 
 70 Console.prototype.logAns = function(str){
 71     var el = $('<div></div>').addClass('log').html('> ' + str.toString().trim()).click(showAllLog);
 72     
 73     this.txt.prepend(el);
 74 };
 75 
 76 Console.prototype.warnAns = function(str){
 77     var el = $('<div></div>').addClass('warn').html('> ' + str.toString().trim()).click(showAllLog);
 78     
 79     this.txt.prepend(el);
 80 };
 81 
 82 Console.prototype.errorAns = function(str){
 83     var el = $('<div></div>').addClass('err').html('> ' + str.toString().trim()).click(showAllLog);
 84     
 85     this.txt.prepend(el);
 86 };
 87 
 88 Console.prototype.saveHist = function(txt){
 89     while(this.oHistory.length)
 90     {
 91         this.history.push(this.oHistory.shift());
 92     }
 93     
 94     this.history.push(txt);
 95 };
 96 
 97 Console.prototype.getHist = function(){
 98     var tmp = this.history.pop();
 99     if (!tmp || tmp.length < 3)
100     {
101         return '';
102     }
103     this.oHistory.unshift(tmp);
104     
105     return tmp;  
106 };
107 
108 Console.prototype.getHistInv = function(){
109     var tmp = this.oHistory.shift();
110     if (!tmp || tmp.length < 3)
111     {
112         return '';
113     }
114     this.history.push(tmp);
115     
116     return tmp;  
117 };
118 
119 Console.prototype.execute = function(){
120     var txt = this.input.val().trim();
121     
122     if (txt === '')
123     {
124         return ;
125     }
126     
127     
128     if (txt === 'getHistory')
129     {
130 		this.input.val('');
131 		
132 	    var string = '';
133 	    
134 	    if(this.history.length > 0){
135 		    var historyLength = this.history.length;
136 		    
137 		    for(var i = 0; i < historyLength; ++i){
138 			    console.log(this.history[i] + '---');
139 			    if(this.history[i] != undefined)
140 		    		string += this.history[i] + '---';
141 		    }
142 			string = string.slice(0,-3);
143 	    }
144 	    	
145 	    if(this.history.length == 0)
146 	    	string = 'History is empty';
147 	    	
148 	    alert(string);
149 
150 	    return 0;
151     }
152     
153     this.log(txt);
154     
155     if(!this.player.parse(txt))
156     {
157         this.input.val('');
158         
159         this.saveHist(txt);
160     }
161     else
162     {
163         this.warnAns('Can not disassemble');
164     }
165 };

Файл "phen.css"

  1 *{margin:0;padding:0;}
  2 body{background: gainsboro;}
  3 .point{
  4     position: absolute;
  5     z-index: 20;
  6     counter-increment: num;
  7     text-align: center;
  8     border: 4px solid white;
  9     background: dodgerblue;
 10 }
 11 .point:before{
 12     content: counter(num);
 13     color: white;
 14     -webkit-user-select: 	none;
 15     -khtml-user-select: 	none;
 16     -moz-user-select: 		none;
 17     -ms-user-select: 		none;
 18     -o-user-select: 		none;
 19     user-select: 			none;
 20 }
 21 #screen{
 22     position: fixed;
 23     z-index: 2;
 24     top:27px;
 25     counter-reset: num;
 26     display: block;
 27     overflow-x: hidden;
 28     width: 100%;
 29     height: auto;
 30     bottom:0;
 31     background: transparent;
 32 }
 33 /*#screen:after{
 34   	position:absolute;
 35 	content: '';
 36     display: block;
 37 	height:596px;
 38 	width:1196px;
 39 	border: 2px solid crimson;
 40 }*/
 41 #btns input
 42 {
 43     font-size: 18px;
 44     position: fixed;
 45     z-index: 2;
 46     overflow: hidden;
 47     width: 25%;
 48     white-space: nowrap;
 49     text-overflow: ellipsis;
 50     color: white;
 51     border: 3px solid white;
 52     border-right: none;
 53     border-radius: none!important;
 54     outline: none!important;
 55     background: dodgerblue;
 56 }
 57 #btns input.hide{display:none;}
 58 #btns input:nth-child(2){left:25%;}
 59 #btns input:nth-child(3){left:50%;}
 60 #btns input:nth-child(4){left:50%;}
 61 #btns input:last-child
 62 {
 63     left: 75%;
 64     border: 3px solid white;
 65 }
 66 #btns input.notActive{}
 67 #btns input:active,
 68 #btns input.active
 69 {
 70     background: crimson!important;
 71 }
 72 
 73 #btns input[disabled],
 74 #btns input[disabled]:active
 75 {
 76     background: gray!important;
 77 }
 78 .wall{
 79     left: 252.5px;
 80     top: 227.5px;
 81     height:0px;
 82     width:500px;
 83     z-index:1000;
 84     margin-top:-2px;
 85     position: absolute;
 86     /*  background: url('./wall.png') repeat-x;*/
 87     -webkit-transform: 	rotate(180deg);
 88     -khtml-transform: 	rotate(180deg);
 89        -moz-transform: 	rotate(180deg);
 90        -ms-transform: 	rotate(180deg);
 91         -o-transform: 	rotate(180deg);
 92            transform: 	rotate(180deg);
 93     /*border-top: 2px solid crimson;*/
 94 }
 95 .stick,.spring{
 96 	background: white;
 97 	width: 2px;
 98 	position:absolute;
 99 	-webkit-transform-origin: 	50% 0;
100 	-khtml-transform-origin: 	50% 0;
101 	   -moz-transform-origin: 	50% 0;
102 	    -ms-transform-origin: 	50% 0;
103 	     -o-transform-origin: 	50% 0;
104 	        transform-origin: 	50% 0;
105 }
106 .spring{
107 	background: url('./spring.png') no-repeat;
108 	background-size: 10px 100%;
109   	width:10px;margin-left:-5px;
110 }
111 input[disabled],
112 input[disabled]:active{
113     background: gray!important;
114 }
115 #stack {
116     width: 130px;
117     height: 260px;
118     border: solid 1px black;
119     border-top: 0px;
120 }
121 
122 #stack .brick {
123     width: 11px;
124     height: 11px;
125     border: solid 1px white;
126     background: white;
127     float: left;
128 }
129 #stack .brick.on {
130     background: black;
131 }
132 #stack .brick.now {
133     background: green;
134 }
135 
136 #controls{
137     position: fixed;
138     z-index: 22;
139     bottom: 5px;
140     left: 5px;
141     width: 165px;
142     height: 30px;
143     padding: 15px;
144     -webkit-transition: all 400ms;
145     -khtml-transition: 	all 400ms;
146        -moz-transition: all 400ms;
147          -o-transition: all 400ms;
148             transition: all 400ms;
149     opacity: .5;
150     border: 3px solid #eee;
151     border-radius: 20px;
152     background-color: dodgerblue;
153 }
154 #controls.show{opacity: 1;}
155 #controls:before{
156     position: absolute;
157     top: -2px;
158     left: -2px;
159     display: block;
160     width: 334px;
161     height: 65px;
162     content: '';
163 }
164 #playerBtns{
165     position: relative;
166     left: 30px;
167 }
168 #playerBtns span{
169     position: absolute;
170     top: 4px;
171 }
172 #controls[type='0'] #playerBtns span:nth-of-type(1){
173     top: 0;
174     width: 0;
175     height: 0;
176     border-width: 15px 0 15px 30px;
177     border-style: solid;
178     border-color: transparent transparent transparent white;
179 }
180 #controls[type='1'] #playerBtns span:nth-of-type(1){
181     top: 0;
182     right: 169px!important;
183     width: 11px;
184     height: 30px;
185     border-right: 8px solid crimson;
186     border-left: 8px solid crimson;
187 }
188 #playerBtns span:nth-of-type(2){
189     width: 0;
190     height: 0;
191     border-width: 10px 0 10px 20px;
192     border-style: solid;
193     border-color: transparent transparent transparent white;
194 }
195 #playerBtns span:nth-of-type(3){
196     width: 0;
197     height: 0;
198     border-width: 10px 0 10px 20px;
199     border-style: solid;
200     border-color: transparent transparent transparent white;
201 }
202 #playerBtns span:nth-of-type(3):after{
203     position: absolute;
204     top: -10px;
205     width: 0;
206     height: 0;
207     content: '';
208     border-width: 10px 0 10px 20px;
209     border-style: solid;
210     border-color: transparent transparent transparent white;
211 }
212 #playerBtns span:nth-of-type(4){
213     width: 0;
214     height: 0;
215     border-width: 10px 0 10px 20px;
216     border-style: solid;
217     border-color: transparent transparent transparent white;
218 }
219 #playerBtns span:nth-of-type(4):after{
220     position: absolute;
221     top: -10px;
222     width: 0;
223     height: 0;
224     content: '';
225     border-width: 10px 2px;
226     border-style: solid;
227     border-color: white;
228 }
229 #playerBtns span:nth-of-type(1){right: 165px;}
230 #playerBtns span:nth-of-type(2){right: 130px;}
231 #playerBtns span:nth-of-type(3){right: 90px;}
232 #playerBtns span:nth-of-type(4){right: 35px;}
233 #consoleWindow{
234 	position: fixed;
235 	top: 30px;
236 	z-index: 2;
237 	right: 0;
238 	width: 25%;
239 	font-weight: bold;
240 	font-size: 14px;
241 	overflow: hidden;
242 	max-height: 339px;
243 	background: white;
244 }
245 #consoleWindow.hide{height:0;}
246 #consoleWindow.show{height: auto;}
247 #consoleInput{border-bottom: 3px solid gainsboro;}
248 #consoleInput:before{
249 	content: '>';
250 	font-size: 14px;
251 	position: absolute;
252 	top: 4px;
253 	left: 4px;
254 }
255 #consoleInput [type="text"]{
256 	border: none!important;
257 	outline: none!important;
258 	height: 20px;
259 	font-size: 14px;
260 	width: 100%;
261 	box-sizing:border-box;
262 	padding-left: 15px;
263 }
264 #consoleInput [type="button"]{
265 	font-size: 18px;
266     overflow: hidden;
267     width: 100%;
268     right: 0;
269     white-space: nowrap;
270     text-overflow: ellipsis;
271     color: white;
272     border: 3px solid white;
273     border-radius: none!important;
274     outline: none!important;
275     background: dodgerblue;
276 }
277 #consoleInput [type="button"]:active{background: crimson!important;}
278 #consoleLog{
279 	width: 100%;
280 	max-height: 290px;
281 	box-sizing: border-box;
282 	height: auto;
283 	word-wrap: break-word;
284 	overflow-y: auto;
285 	overflow-x: hidden;
286 	background: white;
287 	padding-left: 3px;
288 	padding-bottom: 10px;
289 }
290 
291 #consoleLog div:not(.all){
292 	text-overflow: ellipsis;
293 	overflow: hidden;
294 	max-height: 37px;
295 	width: 100%;
296 	white-space: nowrap;
297 }
298 
299 #consoleLog .warn{color: #DAA520;}
300 #consoleLog .err{color: crimson;}
301 #consoleLog .log{color: grey;}

Файл "index.php"

 1 <!DOCTYPE html>
 2 <html lang="ru">
 3 <head>
 4    	<title>Phen v2.0 a</title>
 5    	<link rel="stylesheet" href="./phen.css">
 6     <script type="text/javascript" src="./jquery-2.1.3.min.js"></script>
 7     <script type="text/javascript" src="./jquery-css-transform.js"></script>
 8     <script type="text/javascript" src="./point.js"></script>
 9     <script type="text/javascript" src="./spring.js"></script>
10     <script type="text/javascript" src="./stick.js"></script>
11     <script type="text/javascript" src="./func.js"></script>
12 	<script type="text/javascript" src="./renderer.js"></script>
13   	<script type="text/javascript" src="./wall.js"></script>
14   	<script type="text/javascript" src="./phen.js"></script>
15     <script type="text/javascript" src="./parser.js"></script>
16 	<script type="text/javascript" src="./console.js"></script>
17   	<script type="text/javascript" src="./player.js"></script>
18   	<script type="text/javascript" src="./cycle.js"></script>
19     <script type="text/javascript" src="./clean.js"></script>
20 </head>
21 <body>
22     <div id="screen"></div>
23     <div id="btns">
24         <input type="button" value="Open/Save" disabled="disabled">
25         <input id="getPoint" type="button" value="Get point's id">
26       	<input id="movePoint" type="button" value="Move point">
27       	<input id="demovePoint" type="button" class="active hide" value="Move point">
28       	<input id="consoleOpen" class="active" type="button" value="Console">
29     </div>
30     <div id="consoleWindow" class="show">
31 	    <div id="consoleInput">
32 	    	<input id="console" type="text" autofocus="autofocus">
33 			<input id="consoleBtn" type="button" value="Enter">
34 	    </div>
35 	    <div id="consoleLog">
36 	    </div>
37     </div>
38 	<div id="controls" class="show" type="0">
39       <div id="playerBtns">
40         <span id="stop"></span>
41         <span id="stepMin"></span>
42         <span id="stepMid"></span>
43         <span id="stepMax"></span>
44       </div>
45 	</div>
46 </body>
47 </html>

Обсуждение результатов и выводы


Скачать отчет:
Скачать презентацию:

Ссылки по теме

T, Jakobsen. "Advanced Character Physics", 2003. (перевод статьи )

Л. Ландау, Е. Лифшиц, "Теоретическая физика", том первый, "Механика", 2001.

А, Смирнов. "Курсовой проект: молекула углекислого газа", 2015. (страница проекта)

См. также

Примечания

  1. Стержни рассчитываются на расстяжение/сжатие методом коррекции координат.
    Действие пружин учитывается как действие сил упругости.
  2. 2,0 2,1 2,2 Без выделения жирным написаны необязательные параметры. При желании указать необязательный параметр все значения слева от него следует считать обязательными (во избежание путаницы при парсинге безразмерных величин в команде).
  3. Значения по умолчанию: вектор скорости нулевой, радиус равен 50 пикселям, масса равна 5 у. е.
  4. 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 Идентификационный номер элемента в системе. Генерируется последовательно, начиная с единицы, для стенок, стержней, пружин и частиц. Для частиц значение id можно найти нажатием сначала на кнопку "Get point's id", а затем на частицу: тогда Id отобразится в консоли.
  5. 5,0 5,1 5,2 5,3 Важно! Визуально действие этой команды применится только при перерисовке кадра.
  6. Жёсткость пружины по умолчанию равна 5 у. е.
  7. Запросы выводятся в всплывающем окне одной цельной командой, для воспроизведения цепочки запросов достаточно ввести эту команду в консоль.
    Важно! Учитываются только запросы, успешно введённые в консоль. Воздействия на систему при помощи кнопки "Move point" будут потеряны.
  8. При малой производительности клиента уменьшаем число отрисовок в единицу времени для сохранения гладкости анимации. Управлятся через консоль.