top of page
Search
cedarcantab

sopiro motor constranit



import { Matrix2, Vector2 } from "./math.js";
import { RigidBody } from "./rigidbody.js";
import { Settings } from "./settings.js";
import * as Util from "./util.js";
import { Joint } from "./joint.js";

// Revolute joint + Angle joint + limited force (torque)
export class MotorJoint extends Joint
{
    public localAnchorA: Vector2;
    public localAnchorB: Vector2;

    public initialAngleOffset: number;
    public linearOffset: Vector2;
    public angularOffset: number;

    private _maxForce: number;
    private _maxTorque: number;

    private ra!: Vector2;
    private rb!: Vector2;
    private m0!: Matrix2;
    private m1!: number;
    private bias0!: Vector2;
    private bias1!: number;
    private linearImpulseSum: Vector2 = new Vector2();
    private angularImpulseSum: number = 0.0;

    constructor(
        bodyA: RigidBody, bodyB: RigidBody,
        anchor: Vector2 = bodyB.position,
        maxForce = 1000.0, maxTorque = 1000.0,
        frequency = 60, dampingRatio = 1.0, jointMass = -1
    )
    {
        super(bodyA, bodyB, frequency, dampingRatio, jointMass);

        this.linearOffset = new Vector2();
        this.initialAngleOffset = bodyB.rotation - bodyA.rotation;
        this.angularOffset = 0.0;

        this._maxForce = Util.clamp(maxForce, 0, Number.MAX_VALUE);
        this._maxTorque = Util.clamp(maxTorque, 0, Number.MAX_VALUE);;

        this.localAnchorA = this.bodyA.globalToLocal.mulVector2(anchor, 1);
        this.localAnchorB = this.bodyB.globalToLocal.mulVector2(anchor, 1);

        this.drawConnectionLine = false;
    }

    override prepare()
    {
        // Calculate Jacobian J and effective mass M
        // J = [-I, -skew(ra), I, skew(rb)] // Revolute
        //     [ 0,        -1, 0,        1] // Angle
        // M = (J · M^-1 · J^t)^-1

        this.ra = this.bodyA.localToGlobal.mulVector2(this.localAnchorA, 0);
        this.rb = this.bodyB.localToGlobal.mulVector2(this.localAnchorB, 0);

        let k0 = new Matrix2();

        k0.m00 = this.bodyA.inverseMass + this.bodyB.inverseMass +
            this.bodyA.inverseInertia * this.ra.y * this.ra.y + this.bodyB.inverseInertia * this.rb.y * this.rb.y;

        k0.m01 = -this.bodyA.inverseInertia * this.ra.y * this.ra.x - this.bodyB.inverseInertia * this.rb.y * this.rb.x;

        k0.m10 = -this.bodyA.inverseInertia * this.ra.x * this.ra.y - this.bodyB.inverseInertia * this.rb.x * this.rb.y;

        k0.m11 = this.bodyA.inverseMass + this.bodyB.inverseMass
            + this.bodyA.inverseInertia * this.ra.x * this.ra.x + this.bodyB.inverseInertia * this.rb.x * this.rb.x;

        k0.m00 += this.gamma;
        k0.m11 += this.gamma;

        let k1 = (this.bodyA.inverseInertia + this.bodyB.inverseInertia + this.gamma);

        this.m0 = k0.inverted();
        this.m1 = 1.0 / k1;

        let pa = this.bodyA.position.add(this.ra);
        let pb = this.bodyB.position.add(this.rb);

        let error0 = pb.sub(pa.add(this.linearOffset));
        let error1 = this.bodyB.rotation - this.bodyA.rotation - this.initialAngleOffset - this.angularOffset;

        if (Settings.positionCorrection)
        {
            this.bias0 = new Vector2(error0.x, error0.y).mul(this.beta * Settings.inv_dt);
            this.bias1 = error1 * this.beta * Settings.inv_dt;
        }
        else
        {
            this.bias0 = new Vector2(0.0, 0.0);
            this.bias1 = 0.0;
        }

        if (Settings.warmStarting)
            this.applyImpulse(this.linearImpulseSum, this.angularImpulseSum);
    }

    override solve()
    {
        // Calculate corrective impulse: Pc
        // Pc = J^t * λ (λ: lagrangian multiplier)
        // λ = (J · M^-1 · J^t)^-1 ⋅ -(J·v+b)

        let jv0 = this.bodyB.linearVelocity.add(Util.cross(this.bodyB.angularVelocity, this.rb))
            .sub(this.bodyA.linearVelocity.add(Util.cross(this.bodyA.angularVelocity, this.ra)));
        let jv1 = this.bodyB.angularVelocity - this.bodyA.angularVelocity;

        let lambda0 = this.m0.mulVector(jv0.add(this.bias0).add(this.linearImpulseSum.mul(this.gamma)).inverted());
        let lambda1 = this.m1 * -(jv1 + this.bias1 + this.angularImpulseSum * this.gamma);

        // Clamp linear impulse
        {
            let maxLinearImpulse = Settings.dt * this._maxForce;
            let oldLinearImpulse = this.linearImpulseSum.copy();
            this.linearImpulseSum = this.linearImpulseSum.add(lambda0);

            if (this.linearImpulseSum.length > maxLinearImpulse)
                this.linearImpulseSum = this.linearImpulseSum.normalized().mul(maxLinearImpulse);

            lambda0 = this.linearImpulseSum.sub(oldLinearImpulse);
        }

        // Clamp angular impulse
        {
            let maxAngularImpulse = Settings.dt * this._maxTorque;
            let oldAngularImpulse = this.angularImpulseSum;
            this.angularImpulseSum += lambda1;

            this.angularImpulseSum = Util.clamp(this.angularImpulseSum, -maxAngularImpulse, maxAngularImpulse);

            lambda1 = this.angularImpulseSum - oldAngularImpulse;
        }

        this.applyImpulse(lambda0, lambda1);
    }

    protected applyImpulse(lambda0: Vector2, lambda1: number)
    {
        // V2 = V2' + M^-1 ⋅ Pc
        // Pc = J^t ⋅ λ

        // Solve for point-to-point constraint
        this.bodyA.linearVelocity = this.bodyA.linearVelocity.sub(lambda0.mul(this.bodyA.inverseMass));
        this.bodyA.angularVelocity = this.bodyA.angularVelocity - this.bodyA.inverseInertia * this.ra.cross(lambda0);
        this.bodyB.linearVelocity = this.bodyB.linearVelocity.add(lambda0.mul(this.bodyB.inverseMass));
        this.bodyB.angularVelocity = this.bodyB.angularVelocity + this.bodyB.inverseInertia * this.rb.cross(lambda0);

        // Solve for angle constraint
        this.bodyA.angularVelocity = this.bodyA.angularVelocity - lambda1 * this.bodyA.inverseInertia;
        this.bodyB.angularVelocity = this.bodyB.angularVelocity + lambda1 * this.bodyB.inverseInertia;
    }
    
    get maxForce(): number
    {
        return this._maxForce;
    }

    set maxForce(maxForce: number)
    {
        this._maxForce = Util.clamp(maxForce, 0, Number.MAX_VALUE);
    }
        
    get maxTorque(): number
    {
        return this._maxTorque;
    }

    set maxTorque(maxTorque: number)
    {
        this._maxTorque = Util.clamp(maxTorque, 0, Number.MAX_VALUE);
    }
}

demo



Reflect.set(demo18, "SimulationName", "Motor joint: Windmill");
function demo18(game: Game, world: World): void
{
    updateSetting("g", true);

    let b1 = new Box(0.2, 3.2, Type.Static);
    b1.position.y = 1.5;
    world.register(b1);

    let b2 = new Box(2.0, 0.2);
    b2.position.y = 3.0;
    world.register(b2);

    let j = new MotorJoint(b1, b2, b2.position, 1000, 10.0);
    world.register(j, true);

    let last_spawn = 0;

    game.callback = () =>
    {
        j.angularOffset = b2.rotation + 0.05;

        if ((game.frame - last_spawn) / Engine.fps > 0.2 * 144 * game.deltaTime)
        {
            let c = Util.createRegularPolygon(0.15, undefined, undefined, 18.0);

            c.position.x = Util.random(-1.8, 1.8);
            c.position.y = 6.0;
            world.register(c);
            last_spawn = game.frame;
        }
    }
}


3 views0 comments

Recent Posts

See All

p2 naive broadphase

var Broadphase = require('../collision/Broadphase'); module.exports = NaiveBroadphase; /** * Naive broadphase implementation. Does N^2...

Comentarios


記事: Blog2_Post
bottom of page