mouse constraints. It is a very easy constraint to implement, and you don’t need any broad phase or narrow phase, just the resolution phase and integration phase. The constraint is simple: we want the position difference between an anchor point on a rigid body and the mouse position to be zero. We differentiate this equation with respect to time, and we get “C dot”. We let the mouse position be considered as a constant, so its derivative is zero. With “C dot”, we can easily write out the Jacobian. It forces the anchor point on the constrained rigid body to have zero relative velocity in world space. The bias term uses Baumgarte stabilization to correct positional errors. The effective mass matrix is still using the same formula “J M inverse J t”. Note that this is a multi-constraint with 3 constraints, so the effective mass matrix is a 3-by-3 matrix. During calculation, the “mass of the mouse cursor” should be treated as infinity, since it will not be moved by the constraint at all
can tie string
esesntially a distance joint between a body , whih is between 2 bodies, but insteadm body and mouse.
as the ouse move around, body gets tagged around.
need 2 bodies ready to go! body connected to mouse. one body is easy. where is the second body? new concept called ground body. The gruond body is going to get a target. target is the one that is oging to move around.
spring like parameters.
different from other joints - in our main program, when we press the mouse, want to create the joint.
when mouse button is released, destroy the joint. as the mouse moves around (+ continues to be pressed), update the target.
so when mouse is pressed - need to create the joine;. say you create a function which is aclled when mouse is pressed:
void bind(float x, float y, Box box)
Mousejointdef md = new MosueJointDef()
vec2 mp = box2d.coordPixelsToWorld(x,y)
md.target.set(np)
mx.maxForce = 5000*box.body.m_mass
md.frequencyHz - 5;
md.dampingRatio = 0.5;:
and the target needs to be updated when the mouse is moved
void update(float x, float y)
if (mouseJoint !=null) {
vec2 mouseWorld = box2d.coordPixelsToWorld(x,y);
mouseJoint.setTarget(mouseWorld)
}
}
and when the mouse is released we must destroy the mousejoint - make it null, so that the main loop knows.
void destroy() {
if (mouseJoint != null) {
box2d.world.destroyJoint(mouseJoint)
mouseJoint = null
}
class GrabJoint extends Joint {
constructor(body, anchor, target, frequency = 0.8, dampingRatio = 0.6, jointMass = -1) {
super(body, body, frequency, dampingRatio, jointMass);
this.impulseSum = new Vector2();
this.localAnchor = body.globalToLocal.mulVector2(anchor, 1);
this.target = target;
}
preStep()
preStep(inv_dt) {
// Calculate Jacobian J and effective mass M
// J = [I, skew(r)]
// M = (J · M^-1 · J^t)^-1
this.r = this.body1.localToGlobal.mulVector2(this.localAnchor, 0);
let p = this.body1.position.add(this.r);
let k = new Matrix2();
k.m00 = this.body1.inverseMass + this.body1.inverseInertia * this.r.y * this.r.y;
k.m01 = -this.body1.inverseInertia * this.r.y * this.r.x;
k.m10 = -this.body1.inverseInertia * this.r.x * this.r.y;
k.m11 = this.body1.inverseMass + this.body1.inverseInertia * this.r.x * this.r.x;
k.m00 += this.gamma;
k.m11 += this.gamma;
this.m = k.inverted();
let error = p.sub(this.target);
if (Settings.positionCorrection) {
this.bias = error.mul(this.beta * inv_dt);
}
else
{
this.bias = new Vector2(0.0, 0.0);
}
if (Settings.warmStarting) {
this.applyImpulse(this.impulseSum);
}
}
solve()
solve() {
// Calculate corrective impulse: Pc
// Pc = J^t · λ (λ: lagrangian multiplier)
// λ = (J · M^-1 · J^t)^-1 ⋅ -(J·v+b)
let jv = this.body1.linearVelocity.add(Util.cross(this.body1.angularVelocity, this.r));
let lambda = this.m.mulVector(jv.add(this.bias).add(this.impulseSum.mul(this.gamma)).inverted());
this.applyImpulse(lambda);
if (Settings.warmStarting)
this.impulseSum = this.impulseSum.add(lambda);
}
BOX2D
// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "box2d/b2_body.h"
#include "box2d/b2_mouse_joint.h"
#include "box2d/b2_time_step.h"
// p = attached point, m = mouse point
// C = p - m
// Cdot = v
// = v + cross(w, r)
// J = [I r_skew]
// Identity used:
// w k % (rx i + ry j) = w * (-ry i + rx j)
b2MouseJoint::b2MouseJoint(const b2MouseJointDef* def)
: b2Joint(def)
{
m_targetA = def->target;
m_localAnchorB = b2MulT(m_bodyB->GetTransform(), m_targetA);
m_maxForce = def->maxForce;
m_stiffness = def->stiffness;
m_damping = def->damping;
m_impulse.SetZero();
m_beta = 0.0f;
m_gamma = 0.0f;
}
void b2MouseJoint::SetTarget(const b2Vec2& target)
{
if (target != m_targetA)
{
m_bodyB->SetAwake(true);
m_targetA = target;
}
}
const b2Vec2& b2MouseJoint::GetTarget() const
{
return m_targetA;
}
void b2MouseJoint::SetMaxForce(float force)
{
m_maxForce = force;
}
float b2MouseJoint::GetMaxForce() const
{
return m_maxForce;
}
void b2MouseJoint::InitVelocityConstraints(const b2SolverData& data)
{
m_indexB = m_bodyB->m_islandIndex;
m_localCenterB = m_bodyB->m_sweep.localCenter;
m_invMassB = m_bodyB->m_invMass;
m_invIB = m_bodyB->m_invI;
b2Vec2 cB = data.positions[m_indexB].c;
float aB = data.positions[m_indexB].a;
b2Vec2 vB = data.velocities[m_indexB].v;
float wB = data.velocities[m_indexB].w;
b2Rot qB(aB);
float d = m_damping;
float k = m_stiffness;
// magic formulas
// gamma has units of inverse mass.
// beta has units of inverse time.
float h = data.step.dt;
m_gamma = h * (d + h * k);
if (m_gamma != 0.0f)
{
m_gamma = 1.0f / m_gamma;
}
m_beta = h * k * m_gamma;
// Compute the effective mass matrix.
m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
// K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)]
// = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y]
// [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
b2Mat22 K;
K.ex.x = m_invMassB + m_invIB * m_rB.y * m_rB.y + m_gamma;
K.ex.y = -m_invIB * m_rB.x * m_rB.y;
K.ey.x = K.ex.y;
K.ey.y = m_invMassB + m_invIB * m_rB.x * m_rB.x + m_gamma;
m_mass = K.GetInverse();
m_C = cB + m_rB - m_targetA;
m_C *= m_beta;
// Cheat with some damping
wB *= b2Max(0.0f, 1.0f - 0.02f * (60.0f * data.step.dt));
if (data.step.warmStarting)
{
m_impulse *= data.step.dtRatio;
vB += m_invMassB * m_impulse;
wB += m_invIB * b2Cross(m_rB, m_impulse);
}
else
{
m_impulse.SetZero();
}
data.velocities[m_indexB].v = vB;
data.velocities[m_indexB].w = wB;
}
void b2MouseJoint::SolveVelocityConstraints(const b2SolverData& data)
{
b2Vec2 vB = data.velocities[m_indexB].v;
float wB = data.velocities[m_indexB].w;
// Cdot = v + cross(w, r)
b2Vec2 Cdot = vB + b2Cross(wB, m_rB);
b2Vec2 impulse = b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse));
b2Vec2 oldImpulse = m_impulse;
m_impulse += impulse;
float maxImpulse = data.step.dt * m_maxForce;
if (m_impulse.LengthSquared() > maxImpulse * maxImpulse)
{
m_impulse *= maxImpulse / m_impulse.Length();
}
impulse = m_impulse - oldImpulse;
vB += m_invMassB * impulse;
wB += m_invIB * b2Cross(m_rB, impulse);
data.velocities[m_indexB].v = vB;
data.velocities[m_indexB].w = wB;
}
bool b2MouseJoint::SolvePositionConstraints(const b2SolverData& data)
{
B2_NOT_USED(data);
return true;
}
b2Vec2 b2MouseJoint::GetAnchorA() const
{
return m_targetA;
}
b2Vec2 b2MouseJoint::GetAnchorB() const
{
return m_bodyB->GetWorldPoint(m_localAnchorB);
}
b2Vec2 b2MouseJoint::GetReactionForce(float inv_dt) const
{
return inv_dt * m_impulse;
}
float b2MouseJoint::GetReactionTorque(float inv_dt) const
{
return inv_dt * 0.0f;
}
void b2MouseJoint::ShiftOrigin(const b2Vec2& newOrigin)
{
m_targetA -= newOrigin;
}
// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef B2_MOUSE_JOINT_H
#define B2_MOUSE_JOINT_H
#include "b2_api.h"
#include "b2_joint.h"
/// Mouse joint definition. This requires a world target point,
/// tuning parameters, and the time step.
struct B2_API b2MouseJointDef : public b2JointDef
{
b2MouseJointDef()
{
type = e_mouseJoint;
target.Set(0.0f, 0.0f);
maxForce = 0.0f;
stiffness = 0.0f;
damping = 0.0f;
}
/// The initial world target point. This is assumed
/// to coincide with the body anchor initially.
b2Vec2 target;
/// The maximum constraint force that can be exerted
/// to move the candidate body. Usually you will express
/// as some multiple of the weight (multiplier * mass * gravity).
float maxForce;
/// The linear stiffness in N/m
float stiffness;
/// The linear damping in N*s/m
float damping;
};
/// A mouse joint is used to make a point on a body track a
/// specified world point. This a soft constraint with a maximum
/// force. This allows the constraint to stretch and without
/// applying huge forces.
/// NOTE: this joint is not documented in the manual because it was
/// developed to be used in the testbed. If you want to learn how to
/// use the mouse joint, look at the testbed.
class B2_API b2MouseJoint : public b2Joint
{
public:
/// Implements b2Joint.
b2Vec2 GetAnchorA() const override;
/// Implements b2Joint.
b2Vec2 GetAnchorB() const override;
/// Implements b2Joint.
b2Vec2 GetReactionForce(float inv_dt) const override;
/// Implements b2Joint.
float GetReactionTorque(float inv_dt) const override;
/// Use this to update the target point.
void SetTarget(const b2Vec2& target);
const b2Vec2& GetTarget() const;
/// Set/get the maximum force in Newtons.
void SetMaxForce(float force);
float GetMaxForce() const;
/// Set/get the linear stiffness in N/m
void SetStiffness(float stiffness) { m_stiffness = stiffness; }
float GetStiffness() const { return m_stiffness; }
/// Set/get linear damping in N*s/m
void SetDamping(float damping) { m_damping = damping; }
float GetDamping() const { return m_damping; }
/// The mouse joint does not support dumping.
void Dump() override { b2Log("Mouse joint dumping is not supported.\n"); }
/// Implement b2Joint::ShiftOrigin
void ShiftOrigin(const b2Vec2& newOrigin) override;
protected:
friend class b2Joint;
b2MouseJoint(const b2MouseJointDef* def);
void InitVelocityConstraints(const b2SolverData& data) override;
void SolveVelocityConstraints(const b2SolverData& data) override;
bool SolvePositionConstraints(const b2SolverData& data) override;
b2Vec2 m_localAnchorB;
b2Vec2 m_targetA;
float m_stiffness;
float m_damping;
float m_beta;
// Solver shared
b2Vec2 m_impulse;
float m_maxForce;
float m_gamma;
// Solver temp
int32 m_indexA;
int32 m_indexB;
b2Vec2 m_rB;
b2Vec2 m_localCenterB;
float m_invMassB;
float m_invIB;
b2Mat22 m_mass;
b2Vec2 m_C;
};
#endif
Comments