2012-02-24 12 views
6

I utilizan el siguiente código basado en thiscalcular la velocidad y la dirección de una pelota a la colisión bola basado en masa y rebotando coeficiente

ballA.vx = (u1x * (m1 - m2) + 2 * m2 * u2x)/(m1 + m2); 
ballA.vy = (u1y * (m1 - m2) + 2 * m2 * u2y)/(m1 + m2); 

ballB.vx = (u2x * (m2 - m1) + 2 * m1 * u1x)/(m1 + m2); 
ballB.vy = (u2y * (m2 - m1) + 2 * m1 * u1y)/(m1 + m2); 

pero, obviamente, no así como la fórmula está diseñado para unidimensional colisiones

Así que traté de usar la siguiente fórmula desde this section.

Pero el problema es que no sé lo que el ángulo de desviación es y cómo calcularlo. Además, ¿cómo tomar en cuenta el coeficiente de rebote en esta fórmula?

Editar: Puede que no haya sido claro. El código anterior realiza el trabajo, aunque puede no ser el comportamiento esperado, ya que la fórmula original está diseñada para colisiones 1D. Los temas que estoy tratando, por tanto, son los siguientes:

  • ¿Cuál es el 2D equivalente?
  • ¿Cómo tomar en cuenta el coeficiente de rebote ?
  • cómo calcular la dirección (que se expresa con v x y v y) de las dos bolas siguientes de la colisión?
+0

Parece que está utilizando fórmulas elásticas de colisión cuando y si va a preocuparse por los "coeficientes de rebote" desea ver colisiones inelásticas http://en.wikipedia.org/wiki/Inelastic_collision como las ecuaciones su utilizando no tienen ese concepto ya que "mantienen perfectamente toda la energía del sistema" – ckozl

+0

Resulta que tiene razón. Pero ahora, ¿cómo aplicar la fórmula dada a una colisión en 2D? – seriousdev

+0

en respuesta a la edición: No existe tal cosa como las colisiones "equivalentes en 2D" son de naturaleza lineal – ckozl

Respuesta

3

aquí es una demostración de una ecuación colisión inelástica en acción, por encargo para usted:

function BallObject(elasticity) { 
    this.v = { x: 1, y: 20 }; // velocity: m/s^2 
    this.m = 10; // mass: kg 
    this.p = { x: 40, y: 0}; // position 
    this.r = 15; // radius of obj 
    this.cr = elasticity; // elasticity 
} 

function draw(obj) { 
    ctx.beginPath(); 
    ctx.arc(obj.p.x, obj.p.y, obj.r, 0, 2 * Math.PI); 
    ctx.closePath(); 
    ctx.stroke(); 
    ctx.fill(); 
} 

function collide(obj) { 
    obj.v.y = (obj.cr * floor.m * -obj.v.y + obj.m * obj.v.y)/(obj.m + floor.m); 
} 

function update(obj, dt) { 

    // over-simplified collision detection 
    // only consider the floor for simplicity 
    if ((obj.p.y + obj.r) > c.height) { 
    obj.p.y = c.height - obj.r; 
    collide(obj); 
    } 

    obj.v.y += g * dt; 
    obj.p.x += obj.v.x * dt * ppm; 
    obj.p.y += obj.v.y * dt * ppm; 
} 

var d = document, 
    c = d.createElement('canvas'), 
    b = d.createElement('button'), 
    els = d.createElement('input'), 
    clr = d.createElement('input'), 
    clrl = d.createElement('label'), 
    ctx = c.getContext('2d'), 
    fps = 30, // target frames per second 
    ppm = 20, // pixels per meter 
    g = 9.8, // m/s^2 - acceleration due to gravity 
    objs = [], 
    floor = { 
     v: { x: 0, y: 0 }, // floor is immobile 
     m: 5.9722 * Math.pow(10, 24) // mass of earth (probably could be smaller) 
    }, 
    t = new Date().getTime(); 

b.innerHTML = 'add ball with elasticity: <span>0.70</span>'; 
b.onclick = function() { objs.push(new BallObject(els.value/100)); }; 

els.type = 'range'; 
els.min = 0; 
els.max = 100; 
els.step = 1; 
els.value = 70; 
els.style.display = 'block'; 
els.onchange = function() { 
    b.getElementsByTagName('span')[0].innerHTML = (this.value/100).toFixed(2); 
}; 

clr.type = 'checkbox'; 
clr.checked = true; 

clrl.appendChild(clr); 
clrl.appendChild(d.createTextNode('clear each frame')); 

c.style.border = 'solid 1px #3369ff'; 
c.style.borderRadius = '10px'; 
c.style.display = 'block'; 
c.width = 400; 
c.height = 400; 

ctx.fillStyle = 'rgb(100,200,255)'; 
ctx.strokeStyle = 'rgb(33,69,233)'; 

d.body.appendChild(c); 
d.body.appendChild(els); 
d.body.appendChild(b); 
d.body.appendChild(clrl); 

setInterval(function() { 

    var nt = new Date().getTime(), 
     dt = (nt - t)/1000; 

    if (clr.checked) { 
    ctx.clearRect(0, 0, c.width, c.height); 
    } 

    for (var i = 0; i < objs.length; i++) { 
    update(objs[i], dt); 
    draw(objs[i]); 
    } 

    t = nt; 

}, 1000/fps); 

para ver en acción a sí mismo, sólo tiene que ir aquí: http://jsbin.com/iwuxol/edit#javascript,live

Este utiliza esta ecuación : enter image description here

y como su "piso" no se mueve, solo tiene que considerar la influencia en la velocidad y la bola. Le importaría que hay un buen número de accesos directos y descuidos aquí así que esto es un motor de física muy primitiva, y está destinado principalmente para ilustrar esta ecuación ...

esperanza esto ayuda -CK

+0

¡Gracias por todo el trabajo! Pero creo que te perdiste el punto: ¿y las colisiones de bola a bola? – seriousdev

+0

@seriousdev, sinceramente, creo que tiene muchas lecturas por delante. pero se descompone así, los objetos solo interactúan entre sí con las velocidades en un solo conjunto de ejes transformados, el caso que ilustré para usted arriba es el más simple. un objeto debería ser [casi] no afectado por la colisión y el otro se está actuando directamente en el eje y. Y por favor no me digas que "no entendí el punto", simplemente no estás haciendo las preguntas correctas. Debes pensar en las velocidades angulares en términos de sus componentes vectoriales y tienes que vectorizar tus velocidades de modo que [..cont ..] – ckozl

+0

... la colisión se produce contra un único eje, lo que significa al girar un sistema de pseudocoordenadas para que el la velocidad que afecta el resultado del impacto recae en un solo eje. si tengo tiempo, quizás podría darte una demo más compleja pero aún fácil de seguir ...mientras tanto, piénsalo de esta manera: la fórmula no está "diseñada para colisiones unidimensionales", es solo que la colisión solo ocurre en una dimensión, ignorando la fricción, el impacto no afecta a ninguna otra dimensión, independientemente del ángulo de incidencia ... – ckozl

1

le recomiendo que se familiarice usted mismo con el center of momentum frame. Hace las colisiones mucho más fáciles de entender. (Y sin esa comprensión solo estás manipulando ecuaciones crípticas y nunca sabrás por qué las cosas van mal.)

De todos modos, para determinar el ángulo, puedes usar el parámetro de impacto, básicamente qué tan "fuera del centro" la pelota golpea a la otra. Las dos bolas se aproximan entre sí en direcciones opuestas (en el cuadro del centro del momento), y la distancia entre sus centros perpendicular a esas velocidades es el parámetro de impacto h. Entonces el ángulo de deflexión es 2 acos (h/(r + r)).

Una vez que funcione perfectamente, puede preocuparse por las colisiones inelásticas y el coeficiente de restitución.

11

que debería empezar diciendo: He creado una nueva respuesta porque siento el viejo tiene valor por su sencillez

según lo prometido aquí es un motor de física mucho más compleja, sin embargo, todavía siento que es bastante fácil de seguir (! esperemos o acabo perdido el tiempo ... lol), (url: http://jsbin.com/otipiv/edit#javascript,live)

function Vector(x, y) { 
    this.x = x; 
    this.y = y; 
} 

Vector.prototype.dot = function (v) { 
    return this.x * v.x + this.y * v.y; 
}; 

Vector.prototype.length = function() { 
    return Math.sqrt(this.x * this.x + this.y * this.y); 
}; 

Vector.prototype.normalize = function() { 
    var s = 1/this.length(); 
    this.x *= s; 
    this.y *= s; 
    return this; 
}; 

Vector.prototype.multiply = function(s) { 
    return new Vector(this.x * s, this.y * s); 
}; 

Vector.prototype.tx = function(v) { 
    this.x += v.x; 
    this.y += v.y; 
    return this; 
}; 

function BallObject(elasticity, vx, vy) { 
    this.v = new Vector(vx || 0, vy || 0); // velocity: m/s^2 
    this.m = 10; // mass: kg 
    this.r = 15; // radius of obj 
    this.p = new Vector(0, 0); // position 
    this.cr = elasticity; // elasticity 
} 

BallObject.prototype.draw = function(ctx) { 
    ctx.beginPath(); 
    ctx.arc(this.p.x, this.p.y, this.r, 0, 2 * Math.PI); 
    ctx.closePath(); 
    ctx.fill(); 
    ctx.stroke(); 
}; 

BallObject.prototype.update = function(g, dt, ppm) { 

    this.v.y += g * dt; 
    this.p.x += this.v.x * dt * ppm; 
    this.p.y += this.v.y * dt * ppm; 

}; 

BallObject.prototype.collide = function(obj) { 

    var dt, mT, v1, v2, cr, sm, 
     dn = new Vector(this.p.x - obj.p.x, this.p.y - obj.p.y), 
     sr = this.r + obj.r, // sum of radii 
     dx = dn.length(); // pre-normalized magnitude 

    if (dx > sr) { 
    return; // no collision 
    } 

    // sum the masses, normalize the collision vector and get its tangential 
    sm = this.m + obj.m; 
    dn.normalize(); 
    dt = new Vector(dn.y, -dn.x); 

    // avoid double collisions by "un-deforming" balls (larger mass == less tx) 
    // this is susceptible to rounding errors, "jiggle" behavior and anti-gravity 
    // suspension of the object get into a strange state 
    mT = dn.multiply(this.r + obj.r - dx); 
    this.p.tx(mT.multiply(obj.m/sm)); 
    obj.p.tx(mT.multiply(-this.m/sm)); 

    // this interaction is strange, as the CR describes more than just 
    // the ball's bounce properties, it describes the level of conservation 
    // observed in a collision and to be "true" needs to describe, rigidity, 
    // elasticity, level of energy lost to deformation or adhesion, and crazy 
    // values (such as cr > 1 or cr < 0) for stange edge cases obviously not 
    // handled here (see: http://en.wikipedia.org/wiki/Coefficient_of_restitution) 
    // for now assume the ball with the least amount of elasticity describes the 
    // collision as a whole: 
    cr = Math.min(this.cr, obj.cr); 

    // cache the magnitude of the applicable component of the relevant velocity 
    v1 = dn.multiply(this.v.dot(dn)).length(); 
    v2 = dn.multiply(obj.v.dot(dn)).length(); 

    // maintain the unapplicatble component of the relevant velocity 
    // then apply the formula for inelastic collisions 
    this.v = dt.multiply(this.v.dot(dt)); 
    this.v.tx(dn.multiply((cr * obj.m * (v2 - v1) + this.m * v1 + obj.m * v2)/sm)); 

    // do this once for each object, since we are assuming collide will be called 
    // only once per "frame" and its also more effiecient for calculation cacheing 
    // purposes 
    obj.v = dt.multiply(obj.v.dot(dt)); 
    obj.v.tx(dn.multiply((cr * this.m * (v1 - v2) + obj.m * v2 + this.m * v1)/sm)); 
}; 

function FloorObject(floor) { 
    var py; 

    this.v = new Vector(0, 0); 
    this.m = 5.9722 * Math.pow(10, 24); 
    this.r = 10000000; 
    this.p = new Vector(0, py = this.r + floor); 
    this.update = function() { 
     this.v.x = 0; 
     this.v.y = 0; 
     this.p.x = 0; 
     this.p.y = py; 
    }; 
    // custom to minimize unnecessary filling: 
    this.draw = function(ctx) { 
    var c = ctx.canvas, s = ctx.scale; 
    ctx.fillRect(c.width/-2/s, floor, ctx.canvas.width/s, (ctx.canvas.height/s) - floor); 
    }; 
} 

FloorObject.prototype = new BallObject(1); 

function createCanvasWithControls(objs) { 
    var addBall = function() { objs.unshift(new BallObject(els.value/100, (Math.random() * 10) - 5, -20)); }, 
     d = document, 
     c = d.createElement('canvas'), 
     b = d.createElement('button'), 
     els = d.createElement('input'), 
     clr = d.createElement('input'), 
     cnt = d.createElement('input'), 
     clrl = d.createElement('label'), 
     cntl = d.createElement('label'); 

    b.innerHTML = 'add ball with elasticity: <span>0.70</span>'; 
    b.onclick = addBall; 

    els.type = 'range'; 
    els.min = 0; 
    els.max = 100; 
    els.step = 1; 
    els.value = 70; 
    els.style.display = 'block'; 
    els.onchange = function() { 
    b.getElementsByTagName('span')[0].innerHTML = (this.value/100).toFixed(2); 
    }; 

    clr.type = cnt.type = 'checkbox'; 
    clr.checked = cnt.checked = true; 
    clrl.style.display = cntl.style.display = 'block'; 

    clrl.appendChild(clr); 
    clrl.appendChild(d.createTextNode('clear each frame')); 

    cntl.appendChild(cnt); 
    cntl.appendChild(d.createTextNode('continuous shower!')); 

    c.style.border = 'solid 1px #3369ff'; 
    c.style.display = 'block'; 
    c.width = 700; 
    c.height = 550; 
    c.shouldClear = function() { return clr.checked; }; 

    d.body.appendChild(c); 
    d.body.appendChild(els); 
    d.body.appendChild(b); 
    d.body.appendChild(clrl); 
    d.body.appendChild(cntl); 

    setInterval(function() { 
    if (cnt.checked) { 
     addBall(); 
    } 
    }, 333); 

    return c; 
} 

// start: 
var objs = [], 
    c = createCanvasWithControls(objs), 
    ctx = c.getContext('2d'), 
    fps = 30, // target frames per second 
    ppm = 20, // pixels per meter 
    g = 9.8, // m/s^2 - acceleration due to gravity 
    t = new Date().getTime(); 

// add the floor: 
objs.push(new FloorObject(c.height - 10)); 

// as expando so its accessible in draw [this overides .scale(x,y)] 
ctx.scale = 0.5; 
ctx.fillStyle = 'rgb(100,200,255)'; 
ctx.strokeStyle = 'rgb(33,69,233)'; 
ctx.transform(ctx.scale, 0, 0, ctx.scale, c.width/2, c.height/2); 

setInterval(function() { 

    var i, j, 
     nw = c.width/ctx.scale, 
     nh = c.height/ctx.scale, 
     nt = new Date().getTime(), 
     dt = (nt - t)/1000; 

    if (c.shouldClear()) { 
    ctx.clearRect(nw/-2, nh/-2, nw, nh); 
    } 

    for (i = 0; i < objs.length; i++) { 

    // if a ball > viewport width away from center remove it 
    while (objs[i].p.x < -nw || objs[i].p.x > nw) { 
     objs.splice(i, 1); 
    } 

    objs[i].update(g, dt, ppm, objs, i); 

    for (j = i + 1; j < objs.length; j++) { 
     objs[j].collide(objs[i]); 
    } 

    objs[i].draw(ctx); 
    } 

    t = nt; 

}, 1000/fps); 

la verdadera "carne" y el origen de esta discusión es el método obj.collide(obj).

si nos sumergimos (lo comenté esta vez ya que es mucho más complejo que el "último"), verá que esta ecuación: equation for inelastic collision, sigue siendo la única que se utiliza en esta línea: this.v.tx(dn.multiply((cr * obj.m * (v2 - v1) + this.m * v1 + obj.m * v2)/sm)); ahora Estoy seguro de que todavía dices: "zomg wtf! ¡Esa es la misma ecuación de dimensión única!" pero cuando te paras a pensarlo, una "colisión" solo ocurre en una sola dimensión. Por eso utilizamos ecuaciones vectoriales para extraer los componentes aplicables y aplicar las colisiones solo a esas partes específicas, dejando las otras intactas para seguir su camino feliz (ignorando la fricción y simplificando la colisión para no tener en cuenta las fuerzas dinámicas de transformación de energía descritas en comentarios para CR). Este concepto obviamente se vuelve más complicado a medida que aumenta la complejidad del objeto y aumenta el número de puntos de datos de escena para explicar cosas como deformidad, inercia rotacional, distribución desigual de la masa y puntos de fricción ... pero eso está más allá del alcance de esto. vale la pena mencionar ...

Básicamente, los conceptos que realmente necesita "captar" para que esto le resulte intuitivo son los conceptos básicos de las ecuaciones de Vector (todas ubicadas en el prototipo de Vector), cómo interactúan con cada uno (lo que realmente es significa normalizar, o tomar un producto escalar/punto, por ejemplo, leer/hablar con alguien entendido) y una comprensión básica de cómo las colisiones actúan sobre las propiedades de un objeto (masa, velocidad, etc.) otra vez, leer/hablar con alguien conocedor)

Espero que esto ayuda, buena suerte! -ck

Cuestiones relacionadas