diff --git a/websiteUsingBulma/.idea/workspace.xml b/websiteUsingBulma/.idea/workspace.xml index 2d8645a..de6d9f8 100644 --- a/websiteUsingBulma/.idea/workspace.xml +++ b/websiteUsingBulma/.idea/workspace.xml @@ -1,25 +1,123 @@ - + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + @@ -31,12 +129,15 @@ - + - + - - + + + + + @@ -44,44 +145,63 @@ - - + + - + - - + + - + + + + + + + + + + + - - + + - + + + + + + + + + + - - + + - + - - + + @@ -90,10 +210,10 @@ - + - - + + @@ -106,6 +226,9 @@ 14 10 0 + print + creatureCreator + fucking @@ -117,17 +240,24 @@ - @@ -143,6 +273,21 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1557388092387 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/ConnectionGene.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/ConnectionGene.js new file mode 100644 index 0000000..d54dc8b --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/ConnectionGene.js @@ -0,0 +1,47 @@ +//a connection between 2 nodes +class connectionGene { + constructor(from, to, w, inno) { + this.fromNode = from; + this.toNode = to; + this.weight = w; + this.enabled = true; + this.innovationNo = inno; //each connection is given a innovation number to compare genomes + + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //changes the this.weight + mutateWeight() { + var rand2 = random(1); + if (rand2 < 0.1) { //10% of the time completely change the this.weight + this.weight = random(-1, 1); + } else { //otherwise slightly change it + this.weight += (randomGaussian() / 50); + //keep this.weight between bounds + if (this.weight > 1) { + this.weight = 1; + } + if (this.weight < -1) { + this.weight = -1; + + } + } + } + + //---------------------------------------------------------------------------------------------------------- + //returns a copy of this connectionGene + clone(from, to) { + var clone = new connectionGene(from, to, this.weight, this.innovationNo); + clone.enabled = this.enabled; + + return clone; + } + + + //------------------------------------------------------------------------------------------------------------------- + //returns whether or not these 2 connection genes are the same + isEqualTo(otherConnectionGene) { + return this.fromNode.number === otherConnectionGene.fromNode.number && this.toNode.number === otherConnectionGene.toNode.number + && this.weight === otherConnectionGene.weight && this.enabled === otherConnectionGene.enabled && this.innovationNo === otherConnectionGene.innovationNo; + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/ConnectionHistory.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/ConnectionHistory.js new file mode 100644 index 0000000..10de5d4 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/ConnectionHistory.js @@ -0,0 +1,30 @@ +class connectionHistory { + constructor(from, to, inno, innovationNos) { + this.fromNode = from; + this.toNode = to; + this.innovationNumber = inno; + this.innovationNumbers = []; //the innovation Numbers from the connections of the genome which first had this mutation + //this represents the genome and allows us to test if another genoeme is the same + //this is before this connection was added + arrayCopy(innovationNos, this.innovationNumbers); //copy (from, to) + } + + //---------------------------------------------------------------------------------------------------------------- + //returns whether the genome matches the original genome and the connection is between the same nodes + matches(genome, from, to) { + if (genome.genes.length === this.innovationNumbers.length) { //if the number of connections are different then the genoemes aren't the same + if (from.number === this.fromNode && to.number === this.toNode) { + //next check if all the innovation numbers match from the genome + for (var i = 0; i < genome.genes.length; i++) { + if (!this.innovationNumbers.includes(genome.genes[i].innovationNo)) { + return false; + } + } + //if reached this far then the innovationNumbers match the genes innovation numbers and the connection is between the same nodes + //so it does match + return true; + } + } + return false; + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Node.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Node.js new file mode 100644 index 0000000..d2605b3 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Node.js @@ -0,0 +1,71 @@ +class Node { + + constructor(no) { + this.number = no; + this.inputSum = 0; //current sum i.e. before activation + this.outputValue = 0; //after activation function is applied + this.outputConnections = []; //new ArrayList(); + this.layer = 0; + this.drawPos = createVector(); + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //the node sends its output to the inputs of the nodes its connected to + engage() { + if(this.layer != 0) { //no sigmoid for the inputs and bias + this.outputValue = this.sigmoid(this.inputSum); + } + + for(var i = 0; i < this.outputConnections.length; i++) { //for each connection + if(this.outputConnections[i].enabled) { //dont do shit if not enabled + this.outputConnections[i].toNode.inputSum += this.outputConnections[i].weight * this.outputValue; //add the weighted output to the sum of the inputs of whatever node this node is connected to + } + } + } + //---------------------------------------------------------------------------------------------------------------------------------------- + //not used + stepFunction(x) { + if(x < 0) { + return 0; + } else { + return 1; + } + } + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //sigmoid activation function + sigmoid(x) { + return 1.0 / (1.0 + pow(Math.E, -4.9 * x)); //todo check pow + } + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + //returns whether this node connected to the parameter node + //used when adding a new connection + isConnectedTo(node) { + if(node.layer == this.layer) { //nodes in the same this.layer cannot be connected + return false; + } + + //you get it + if(node.layer < this.layer) { + for(var i = 0; i < node.outputConnections.length; i++) { + if(node.outputConnections[i].toNode == this) { + return true; + } + } + } else { + for(var i = 0; i < this.outputConnections.length; i++) { + if(this.outputConnections[i].toNode == node) { + return true; + } + } + } + + return false; + } + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //returns a copy of this node + clone() { + var clone = new Node(this.number); + clone.layer = this.layer; + return clone; + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Player.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Player.js new file mode 100644 index 0000000..457a74a --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Player.js @@ -0,0 +1,619 @@ +let maxJointAcceleration = 0.5; + +//the player class holds the logic for each player, this means it has the box2d body and the brain and handles their functionality +class Player { + + constructor() { + this.world = new b2World(new Vec2(0, 6), true);//each player has its own world + this.world.SetContactListener(listener);//set listener which is listening for contact with the ground to determine how many limbs it has on the ground + + this.score = 0; + this.fitness = 0; + this.gen = 0; + + + this.vision = []; //the input array fed into the neuralNet + this.decision = []; //the out put of the NN + + this.lifespan = 0; //how long the player lived for this.fitness + this.bestScore = 0; //stores the this.score achieved used for replay + + this.dead = false; + + + + + this.floorBody;// the floor body (created in the addBodiesAndSHitFromCreature()) + this.bodies = []; + this.joints = []; + this.jointSpeeds = [];//the current joint speeds of each joint, these values are controlled by the brain + this.addBodiesAndShitFromCreature(); + + + this.stepsToLive = 10000;//so the players dont continue forever they die when this reaches 0 + this.startingX = this.getCenterOfMass().x; + this.controllableJoints = this.joints.filter((b) => b.controlJoint).length;//get a list of all controllable joints, this is simply all joints but just in case i want to turn off certain joints later + + this.look();//in order to get the number of inputs we need for the genome we call the look function and see how many values populate the vision array + + this.genomeInputs = this.vision.length;//the number of inputs in the genome + this.genomeOutputs = this.jointSpeeds.length;//the number of outputs in the genome + this.brain = new Genome(this.genomeInputs, this.genomeOutputs);//the brain itself + + this.maxJointSpeed = 5;//joints will move at speeds in the range of -1*maxJointSPeed and 1* maxJointSpeed + + this.bestX = this.startingX;//keep track of the bestX value the player reaches in order to calculate the score + this.touchingGround = false;//is the player currently touching the ground + + this.lifespan = 0; //how long the player lived + + this.justDiedFromLazer = false; + this.deathCounter = 100;//when the player dies it turns into a trasparent red blob and once this timer hits 0 its fully dead + + + this.isOnScreen = false; + this.isShowing = false; + + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //draws the creature to the screen if it isn't dead + show() { + + if (!this.dead) { + this.isOnScreen = false; + push(); + translate(panX, panY); + + //draw all the bodies to the screen + for (var i = 0; i < this.bodies.length; i++) { + + this.bodies[i].show(); + if (this.bodies[i].isOnScreen) { + this.isOnScreen = true; + } + } + + pop(); + + this.isShowing = true; + } + + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //sets the joint speed of each joint based on the outputs of the brain + move() { + + let counter = 0; + for (var j of this.joints) { + if (j.type === "revolute" && j.controlJoint) { + //for each joint + + let jointSpeed = this.jointSpeeds[counter]; + let simulatedEase = false; + //if the joint is limited then decrease the velocity of the joint as it reaches the limits so it looks more natural + if (j.limitRevolution) { + + let upperLim = j.joint.GetUpperLimit(); + let lowerLim = j.joint.GetLowerLimit(); + let jointRange = upperLim - lowerLim; + let jointAngle = j.joint.GetJointAngle(); + + //if the joint is near the limit then slow the joint speed down to simulate ease in and out + let previousJointSpeed = jointSpeed; + + if (jointSpeed < 0) { + if (jointAngle - lowerLim < 0.2 * jointRange) { + if (jointAngle < lowerLim) { + jointSpeed = 0; + simulatedEase = true; + } else { + jointSpeed = map(jointAngle - lowerLim, 0, 0.2 * jointRange, this.jointSpeeds[counter], 0); + simulatedEase = true; + } + } + + } else { + + if (upperLim - jointAngle < 0.2 * jointRange) { + if (upperLim < jointAngle) { + jointSpeed = 0; + simulatedEase = true; + } else { + jointSpeed = map(upperLim - jointAngle, 0, 0.2 * jointRange, 0, this.jointSpeeds[counter]); + simulatedEase = true; + } + } + + + } + + } + + + //if the joint hasnt already had simulated ease applied because its close to the limits then limit the joints acceleration so it looks more natural + if (!simulatedEase && Math.abs(j.motorSpeed - jointSpeed) > maxJointAcceleration) { + if (jointSpeed > j.motorSpeed) { + jointSpeed = j.motorSpeed + maxJointAcceleration; + } else { + jointSpeed = j.motorSpeed - maxJointAcceleration; + } + } + + j.setMotorSpeed(jointSpeed); + counter += 1; + } + } + + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //called every frame + update() { + this.lifespan++; + //decrease steps to live and kill it if its less than 0 + if (!this.dead) { + this.stepsToLive -= 1; + } + if (this.stepsToLive <= 0) { + this.dead = true; + } + + //progress the box2d world + this.world.Step(1 / 30, 10, 10); + + //if the player is currently in the death animation phase (i.e.is red and transparent because it hit the lazer) then decrement the death counter and if it hits 0 then kill the player + if (this.justDiedFromLazer) { + if (this.deathCounter > 0) { + this.deathCounter--; + } else { + this.dead = true; + } + return; + } + + //get the center of mass and see if the player has progressed further than its previous bestX + let centerOfMass = this.getCenterOfMass(); + + if (!this.justDiedFromLazer && this.bestX < centerOfMass.x) { + this.bestX = centerOfMass.x; + this.score = this.bestX - this.startingX; + } + + + //set the joint speeds + this.move(); + + //if any bodies have the deathIfTouchesGround property and if that body is touching the ground kill the player + if (!this.justDiedFromLazer) { + for (let b of this.bodies) { + if (b.touchingGround && b.deathIfTouchesGround) { + this.dead = true; + this.deathCounter = -1; + } + } + } + + //check if the player is hit by the lazer + let lazerX = (-10 + deathLazerSpeed * this.lifespan / 100.0) * SCALE; + let hitByLazer = false; + for (let b of this.bodies) { + for (let f of b.fixtures) { + if (f.hitByLazer(lazerX)) { + hitByLazer = true; + break; + } + } + if (hitByLazer) { + break; + } + + } + + //if the player was hit by the lazer this frame then play a sound and kill the player + if (hitByLazer) { + if (this.isShowing && !muted) { + lazerShot.play(); + burnedPlayer.play(); + } + + + if (!showDeath) { + this.dead = true; + this.deathCounter = -1; + } else { + //if showing death then remove all joints from the creature and set each body part to dead + this.justDiedFromLazer = true; + for (var j of this.joints) { + this.world.DestroyJoint(j.joint); + } + for (var b of this.bodies) { + b.isDead = true; + } + } + } + + + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + //gets input for the NN from its environment + //populates the vision array + look() { + this.vision = []; + + //-------------------------------------------angles and speeds of revolute joints + let jointAngles = []; + let inputJointSpeeds = []; + for (var j of this.joints) { + if (j.type === "revolute") { + if (j.limitRevolution) {// if limited then map 0 and 1 to the limits + let upperLim = j.joint.GetUpperLimit(); + let lowerLim = j.joint.GetLowerLimit(); + let jointAngle = constrain(j.joint.GetJointAngle(), lowerLim, upperLim);//make sure the angle is within the limit (sometimes the physics engine can be a little fucky) + this.vision.push(map(jointAngle, lowerLim, upperLim, 0, 1)); + } else { + jointAngles.push(j.joint.GetJointAngle()); + } + + inputJointSpeeds.push(j.joint.GetJointSpeed()); + + } + } + + + //add the joint angles to the vision array + for (var j of jointAngles) { + let val = j; + while (val < 0) { + val += 2 * PI; + } + val %= 2 * PI; + this.vision.push(map(val, 0, 2 * PI, 0, 1)); + } + + //add the joint speeds to the vision array + for (var j of inputJointSpeeds) { + let val = j; + val = val / this.maxJointSpeed; + // this.vision.push(val); + } + + + //-------------------------------------------if touching ground + + + let bodyTouchingGroundCounter = this.bodies.filter((b) => b.touchingGround).length; + this.vision.push(bodyTouchingGroundCounter / this.bodies.length); + + for (let b of this.bodies) { + if (b.untouchGroundNextFrame) { + b.touchingGround = false; + b.untouchGroundNextFrame = false; + } + } + + //-------------------------------------------rotation of the body + //the rotation needs to be relative to the mass of the object + let bodyMasses = []; + let totalMass = 0; + for (let b of this.bodies) { + bodyMasses.push(b.body.GetMass()); + totalMass += b.body.GetMass(); + } + + let averageRotationRelativeToMass = 0; + for (let i = 0; i < this.bodies.length; i++) { + let val = getAngleBetween0and2PI(this.bodies[i].body.GetAngle()); + let angle = getAngleBetween0and2PI(this.bodies[i].angle); + //get the difference between the current angle and the stating angle + let angleDiff = val - angle; + + //now we want that difference to be in range (-PI,PI) + + if (angleDiff > PI) { + angleDiff -= 2 * PI; + } else if (angleDiff < -PI) { + angleDiff += 2 * PI; + } + + //now we need to calculate the effeted difference based on it mass + let rotationMass = bodyMasses[i] * angle / totalMass; + averageRotationRelativeToMass += rotationMass; + } + + this.vision.push(map(averageRotationRelativeToMass, -PI, PI, -1, 1)); + + //-------------------------------------------height off the ground based on weight + //essentially get the height of the center of mass of the entire creature + let heightOfCenterOfMass = 0; + for (let i = 0; i < this.bodies.length; i++) { + let height = this.bodies[i].body.GetWorldCenter().y; + height = abs(height - this.floorBody.body.GetPosition().y); + heightOfCenterOfMass += bodyMasses[i] * height / totalMass; + } + this.vision.push(map(heightOfCenterOfMass, 0, 7, 0, 1)); + + + //-------------------------------------------verticle velocity of the creature + //again it should be relative to the mass of each body part so it cant just fling a leg up and be like "im flying" + bodyMasses = []; + totalMass = 0; + for (let b of this.bodies) { + bodyMasses.push(b.body.GetMass()); + totalMass += b.body.GetMass(); + } + + let averageVertVelRelativeToMass = 0; + for (let i = 0; i < this.bodies.length; i++) { + let val = this.bodies[i].body.GetLinearVelocity().y; + + averageVertVelRelativeToMass += bodyMasses[i] * val / totalMass; + } + this.vision.push(map(averageVertVelRelativeToMass, -5, 5, -1, 1)); + + + //-------------------------------------------angular velocity of creature + //again based on mass + bodyMasses = []; + totalMass = 0; + for (let b of this.bodies) { + bodyMasses.push(b.body.GetMass()); + totalMass += b.body.GetMass(); + } + + let averageAngularVelRelativeToMass = 0; + for (let i = 0; i < this.bodies.length; i++) { + let val = this.bodies[i].body.GetAngularVelocity(); + + averageAngularVelRelativeToMass += bodyMasses[i] * val / totalMass; + } + this.vision.push(map(averageAngularVelRelativeToMass, -1.5, 1.5, -1, 1)); + + } + + //gets the center of mass of the creature + getCenterOfMass() { + let bodyMasses = []; + let totalMass = 0; + for (let b of this.bodies) { + bodyMasses.push(b.body.GetMass()); + totalMass += b.body.GetMass(); + } + + let centerOfMass = createVector(0, 0); + for (let i = 0; i < this.bodies.length; i++) { + let pos = this.bodies[i].body.GetWorldCenter(); + centerOfMass = createVector(centerOfMass.x + pos.x * bodyMasses[i] / totalMass, centerOfMass.y + pos.y * bodyMasses[i] / totalMass); + } + + return centerOfMass; + } + + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //gets the output of the brain then converts them to actions, i.e. populates the jointSpeeds array + think() { + + + //get the output of the neural network + this.decision = this.brain.feedForward(this.vision); + + for (var i = 0; i < this.decision.length; i++) { + + let jointSpeed = map(this.decision[i], 0, 1, -this.maxJointSpeed, this.maxJointSpeed); + this.jointSpeeds[i] = jointSpeed; + + } + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //returns a clone of this player with the same brian + clone() { + var clone = new Player(); + clone.brain = this.brain.clone(); + clone.fitness = this.fitness; + clone.brain.generateNetwork(); + clone.gen = this.gen; + clone.bestScore = max(this.score, this.bestScore); + clone.parent = this; + return clone; + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //since there is some randomness in games sometimes when we want to replay the game we need to remove that randomness + //this fuction does that + + cloneForReplay() { + var clone = new Player(); + clone.brain = this.brain.clone(); + clone.fitness = this.fitness; + clone.brain.generateNetwork(); + clone.gen = this.gen; + clone.bestScore = max(this.score, this.bestScore); + + //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<0){ + // let fixture = new CompoundFixture(f.fixtures[0].pixelVectorPositions); + // for(let arrFix of f.fixtures){ + // fixture.addArrayFixture(arrFix); + // } + // fixture.addToBody(this); + // this.fixtures.push(fixture); + // } + break; + } + //add the color of the fixture + if (f.fillColor.toString() === color(240, 150).toString()) { + body.fixtures[body.fixtures.length - 1].fillColor = fillColor; + } else { + + body.fixtures[body.fixtures.length - 1].fillColor = f.fillColor; + } + } + //add all body images + for (let bodyImage of obj.bodyImages) { + let newBodyImage = new BodyImage(bodyImage.image, bodyImage.x, bodyImage.y, bodyImage.w, bodyImage.h); + newBodyImage.angle = bodyImage.angle; + newBodyImage.addToBody(body); + } + //add the collision logic + body.categoryBits = obj.categoryBits; + body.categoryMask = obj.categoryMask; + + body.resetFilterData(); + + this.bodies.push(body); + + } + + //adds the argument joint to this players box2d world + addJointFromObject(obj) { + const {type, anchorX, anchorY, body1No, body2No, limitRevolution, upperLimit, lowerLimit} = obj; + if (type !== "revolute") { + return; + } + let body1 = this.bodies[body1No]; + let body2 = this.bodies[body2No]; + + let joint = new RevoluteJoint(body1, body2, anchorX, anchorY, this.world); + + //set limits + if (limitRevolution) { + joint.setUpperLimit(upperLimit); + joint.setLowerLimit(lowerLimit); + joint.enableLimits(true); + } + joint.setValues(true, 0, 1000); + this.joints.push(joint); + } + + +} + +//ensures an angle is between 0 and 2*PI +function getAngleBetween0and2PI(angle) { + + while (angle < 0) { + angle += 2 * PI; + } + angle %= 2 * PI; + return angle; + +} + +//this listener listen for contact between a body and the ground +var listener = new Box2D.Dynamics.b2ContactListener; + +listener.BeginContact = function (contact) { + + let world = contact.GetFixtureA().GetBody().GetWorld(); + let objectA = contact.GetFixtureA().GetBody().GetUserData(); + let objectB = contact.GetFixtureB().GetBody().GetUserData(); + if (objectB.id === "ground") { + objectA.touchingGround = true; + + } else if (objectA.id === "ground") { + objectB.touchingGround = true; + } +}; + +listener.EndContact = function (contact) { + + let world = contact.GetFixtureA().GetBody().GetWorld(); + let objectA = contact.GetFixtureA().GetBody().GetUserData(); + let objectB = contact.GetFixtureB().GetBody().GetUserData(); + if (objectB.id === "ground") { + if (objectA.deathIfTouchesGround) { + return; + } + objectA.untouchGroundNextFrame = true; + } else if (objectA.id === "ground") { + if (objectB.deathIfTouchesGround) { + return; + } + objectB.untouchGroundNextFrame = true; + + } + +}; diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Population.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Population.js new file mode 100644 index 0000000..21bf864 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Population.js @@ -0,0 +1,613 @@ +//this class handles the logic used to run and evolve the players using the NEAT algorithm +class Population { + + constructor(size) { + this.players = []; + this.bestPlayer; //the best ever player + this.bestScore = 0; //the score of the best ever player + this.globalBestScore = 0; + this.gen = 1; + this.species = []; + this.massExtinctionEvent = false; + + this.innovationHistory = [];//a list of all innovations made throughout the evolution, its needed for NEAT + this.champPositions = [];//the positions of the species champs in the players array + + this.numberOfBatches = numberOfBatches; + + this.playersPerBatch = Math.ceil(size / this.numberOfBatches); + this.batchNo = 0; + + this.bestScoreOfAPreviousBatch = 0; + + this.bestOfEachSpecies = [];//stores the best players of each species so they can be replayed + this.genPlayers = [];//stores the best players of each generation so they can be replayed + + //populate the players array + for (let i = 0; i < size; i++) { + this.players.push(new Player()); + this.players[this.players.length - 1].brain.fullyConnect(this.innovationHistory); + this.players[this.players.length - 1].brain.mutate(this.innovationHistory); + this.players[this.players.length - 1].brain.generateNetwork(); + } + + + this.showingCount = 0;// the number of players currently showing on the screen + speciesNumber = 0; + + } + + + //------------------------------------------------------------------------------------------------------------------------------------------ + //returns true if all the players are dead, plays a warning if the batch/generation timed out + done() { + let lowestTTL = 1000000; + for (let player of this.players) { + lowestTTL = Math.min(lowestTTL, player.stepsToLive); + if (!player.dead) + return false; + } + + if (lowestTTL <= 0) { + + if (this.numberOfBatches > 1) { + warning = new Warning("Batch Timed out", 200, true); + } else { + warning = new Warning("Generation Timed out", 200, true); + } + } + + return true; + } + + + //------------------------------------------------------------------------------------------------------------------------------------------ + //sets the best player globally and for thisthis.gen + setBestPlayer() { + let tempBest = this.species[0].players[0]; + tempBest.gen = this.gen; + + + //if best thisthis.gen is better than the global best score then set the global best as the best thisthis.gen + + if (tempBest.score > this.bestScore) { + this.genPlayers.push(tempBest.cloneForReplay()); + console.log("old best: " + this.bestScore); + console.log("new best: " + tempBest.score); + this.bestScore = tempBest.score; + this.bestPlayer = tempBest.cloneForReplay(); + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------------ + //this function is called when all the players in the this.players are dead and a newthis.generation needs to be made + naturalSelection() { + + this.batchNo = 0; + + let previousPopulationSize = this.players.length; + let previousBest = this.players[0]; + this.bestScoreOfAPreviousBatch = this.globalBestScore; + this.speciate(); //seperate the this.players varo this.species + this.calculateFitness(); //calculate the fitness of each player + this.sortSpecies(); //sort the this.species to be ranked in fitness order, best first + if (this.massExtinctionEvent) { + this.massExtinction(); + this.massExtinctionEvent = false; + } + this.cullSpecies(); //kill off the bottom half of each this.species + this.setBestPlayer(); //save the best player of thisthis.gen + this.killStaleSpecies(); //remove this.species which haven't improved in the last 15(ish)this.generations + this.killBadSpecies(); //kill this.species which are so bad that they cant reproduce + + + console.log("generation " + this.gen + " Number of mutations " + this.innovationHistory.length + " species: " + this.species.length + " <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); + + + let averageSum = this.getAvgFitnessSum(); + let children = []; + this.champPositions = []; + + for (let j = 0; j < this.species.length; j++) { //for each this.species + children.push(this.species[j].champ.clone()); //add champion without any mutation + this.champPositions.push(children.length - 1); + let NoOfChildren = floor(this.species[j].averageFitness / averageSum * populationSize) - 1; //the number of children this species is allowed, note -1 is because the champ is already added + for (let i = 0; i < NoOfChildren; i++) { //get the calculated amount of children from this this.species + children.push(this.species[j].giveMeBaby(this.innovationHistory)); + } + } + + if (children.length < populationSize) { + children.push(previousBest.clone()); + } + + while (children.length < populationSize) { //if not enough babies (due to flooring the number of children to get a whole var) + children.push(this.species[0].giveMeBaby(this.innovationHistory)); //get babies from the best this.species + } + + this.players = []; + arrayCopy(children, this.players); //set the children as the current this.playersulation + this.gen += 1; + for (let i = 0; i < this.players.length; i++) { //generate networks for each of the children + this.players[i].brain.generateNetwork(); + } + + //set number of players per batch + this.playersPerBatch = Math.ceil(this.players.length / this.numberOfBatches); + + //if the numbers of players changed inform the user + if (this.players.length !== previousPopulationSize) { + if (this.numberOfBatches > 1) + warning = new Warning(`Population Size updated to ${this.players.length}, Players Per Batch: ${this.playersPerBatch}`, 200, true); + else + warning = new Warning(`Population Size updated to ${this.players.length}`, 200, true); + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //seperate players into species based on how similar they are to the leaders of each species in the previous gen + speciate() { + for (let s of this.species) { //empty this.species + s.players = []; + } + for (let i = 0; i < this.players.length; i++) { //for each player + let speciesFound = false; + for (let s of this.species) { //for each species + if (s.sameSpecies(this.players[i].brain)) { //if the player is similar enough to be considered in the same species + s.addToSpecies(this.players[i]); //add it to the species + speciesFound = true; + break; + } + } + if (!speciesFound) { //if no species was similar enough then add a new this.species with this as its champion + this.species.push(new Species(this.players[i])); + this.bestOfEachSpecies.push({champ: this.players[i].cloneForReplay(), maxPlayerLength: 1}); + print("new Species"); + } + } + for (let s of this.species) { + this.bestOfEachSpecies[s.speciesNumber] = { + champ: s.champ.cloneForReplay(), + maxPlayerLength: Math.max(this.bestOfEachSpecies[s.speciesNumber].maxPlayerLength, s.players.length) + }; + } + + + this.bestOfEachSpeciesSorted = [...this.bestOfEachSpecies]; + this.bestOfEachSpeciesSorted.sort((a, b) => a.champ.bestScore - b.champ.bestScore); + this.bestOfPopularSpeciesSorted = this.bestOfEachSpeciesSorted.filter((a) => a.maxPlayerLength >= 2); + + + let minAllowedPlayerLength = 3; + while (this.bestOfPopularSpeciesSorted.length > 30) { + + for (let i = 0; i < this.bestOfPopularSpeciesSorted.length - 5; i++) { + if (this.bestOfPopularSpeciesSorted[i].maxPlayerLength < minAllowedPlayerLength && this.bestOfPopularSpeciesSorted.length > 30) { + this.bestOfPopularSpeciesSorted.splice(i, 1); + i--; + } + } + + minAllowedPlayerLength++; + } + + + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //calculates the fitness of all of the players + calculateFitness() { + for (let i = 1; i < this.players.length; i++) { + this.players[i].calculateFitness(); + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //sorts the players within a this.species and the this.species by their fitnesses + sortSpecies() { + //sort the players within a this.species + for (let s of this.species) { + s.sortSpecies(); + } + + //sort the this.species by the fitness of its best player + //using selection sort like a loser + let temp = []; //new ArrayList(); + for (let i = 0; i < this.species.length; i++) { + let max = 0; + let maxIndex = 0; + for (let j = 0; j < this.species.length; j++) { + if (this.species[j].bestFitness > max) { + max = this.species[j].bestFitness; + maxIndex = j; + } + } + temp.push(this.species[maxIndex]); + this.species.splice(maxIndex, 1); + // this.species.remove(maxIndex); + i--; + } + this.species = []; + arrayCopy(temp, this.species); + + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //kills all this.species which haven't improved in 15this.generations + killStaleSpecies() { + for (let i = 2; i < this.species.length; i++) { + if (this.species[i].staleness >= 15) { + // .remove(i); + // splice(this.species, i) + this.species.splice(i, 1); + i--; + } + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //if a this.species sucks so much that it wont even be allocated 1 child for the nextthis.generation then kill it now + killBadSpecies() { + let averageSum = this.getAvgFitnessSum(); + + for (let i = 1; i < this.species.length; i++) { + if (this.species[i].averageFitness / averageSum * this.players.length < 1) { //if wont be given a single child + // this.species.remove(i); //sad + this.species.splice(i, 1); + + i--; + } + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //returns the sum of each this.species average fitness + getAvgFitnessSum() { + let averageSum = 0; + for (let s of this.species) { + averageSum += s.averageFitness; + } + return averageSum; + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + //kill the bottom half of each this.species + cullSpecies() { + for (let s of this.species) { + s.cull(); //kill bottom half + s.fitnessSharing(); //also while we're at it lets do fitness sharing + s.setAverage(); //reset averages because they will have changed + } + } + + //remove all but the top 5 species + massExtinction() { + for (let i = 5; i < this.species.length; i++) { + // this.species.remove(i); //sad + this.species.splice(i, 1); + + i--; + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------ + // BATCH LEARNING FUNCTIONS + //------------------------------------------------------------------------------------------------------------------------------------------ + //update all the players which are alive + updateAliveInBatches() { + let aliveCount = 0; + let lowestTTL = 10000; + let currentBatch = this.getCurrentBatch(); + for (let player of currentBatch) { + if (!player.dead) { + aliveCount++; + player.look(); + player.think(); + player.update(); + this.globalBestScore = Math.max(this.globalBestScore, player.score); + } + lowestTTL = Math.min(lowestTTL, player.stepsToLive); + } + + + //if all are dead in this batch and this isnt the last bach of this gen then go to the next batch + if (aliveCount === 0 && this.batchNo < this.numberOfBatches - 1) { + this.batchNo++; + genLifespan = 0; + moveCounter = 0; + panX = 0; + this.bestScoreOfAPreviousBatch = this.globalBestScore; + + if (lowestTTL === 0) { + warning = new Warning("Batch Timed Out", 150, true); + } + resetAudio(); + } + + } + + //returns all the players in the current batch + getCurrentBatch() { + let batchStartInclusive = this.batchNo * this.playersPerBatch; + let batchFinishExclusive = Math.min(this.players.length, batchStartInclusive + this.playersPerBatch); + return this.players.slice(batchStartInclusive, batchFinishExclusive); + } + + //returns whether the player at this.players[playerNo] is in the current batch + isPlayerNumberInCurrentBatch(playerNo) { + + let batchStartInclusive = this.batchNo * this.playersPerBatch; + let batchFinishExclusive = Math.min(this.players.length, batchStartInclusive + this.playersPerBatch); + return playerNo >= batchStartInclusive && playerNo < batchFinishExclusive; + + + } + + //updates the players without letting them see or think so their movements are more smooth + updateAliveWithoutThinkingInBatches() { + let aliveCount = 0; + let lowestTTL = 10000; + + let currentBatch = this.getCurrentBatch(); + for (let player of currentBatch) { + if (!player.dead) { + aliveCount++; + player.update(); + this.globalBestScore = Math.max(this.globalBestScore, player.score); + + } + lowestTTL = Math.min(lowestTTL, player.stepsToLive); + + } + + + //if all are dead in this batch and this isnt the last bach of this gen then go to the next batch + if (aliveCount === 0 && this.batchNo < this.numberOfBatches - 1) { + this.batchNo++; + genLifespan = 0; + moveCounter = 0; + panX = 0; + resetAudio(); + this.bestScoreOfAPreviousBatch = this.globalBestScore; + if (lowestTTL === 0) { + warning = new Warning("Batch Timed Out", 150, true); + } + } + + } + + //show the players in the current batch, which players are shown is dependent on the showMode + showPlayersInBatch() { + + push(); + + translate(0, 2 * 376 * (1 - playerScaleAmount)); + + scale(playerScaleAmount); + this.showingCount = 0; + + let currentBatch = this.getCurrentBatch(); + + for (let p of this.players) { + p.isShowing = false; + } + + if (!showNothing) { + switch (showMode) { + case 0://show all + for (let p of currentBatch) { + p.show(); + if (!p.dead && !p.justDiedFromLazer && p.isOnScreen) { + this.showingCount++; + } + } + break; + case 1://show previous best player + if (this.batchNo === 0) { + this.players[0].show(); + if (!this.players[0].dead && !this.players[0].justDiedFromLazer && this.players[0].isOnScreen) { + this.showingCount++; + } + } + break; + case 2://show best of each species + + for (let p of this.champPositions) { + if (this.isPlayerNumberInCurrentBatch(p)) { + this.players[p].show(); + if (!this.players[p].dead && !this.players[p].justDiedFromLazer && this.players[p].isOnScreen) { + this.showingCount++; + } + } + } + break; + case 3://show 20% of players + for (let i = 0; i < currentBatch.length; i++) { + if (i % 5 === 0) { + currentBatch[i].show(); + if (!currentBatch[i].dead && !currentBatch[i].justDiedFromLazer && currentBatch[i].isOnScreen) { + this.showingCount++; + } + } + + } + + for (let p of this.champPositions) { + if (this.isPlayerNumberInCurrentBatch(p)) { + this.players[p].show(); + if (!this.players[p].dead && !this.players[p].justDiedFromLazer && this.players[p].isOnScreen) { + this.showingCount++; + } + } + } + break; + + } + + } + pop(); + } + + //updates the number of batches and the players per batch + setNumberOfBatches(newNumber) { + this.numberOfBatches = newNumber; + numberOfBatches = newNumber; + this.playersPerBatch = Math.ceil(this.players.length / this.numberOfBatches); + } + + //returns the best player in the current batch, used for finding the current best score + getCurrentBestPlayerInBatch() { + let currentBatch = this.getCurrentBatch(); + + let bestPlayer = currentBatch[0]; + for (let p of currentBatch) { + if (p.score > bestPlayer.score) + bestPlayer = p; + } + return bestPlayer; + + } + + //returns the number of players which are still alive in the current batch + getNumberOfPlayersAliveInBatch() { + let aliveCount = 0; + let currentBatch = this.getCurrentBatch(); + for (let player of currentBatch) { + if (!player.dead) + aliveCount++; + + } + return aliveCount; + + } + + + //------------------------------------------------------------------------------------------------------------------------------------------ + //returns all players to the starting position and resets the generation + resetGeneration() { + this.batchNo = 0; + genLifespan = 0; + moveCounter = 0; + for (let i = 0; i < this.players.length; i++) { + this.players[i] = this.players[i].clone(); + this.players[i].fitness = 0; + this.players[i].bestScore = 0; + } + + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // UNUSED FUNCTIONS REPLACED BY BATCH LOGIC + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // updateAlive() { + // + // for (let i = 0; i < this.players.length; i++) { + // if (!this.players[i].dead) { + // this.players[i].look(); //get inputs for brain + // this.players[i].think(); //use outputs from neural network + // this.players[i].update(); //move the player according to the outputs from the neural network + // if (this.players[i].score > this.globalBestScore) { + // this.globalBestScore = this.players[i].score; + // } + // } + // } + // + // } + // + // + // updateAliveWithoutThinking() { + // for (let i = 0; i < this.players.length; i++) { + // if (!this.players[i].dead) { + // this.players[i].update(); //move the player according to the outputs from the neural network + // if (this.players[i].score > this.globalBestScore) { + // this.globalBestScore = this.players[i].score; + // } + // } + // } + // } + // + // + // showPlayers() { + // + // push(); + // translate(0, 2 * 376 * (1 - playerScaleAmount)); + // + // scale(playerScaleAmount); + // this.showingCount = 0; + // + // if (!showNothing) { + // switch (showMode) { + // case 0://show all + // for (let p of this.players) { + // p.show(); + // if (!p.dead && !p.justDiedFromLazer && p.isOnScreen) { + // this.showingCount++; + // } + // } + // break; + // case 1://show previous best player + // this.players[0].show(); + // if (!this.players[0].dead && !this.players[0].justDiedFromLazer && this.players[0].isOnScreen) { + // this.showingCount++; + // } + // break; + // case 2://show best of each species + // + // for (let p of this.champPositions) { + // this.players[p].show(); + // if (!this.players[p].dead && !this.players[p].justDiedFromLazer && this.players[p].isOnScreen) { + // this.showingCount++; + // } + // } + // break; + // case 3://show 20% of players + // for (let i = 0; i < this.players.length; i++) { + // if (i % 5 === 0) { + // this.players[i].show(); + // if (!this.players[i].dead && !this.players[i].justDiedFromLazer && this.players[i].isOnScreen) { + // this.showingCount++; + // } + // } + // + // } + // + // for (let p of this.champPositions) { + // this.players[p].show(); + // if (!this.players[p].dead && !this.players[p].justDiedFromLazer && this.players[p].isOnScreen) { + // this.showingCount++; + // } + // } + // break; + // + // } + // + // } + // pop(); + // } + // + // //------------------------------------------------------------------------------------------------------------------------------------------- + // getNumberOfPlayersAlive() { + // let count = 0; + // for (let p of this.players) { + // if (!p.dead) { + // count++; + // } + // } + // return count; + // } + // //------------------------------------------------------------------------------------------------------------------------------------------ + // getCurrentBestPlayer() { + // let bestPlayer = 0; + // let maxScore = -1; + // for (let i = 0; i < this.players.length; i++) { + // let playerScore = this.players[i].score; + // if (playerScore > maxScore) { + // maxScore = playerScore; + // bestPlayer = i; + // } + // } + // return this.players[bestPlayer]; + // } + + +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Species.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Species.js new file mode 100644 index 0000000..8e3d32f --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/Species.js @@ -0,0 +1,214 @@ +let speciesNumber = 0; + +//this class stores a species of players, a species consists of players which have similar brains, similarity is determined by the sameSpecies function +class Species { + + constructor(p) { + this.speciesNumber = speciesNumber; + speciesNumber++; + this.maxNumberOfPlayers = 1; + + this.players = []; + this.bestFitness = 0; + this.champ;//the best player of this gen + this.averageFitness = 0; + this.staleness = 0; //how many generations the species has gone without an improvement + this.rep;//the brain which represents this species and is what players are compared to, to determine if they are in this species + + //-------------------------------------------- + //coefficients for testing compatibility + this.excessCoeff = 1; + this.weightDiffCoeff = 2; + this.compatibilityThreshold = 1.5; + + //if a player is passed into the constructor then set it as the champ and its brain as the rep + if (p) { + this.players.push(p); + //since it is the only one in the species it is by default the best + this.bestFitness = p.fitness; + this.rep = p.brain.clone(); + this.champ = p.cloneForReplay(); + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + //returns whether the parameter genome is in this species + sameSpecies(g) { + var compatibility; + var excessAndDisjoint = this.getExcessDisjoint(g, this.rep); //get the number of excess and disjoint genes between this player and the current species this.rep + var averageWeightDiff = this.averageWeightDiff(g, this.rep); //get the average weight difference between matching genes + + + var largeGenomeNormaliser = g.genes.length - 100; + if (largeGenomeNormaliser < 1) { + largeGenomeNormaliser = 1; + } + + compatibility = (this.excessCoeff * excessAndDisjoint / largeGenomeNormaliser) + (this.weightDiffCoeff * averageWeightDiff); //compatibility formula + return (this.compatibilityThreshold >= compatibility); + } + + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + //add a player to the species + addToSpecies(p) { + this.players.push(p); + } + + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + //returns the number of excess and disjoint genes between the 2 input genomes + //i.e. returns the number of genes which dont match + getExcessDisjoint(brain1, brain2) { + var matching = 0.0; + for (var i = 0; i < brain1.genes.length; i++) { + for (var j = 0; j < brain2.genes.length; j++) { + if (brain1.genes[i].innovationNo == brain2.genes[j].innovationNo) { + matching++; + break; + } + } + } + return (brain1.genes.length + brain2.genes.length - 2 * (matching)); //return no of excess and disjoint genes + } + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + //returns the avereage weight difference between matching genes in the input genomes + averageWeightDiff(brain1, brain2) { + if (brain1.genes.length == 0 || brain2.genes.length == 0) { + return 0; + } + + + var matching = 0; + var totalDiff = 0; + for (var i = 0; i < brain1.genes.length; i++) { + for (var j = 0; j < brain2.genes.length; j++) { + if (brain1.genes[i].innovationNo == brain2.genes[j].innovationNo) { + matching++; + totalDiff += abs(brain1.genes[i].weight - brain2.genes[j].weight); + break; + } + } + } + if (matching == 0) { //divide by 0 error + return 100; + } + return totalDiff / matching; + } + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //sorts the species by fitness + sortSpecies() { + + var temp = []; // new ArrayList < Player > (); + + //selection short + for (var i = 0; i < this.players.length; i++) { + var max = 0; + var maxIndex = 0; + for (var j = 0; j < this.players.length; j++) { + if (this.players[j].fitness > max) { + max = this.players[j].fitness; + maxIndex = j; + } + } + temp.push(this.players[maxIndex]); + + this.players.splice(maxIndex, 1); + // this.players.remove(maxIndex); + i--; + } + + // this.players = (ArrayList) temp.clone(); + arrayCopy(temp, this.players); + if (this.players.length == 0) { + this.staleness = 200; + return; + } + //if new best player + if (this.players[0].fitness > this.bestFitness) { + this.staleness = 0; + this.bestFitness = this.players[0].fitness; + this.rep = this.players[0].brain.clone(); + this.champ = this.players[0].cloneForReplay(); + } else { //if no new best player + this.staleness++; + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //simple stuff + setAverage() { + var sum = 0; + for (var i = 0; i < this.players.length; i++) { + sum += this.players[i].fitness; + } + this.averageFitness = sum / this.players.length; + } + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + //gets baby from the this.players in this species + giveMeBaby(innovationHistory) { + var baby; + let parent; + if (random(1) < 0.25) { //25% of the time there is no crossover and the child is simply a clone of a random(ish) player + baby = this.selectPlayer().clone(); + parent = baby.parent; + } else { //75% of the time do crossover + + //get 2 random(ish) parents + var parent1 = this.selectPlayer(); + var parent2 = this.selectPlayer(); + + //the crossover function expects the highest fitness parent to be the object and the lowest as the argument + if (parent1.fitness < parent2.fitness) { + baby = parent2.crossover(parent1); + parent = parent2; + } else { + baby = parent1.crossover(parent2); + parent = parent2; + } + } + + + baby.brain.mutate(innovationHistory); //mutate that baby brain + + + return baby; + } + + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //selects a player based on it fitness + selectPlayer() { + var fitnessSum = 0; + for (var i = 0; i < this.players.length; i++) { + fitnessSum += this.players[i].fitness; + } + var rand = random(fitnessSum); + var runningSum = 0; + + for (var i = 0; i < this.players.length; i++) { + runningSum += this.players[i].fitness; + if (runningSum > rand) { + return this.players[i]; + } + } + //unreachable code to make the parser happy + return this.players[0]; + } + //------------------------------------------------------------------------------------------------------------------------------------------ + //kills off bottom half of the species + cull() { + if (this.players.length > 2) { + for (var i = this.players.length / 2; i < this.players.length; i++) { + // this.players.remove(i); + this.players.splice(i, 1); + i--; + } + } + } + //------------------------------------------------------------------------------------------------------------------------------------------ + //in order to protect unique this.players, the fitnesses of each player is divided by the number of this.players in the species that that player belongs to + fitnessSharing() { + for (var i = 0; i < this.players.length; i++) { + this.players[i].fitness /= this.players.length; + } + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/genome.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/genome.js new file mode 100644 index 0000000..4387c38 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/genome.js @@ -0,0 +1,543 @@ +class Genome { + constructor(inputs, outputs, crossover) { + this.genes = []; //a list of connections between this.nodes which represent the NN + this.nodes = []; + this.inputs = inputs; + this.outputs = outputs; + this.layers = 2; + this.nextNode = 0; + // this.biasNode; + this.network = []; //a list of the this.nodes in the order that they need to be considered in the NN + //create input this.nodes + + if (crossover) { + return; + } + + for (var i = 0; i < this.inputs; i++) { + this.nodes.push(new Node(i)); + this.nextNode++; + this.nodes[i].layer = 0; + } + + //create output this.nodes + for (var i = 0; i < this.outputs; i++) { + this.nodes.push(new Node(i + this.inputs)); + this.nodes[i + this.inputs].layer = 1; + this.nextNode++; + } + + this.nodes.push(new Node(this.nextNode)); //bias node + this.biasNode = this.nextNode; + this.nextNode++; + this.nodes[this.biasNode].layer = 0; + + + } + + + fullyConnect(innovationHistory) { + + // //connect all input nodes to output nodes + for (var i = 0; i < this.inputs; i++) { + for (var j = 0; j < this.outputs; j++) { + let connectionInnovationNumber = this.getInnovationNumber(innovationHistory, this.nodes[i], this.nodes[this.inputs + j]); + this.genes.push(new connectionGene(this.nodes[i], this.nodes[this.inputs + j], random(-1, 1), connectionInnovationNumber)); + } + } + + + //connect the bias node to all output nodes + for(let i = 0 ; i< this.outputs; i++){ + let connectionInnovationNumber = this.getInnovationNumber(innovationHistory, this.nodes[this.nodes.length-1], this.nodes[this.inputs + i]); + this.genes.push(new connectionGene( this.nodes[this.nodes.length-1], this.nodes[this.inputs + i], random(-1, 1), connectionInnovationNumber)); + } + + + + if(!this.fullyConnected()){ + this.addConnection(innovationHistory); + } + this.connectNodes(); + + + + + + } + + + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //returns the node with a matching number + //sometimes the this.nodes will not be in order + getNode(nodeNumber) { + for (let i = 0; i < this.nodes.length; i++) { + if (this.nodes[i].number == nodeNumber) { + return this.nodes[i]; + } + } + return null; + } + + + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //adds the conenctions going out of a node to that node so that it can acess the next node during feeding forward + connectNodes() { + + for (var i = 0; i < this.nodes.length; i++) { //clear the connections + this.nodes[i].outputConnections = []; + } + + for (var i = 0; i < this.genes.length; i++) { //for each connectionGene + this.genes[i].fromNode.outputConnections.push(this.genes[i]); //add it to node + } + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //feeding in input values varo the NN and returning output array + feedForward(inputValues) { + //set the outputs of the input this.nodes + for (var i = 0; i < this.inputs; i++) { + this.nodes[i].outputValue = inputValues[i]; + } + this.nodes[this.biasNode].outputValue = 1; //output of bias is 1 + + for (var i = 0; i < this.network.length; i++) { //for each node in the network engage it(see node class for what this does) + this.network[i].engage(); + } + + //the outputs are this.nodes[inputs] to this.nodes [inputs+outputs-1] + const outs = []; + for (var i = 0; i < this.outputs; i++) { + outs[i] = this.nodes[this.inputs + i].outputValue; + } + + for (var i = 0; i < this.nodes.length; i++) { //reset all the this.nodes for the next feed forward + this.nodes[i].inputSum = 0; + } + + return outs; + } + + //---------------------------------------------------------------------------------------------------------------------------------------- + //sets up the NN as a list of this.nodes in order to be engaged + + generateNetwork() { + this.connectNodes(); + this.network = []; + //for each layer add the node in that layer, since layers cannot connect to themselves there is no need to order the this.nodes within a layer + + for (let l = 0; l < this.layers; l++) { //for each layer + for (let i = 0; i < this.nodes.length; i++) { //for each node + if (this.nodes[i].layer == l) { //if that node is in that layer + this.network.push(this.nodes[i]); + } + } + } + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + //mutate the NN by adding a new node + //it does this by picking a random connection and disabling it then 2 new connections are added + //1 between the input node of the disabled connection and the new node + //and the other between the new node and the output of the disabled connection + addNode(innovationHistory) { + //pick a random connection to create a node between + if (this.genes.length == 0) { + this.addConnection(innovationHistory); + return; + } + let randomConnection = floor(random(this.genes.length)); + + while (this.genes[randomConnection].fromNode == this.nodes[this.biasNode] && this.genes.length != 1) { //dont disconnect bias + randomConnection = floor(random(this.genes.length)); + } + + this.genes[randomConnection].enabled = false; //disable it + + const newNodeNo = this.nextNode; + this.nodes.push(new Node(newNodeNo)); + this.nextNode++; + //add a new connection to the new node with a weight of 1 + let connectionInnovationNumber = this.getInnovationNumber(innovationHistory, this.genes[randomConnection].fromNode, this.getNode(newNodeNo)); + this.genes.push(new connectionGene(this.genes[randomConnection].fromNode, this.getNode(newNodeNo), 1, connectionInnovationNumber)); + + + connectionInnovationNumber = this.getInnovationNumber(innovationHistory, this.getNode(newNodeNo), this.genes[randomConnection].toNode); + //add a new connection from the new node with a weight the same as the disabled connection + this.genes.push(new connectionGene(this.getNode(newNodeNo), this.genes[randomConnection].toNode, this.genes[randomConnection].weight, connectionInnovationNumber)); + this.getNode(newNodeNo).layer = this.genes[randomConnection].fromNode.layer + 1; + + + connectionInnovationNumber = this.getInnovationNumber(innovationHistory, this.nodes[this.biasNode], this.getNode(newNodeNo)); + //connect the bias to the new node with a weight of 0 + this.genes.push(new connectionGene(this.nodes[this.biasNode], this.getNode(newNodeNo), 0, connectionInnovationNumber)); + + //if the layer of the new node is equal to the layer of the output node of the old connection then a new layer needs to be created + //more accurately the layer numbers of all layers equal to or greater than this new node need to be incrimented + if (this.getNode(newNodeNo).layer == this.genes[randomConnection].toNode.layer) { + for (let i = 0; i < this.nodes.length - 1; i++) { //dont include this newest node + if (this.nodes[i].layer >= this.getNode(newNodeNo).layer) { + this.nodes[i].layer++; + } + } + this.layers++; + } + this.connectNodes(); + } + + //------------------------------------------------------------------------------------------------------------------ + //adds a connection between 2 nodes which aren't currently connected + addConnection(innovationHistory) { + //cannot add a connection to a fully connected network + if (this.fullyConnected()) { + console.log("connection failed"); + return; + } + + + //get random this.nodes + let randomNode1 = floor(random(this.nodes.length)); + let randomNode2 = floor(random(this.nodes.length)); + while (this.randomConnectionNodesAreShit(randomNode1, randomNode2)) { //while the random this.nodes are no good + //get new ones + randomNode1 = floor(random(this.nodes.length)); + randomNode2 = floor(random(this.nodes.length)); + } + let temp; + if (this.nodes[randomNode1].layer > this.nodes[randomNode2].layer) { //if the first random node is after the second then switch + temp = randomNode2; + randomNode2 = randomNode1; + randomNode1 = temp; + } + + //get the innovation number of the connection + //this will be a new number if no identical genome has mutated in the same way + const connectionInnovationNumber = this.getInnovationNumber(innovationHistory, this.nodes[randomNode1], this.nodes[randomNode2]); + //add the connection with a random array + + this.genes.push(new connectionGene(this.nodes[randomNode1], this.nodes[randomNode2], random(-1, 1), connectionInnovationNumber)); //changed this so if error here + this.connectNodes(); + } + + //------------------------------------------------------------------------------------------------------------------------------------------- + randomConnectionNodesAreShit(r1, r2) { + if (this.nodes[r1].layer == this.nodes[r2].layer) return true; // if the this.nodes are in the same layer + if (this.nodes[r1].isConnectedTo(this.nodes[r2])) return true; //if the this.nodes are already connected + + + return false; + } + + //------------------------------------------------------------------------------------------------------------------------------------------- + //returns the innovation number for the new mutation + //if this mutation has never been seen before then it will be given a new unique innovation number + //if this mutation matches a previous mutation then it will be given the same innovation number as the previous one + getInnovationNumber(innovationHistory, from, to) { + let isNew = true; + let connectionInnovationNumber = nextConnectionNo; + for (var i = 0; i < innovationHistory.length; i++) { //for each previous mutation + if (innovationHistory[i].matches(this, from, to)) { //if match found + isNew = false; //its not a new mutation + connectionInnovationNumber = innovationHistory[i].innovationNumber; //set the innovation number as the innovation number of the match + break; + } + } + + if (isNew) { //if the mutation is new then create an arrayList of varegers representing the current state of the genome + const innoNumbers = []; + for (var i = 0; i < this.genes.length; i++) { //set the innovation numbers + innoNumbers.push(this.genes[i].innovationNo); + } + + //then add this mutation to the innovationHistory + innovationHistory.push(new connectionHistory(from.number, to.number, connectionInnovationNumber, innoNumbers)); + nextConnectionNo++; + } + return connectionInnovationNumber; + } + + //---------------------------------------------------------------------------------------------------------------------------------------- + + //returns whether the network is fully connected or not + fullyConnected() { + + let maxConnections = 0; + const nodesInLayers = []; //array which stored the amount of this.nodes in each layer + for (var i = 0; i < this.layers; i++) { + nodesInLayers[i] = 0; + } + //populate array + for (var i = 0; i < this.nodes.length; i++) { + nodesInLayers[this.nodes[i].layer] += 1; + } + //for each layer the maximum amount of connections is the number in this layer * the number of this.nodes infront of it + //so lets add the max for each layer together and then we will get the maximum amount of connections in the network + for (var i = 0; i < this.layers - 1; i++) { + let nodesInFront = 0; + for (let j = i + 1; j < this.layers; j++) { //for each layer infront of this layer + nodesInFront += nodesInLayers[j]; //add up this.nodes + } + + maxConnections += nodesInLayers[i] * nodesInFront; + } + + if (maxConnections <= this.genes.length) { //if the number of connections is equal to the max number of connections possible then it is full + return true; + } + + return false; + } + + + //------------------------------------------------------------------------------------------------------------------------------- + //mutates the genome + mutate(innovationHistory) { + if (this.genes.length == 0) { + this.addConnection(innovationHistory); + } + + + const rand1 = random(1); + if (rand1 < 0.8) { // 80% of the time mutate weights + + for (let i = 0; i < this.genes.length; i++) { + this.genes[i].mutateWeight(); + } + } + + //5% of the time add a new connection + const rand2 = random(1); + if (rand2 < 0.05) { + + this.addConnection(innovationHistory); + } + + //1% of the time add a node + const rand3 = random(1); + if (rand3 < 0.01) { + + this.addNode(innovationHistory); + } + } + + //--------------------------------------------------------------------------------------------------------------------------------- + //called when this Genome is better that the other parent + crossover(parent2) { + const child = new Genome(this.inputs, this.outputs, true); + child.genes = []; + child.nodes = []; + child.layers = this.layers; + child.nextNode = this.nextNode; + child.biasNode = this.biasNode; + const childGenes = []; // new ArrayList();//list of genes to be inherrited form the parents + const isEnabled = []; // new ArrayList(); + //all inherited genes + for (var i = 0; i < this.genes.length; i++) { + let setEnabled = true; //is this node in the chlid going to be enabled + + const parent2gene = this.matchingGene(parent2, this.genes[i].innovationNo); + if (parent2gene != -1) { //if the genes match + if (!this.genes[i].enabled || !parent2.genes[parent2gene].enabled) { //if either of the matching genes are disabled + + if (random(1) < 0.75) { //75% of the time disabel the childs gene + setEnabled = false; + } + } + const rand = random(1); + if (rand < 0.5) { + childGenes.push(this.genes[i]); + + //get gene from this fucker + } else { + //get gene from parent2 + childGenes.push(parent2.genes[parent2gene]); + } + } else { //disjoint or excess gene + childGenes.push(this.genes[i]); + setEnabled = this.genes[i].enabled; + } + isEnabled.push(setEnabled); + } + + + //since all excess and disjovar genes are inherrited from the more fit parent (this Genome) the childs structure is no different from this parent | with exception of dormant connections being enabled but this wont effect this.nodes + //so all the this.nodes can be inherrited from this parent + for (var i = 0; i < this.nodes.length; i++) { + child.nodes.push(this.nodes[i].clone()); + } + + //clone all the connections so that they connect the childs new this.nodes + + for (var i = 0; i < childGenes.length; i++) { + child.genes.push(childGenes[i].clone(child.getNode(childGenes[i].fromNode.number), child.getNode(childGenes[i].toNode.number))); + child.genes[i].enabled = isEnabled[i]; + } + + child.connectNodes(); + return child; + } + + //---------------------------------------------------------------------------------------------------------------------------------------- + //returns whether or not there is a gene matching the input innovation number in the input genome + matchingGene(parent2, innovationNumber) { + for (let i = 0; i < parent2.genes.length; i++) { + if (parent2.genes[i].innovationNo == innovationNumber) { + return i; + } + } + return -1; //no matching gene found + } + + //---------------------------------------------------------------------------------------------------------------------------------------- + //prints out info about the genome to the console + printGenome() { + console.log("Print genome layers:" + this.layers); + console.log("bias node: " + this.biasNode); + console.log("this.nodes"); + for (var i = 0; i < this.nodes.length; i++) { + console.log(this.nodes[i].number + ","); + } + console.log("Genes"); + for (var i = 0; i < this.genes.length; i++) { //for each connectionGene + console.log("gene " + this.genes[i].innovationNo + "From node " + this.genes[i].fromNode.number + "To node " + this.genes[i].toNode.number + + "is enabled " + this.genes[i].enabled + "from layer " + this.genes[i].fromNode.layer + "to layer " + this.genes[i].toNode.layer + "weight: " + this.genes[i].weight); + } + + console.log(); + } + + //---------------------------------------------------------------------------------------------------------------------------------------- + //returns a copy of this genome + clone() { + + const clone = new Genome(this.inputs, this.outputs, true); + + for (var i = 0; i < this.nodes.length; i++) { //copy this.nodes + clone.nodes.push(this.nodes[i].clone()); + } + + //copy all the connections so that they connect the clone new this.nodes + + for (var i = 0; i < this.genes.length; i++) { //copy genes + clone.genes.push(this.genes[i].clone(clone.getNode(this.genes[i].fromNode.number), clone.getNode(this.genes[i].toNode.number))); + } + + clone.layers = this.layers; + clone.nextNode = this.nextNode; + clone.biasNode = this.biasNode; + clone.connectNodes(); + + return clone; + } + + //---------------------------------------------------------------------------------------------------------------------------------------- + //draw the genome on the screen + drawGenome(startX, startY, w, h) { + + let transparency = 100; + //i know its ugly but it works (and is not that important) so I'm not going to mess with it + const allNodes = []; //new ArrayList>(); + const nodePoses = []; // new ArrayList(); + const nodeNumbers = []; // new ArrayList(); + + //get the positions on the screen that each node is supposed to be in + + + let ellipseSize = 20; + + startX += ellipseSize / 2; + startY += ellipseSize / 2; + w -= ellipseSize / 2; + h -= ellipseSize / 2; + + //split the this.nodes varo layers + for (var i = 0; i < this.layers; i++) { + const temp = []; // new ArrayList(); + for (var j = 0; j < this.nodes.length; j++) { //for each node + if (this.nodes[j].layer == i) { //check if it is in this layer + temp.push(this.nodes[j]); //add it to this layer + } + } + allNodes.push(temp); //add this layer to all this.nodes + } + + //for each layer add the position of the node on the screen to the node posses arraylist + for (var i = 0; i < this.layers; i++) { + fill(255, 0, 0, transparency); + const x = startX + float((i + 1) * w) / float(this.layers + 1); + for (var j = 0; j < allNodes[i].length; j++) { //for the position in the layer + let y = 0; + if (i !== 0)//if this is the final layer then do it differently + y = startY + float((j+1.0)* h) / float(allNodes[i].length+1.0); + else + y = startY + float((j)* h) / float(allNodes[i].length-1.0); + + nodePoses.push(createVector(x, y)); + nodeNumbers.push(allNodes[i][j].number); + } + } + + //draw connections + stroke(0, transparency); + strokeWeight(2); + for (var i = 0; i < this.genes.length; i++) { + if (this.genes[i].enabled) { + stroke(0, transparency); + } else { + stroke(100, transparency); + } + let from; + let to; + from = nodePoses[nodeNumbers.indexOf(this.genes[i].fromNode.number)]; + to = nodePoses[nodeNumbers.indexOf(this.genes[i].toNode.number)]; + if (this.genes[i].weight > 0) { + stroke(0, 0, 0, transparency); + } else { + stroke(255, 255, 0, transparency); + } + strokeWeight(map(abs(this.genes[i].weight), 0, 1, 0, 3)); + line(from.x, from.y, to.x, to.y); + } + + //draw this.nodes last so they appear ontop of the connection lines + + for (var i = 0; i < nodePoses.length; i++) { + noStroke(); + fill(200); + noStroke(); + ellipse(nodePoses[i].x, nodePoses[i].y, ellipseSize, ellipseSize); + + + fill(255, transparency); + stroke(0, transparency); + strokeWeight(1); + + ellipse(nodePoses[i].x, nodePoses[i].y, ellipseSize, ellipseSize); + + + textSize(10); + fill(0, transparency); + textAlign(CENTER, CENTER); + text(nodeNumbers[i], nodePoses[i].x, nodePoses[i].y - 2); + + } + + // print out neural network info text + // textAlign(RIGHT); + // fill(255); + // textSize(15); + // noStroke(); + // text("car angle", nodePoses[0].x - 20, nodePoses[0].y); + // text("touching ground", nodePoses[1].x - 20, nodePoses[1].y); + // text("angular velocity", nodePoses[2].x - 20, nodePoses[2].y); + // text("Distance to ground", nodePoses[3].x - 20, nodePoses[3].y); + // text("gradient", nodePoses[4].x - 20, nodePoses[4].y); + // text("bias", nodePoses[5].x - 20, nodePoses[5].y); + // textAlign(LEFT); + // text("gas", nodePoses[nodePoses.length - 2].x + 20, nodePoses[nodePoses.length - 2].y); + // text("break", nodePoses[nodePoses.length - 1].x + 20, nodePoses[nodePoses.length - 1].y); + + + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sketch.js b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sketch.js new file mode 100644 index 0000000..3d3e1b5 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sketch.js @@ -0,0 +1,543 @@ +//------------------------------------------GLOBALS + + +let showMode = 3; // 0: show all, 1:show previous best, 2: show best of each species, 3:show 20% of the population +let genLifespan = 0; //how long the current generation have lived, used for the lazer position +let deathLazerSpeed = 8; + + +//some storage to keep track of the lazer position when switching modes (i.e. gen replay and species replay) +let storeGenLifespan = 0; +let storeMoveCounter = 0; + +//Evolution highlights replay stuff +let genReplay = false; +let genPlayer; +let genPlayersCounter = 0; + +//Popular species replay stuff +let speciesReplay = false; +let speciesPlayer; +let speciesPlayersCounter = 0; + + +let thinkEveryXFrames = 5;//in order to smooth the players movement the player will only change their current movement decisions every X frames +let moveCounter = 0; //since the players only think every (thinkEveryXframes) a counter must be held to keep track of how many frames the players have gone without thinking + + +var showNothing = false; +let autoSpeedUp = false; +let showDeath = true; + +let playerScaleAmount = 0.8;//scale down the players by this amount, this is only a visual change +let nextConnectionNo = 1000;//used by the neat algorithm to give each connection gene a unique number + +let population; +let populationSize = 200; +let numberOfBatches = 1; + +let simulationSpeed = 1; +let paused = false; + +let muted = false; +let isCreatureScreaming = false; + +//called whenever the program enters the learning mode +function AILearnsToWalkSetup() { + + setDefaultInstructions(); + + buttonManager.activateLearningButtons(); + + genLifespan = 0; + moveCounter = 0; + + population = new Population(populationSize); +} + +//called every frame when in AI learns to draw mode +function AILearnsToWalkDraw() { + if (!paused) { + + + autoPan(); + + + drawBackground(); + drawToScreen(); + + showWarning(); + + if (!paused && !genReplay && !speciesReplay) { + if (!population.done()) { //if any players are alive then update them\ + let speedUpAmount = simulationSpeed; + if (autoSpeedUp) {//speeds the simulation speed based on howmany players are alive, since less players, less box2d simulations, less processing required + let alive = population.getNumberOfPlayersAliveInBatch(); + speedUpAmount *= floor(population.playersPerBatch / alive); + } + + let startingBatchNo = population.batchNo; + for (var m = 0; m < speedUpAmount; m++) { + moveCounter++; + genLifespan++; + if (moveCounter % thinkEveryXFrames === 0) { + population.updateAliveInBatches(); + } else { + population.updateAliveWithoutThinkingInBatches(); + } + if (startingBatchNo != population.batchNo)//if the batch has ended then stop updating + break; + } + population.showPlayersInBatch(); + + } else { //all dead + //genetic algorithm + population.naturalSelection(); + genLifespan = 0; + moveCounter = 0; + panX = 0; + resetAudio(); + + } + } + + if (!paused) { + + buttonManager.updateCurrentMode(); + } + + + if (!showNothing || genReplay || speciesReplay) { + drawLazer(); + } + showInstructions(); + buttonManager.showActiveButtons(); + } else if (warning && (!warning.isFinished() || warningFade > 0)) { + drawWhilePaused(); + } else { + buttonManager.showActiveButtons(); + } + manageSounds(); + +} + +//when the simulator is paused not only the visuals need to be redrawn, so no updating the players or box2d math, just drawing shit to look pretty +function drawWhilePaused() { + + drawBackground(); + drawToScreen(); + showWarning(); + + if (!genReplay && !speciesReplay) { + population.showPlayersInBatch(); + } + buttonManager.showCurrentModeEffects(); + + if (!showNothing || genReplay || speciesReplay) { + drawLazer(); + } + showInstructions(); + buttonManager.showActiveButtons(); + + manageSounds(); + + +} + +//stores the point the camera is panned to +//it actually is the offset for the players , so if a player is at 100,10 and we have pan X set to -100 then the player will be displayed at 0,10 + +let panX = 0; +let panY = 0;//never used (yet) + +//pans to the leading player +function autoPan() { + + //get the player to pan to depending on the mode + let playerToPanTo; + if (speciesReplay) + playerToPanTo = speciesPlayer; + else if (genReplay) + playerToPanTo = genPlayer; + else if (!showNothing) + playerToPanTo = population.getCurrentBestPlayerInBatch(); + else + return;//if show nothing + + //if the player to pan to is too far right then pan right + while (worldCoordsToPixelCoords(playerToPanTo.getCenterOfMass().x, 0).x * playerScaleAmount > canvas.width * 0.65) { + panX -= 1; + } + + //if the player to pan to is too far left then pan left + while (worldCoordsToPixelCoords(playerToPanTo.getCenterOfMass().x, 0).x * playerScaleAmount < canvas.width * 0.25) { + panX += 1; + } + +} + +//stores the random values used to simulate the sparks of the lazer, this is used when the simulator is paused so the sparks can be in the same position +let randomValues = []; + +//draws the lazer +function drawLazer() { + + if (!paused) + randomValues = []; + + + //get the position of the lazer + let pos = worldCoordsToPixelCoords(-10 + deathLazerSpeed * genLifespan / 100.0, 0); + pos.mult(playerScaleAmount); + + //draw lines making the lazer + push(); + stroke(255, 0, 0); + strokeWeight(11); + line(pos.x, 0, pos.x, canvas.height - 105); + stroke(255, 100, 100); + strokeWeight(5); + line(pos.x, 0, pos.x, canvas.height - 102); + stroke(255, 200, 200); + strokeWeight(1); + line(pos.x, 0, pos.x, canvas.height - 102); + + //draw some images to sell the power of the lazer + image(lazerBurnGroundImage, pos.x - lazerBurnGroundImage.width + 4, canvas.height - 121); + + //draw spark lines + let randomValueCounter = 0; + for (let i = 0; i < 20; i++) { + + i < 10 ? strokeWeight(2) : strokeWeight(1);//have first 10 draw be thicker than the second half + + //if the lazer is paused then use the previously used random numbers, randomise it + let rand1 = paused ? randomValues[randomValueCounter++] : random(200, 255); + let rand2 = paused ? randomValues[randomValueCounter++] : random(0, 100); + + //random color + stroke(rand1, rand2, 0); + + + //random line length and direction + let rand3 = paused ? randomValues[randomValueCounter++] : random(-30, 30); + let rand4 = paused ? randomValues[randomValueCounter++] : random(30); + + line(pos.x, canvas.height - 100, pos.x + rand3, canvas.height - 100 - rand4); + + //add these new values to storage array if the game is paused + if (!paused) { + randomValues.push(rand1, rand2, rand3, rand4); + } + } +} + + +//called whenever a key is pressed when the ais are training +function AILearnsToWalkKeyPressed() { + switch (keyCode) { + case TAB://changes the show mode + showMode = (showMode + 1) % 5; + showNothing = showMode === 4; + break; + } + switch (key) { + case ' '://toggles pause + paused = !paused; + manageSounds(); + break; + + } + + //see if any of the current modes need the input + buttonManager.onKeyPressed(); + + //if the simulator is paused then things might have changed so redraw everything + if (paused) + drawWhilePaused(); +} + +//a useless function implemented incase I wanted the arrow keys to control something and you needed to hold them down. +//i dont know why i wrote this comment instead of just deleting the function but im a code hoarder +let up = false; +let down = false; +let right = false; +let left = false; + +function AILearnsToWalkKeyReleased() { + switch (keyCode) { + case RIGHT_ARROW: + right = false; + break; + case LEFT_ARROW: + left = false; + break; + case UP_ARROW: + up = false; + break; + case DOWN_ARROW: + down = false; + break; + } +} + + +function AILearnsToWalkMousePressed() { + dragging = true; + buttonManager.onClick(); + if (paused) + drawWhilePaused(); + +} + + +//handles most of the sounds played by the simulator +//handles when and howmany screams to play +//handles the lazer sound +//doesn't handle the player death sound (that it handled by each indavidual player) +let previousShowingCount = 0;//counts the number of players shown on the screen in the previous scene. this determines the number of sounds we need to stop + +function manageSounds() { + //manage lazer sound + + if (!paused && !muted && !lazerSoundEffect.isPlaying()) { + lazerSoundEffect.play(); + + } + if ((paused || muted) && lazerSoundEffect.isPlaying()) { + lazerSoundEffect.pause(); + + } + + + //manage screams + + if (isCreatureScreaming) { + let maxNumberOfSounds = screamSounds.length; + + masterVolume(1); + //if the previous count is not the same as the current showing count then we might need to add or remove audio + if (population.showingCount !== previousShowingCount || paused || muted || genReplay || speciesReplay) { + //since all showing counts greater than 10 are considered as playing 10 sounds then we dont need to change them + if (!genReplay && !speciesReplay && !paused && !muted && previousShowingCount >= 10 && population.showingCount >= maxNumberOfSounds) { + previousShowingCount = population.showingCount; + return; + } + + let numberOfSoundsCurrentlyPlaying = constrain(previousShowingCount, 0, maxNumberOfSounds); + let numberOfSoundsNeedToPlay = constrain(population.showingCount, 0, maxNumberOfSounds); + if (genReplay || speciesReplay) { + numberOfSoundsNeedToPlay = 1; + } + + if (paused || muted) { + masterVolume(0); + numberOfSoundsNeedToPlay = 0; + for (let sound of screamSounds) { + if (sound.isPlaying()) { + sound.setVolume(0.2); + sound.setLoop(true); + sound.playMode('restart'); + sound.pause(); + } + } + previousShowingCount = 0; + return; + } + + + if (numberOfSoundsCurrentlyPlaying > numberOfSoundsNeedToPlay) { + //remove sounds; + for (let i = numberOfSoundsCurrentlyPlaying; i > numberOfSoundsNeedToPlay; i--) { + screamSounds[i - 1].setVolume(0.2); + screamSounds[i - 1].setLoop(true); + screamSounds[i - 1].playMode('restart'); + screamSounds[i - 1].pause(); + } + } + if (numberOfSoundsCurrentlyPlaying < numberOfSoundsNeedToPlay) { + //add sounds; + for (let i = numberOfSoundsCurrentlyPlaying + 1; i <= numberOfSoundsNeedToPlay; i++) { + //jump the audio to a random point then play it + screamSounds[i - 1].setVolume(0.2); + screamSounds[i - 1].setLoop(true); + screamSounds[i - 1].playMode('restart'); + screamSounds[i - 1].rate(1 + (Math.random() - 1) * 0.1); + screamSounds[i - 1].play(); + screamSounds[i - 1].playMode('restart'); + + } + } + } + + previousShowingCount = population.showingCount; + if (genReplay || speciesReplay) { + previousShowingCount = 1; + } + + if (paused || muted) { + previousShowingCount = 0; + } + + } + +} + +//resets all audio tracks to the begining and stops them, used when a generation or batch is finished +function resetAudio() { + + for (let sound of screamSounds) { + sound.setVolume(0.2); + sound.setLoop(true); + sound.playMode('restart'); + sound.stop(); + } + + previousShowingCount = 0; + lazerSoundEffect.playMode('restart'); + lazerSoundEffect.stop(); + +} + +//converts world coordinates(in box2ds meters) to pixel coordinates +function worldCoordsToPixelCoords(x, y) { + x *= SCALE; + y *= SCALE; + x += panX; + y += panY; + return createVector(x, y); +} + + + +//--------------------------------------------------------------------------------------------------------------------------------------------------------- +//draws the display screen +function drawToScreen() { + + drawBrain(); + writeInfo(); + +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +//show the brain of whatever genome is currently showing +function drawBrain() { + var startX = canvas.width / 2 - 225; + var startY = 70; + var w = 450; + var h = 160; + if (genReplay) { + genPlayer.brain.drawGenome(startX, startY, w, h); + } else if (speciesReplay) { + speciesPlayer.brain.drawGenome(startX, startY, w, h); + } else { + population.players[0].brain.drawGenome(startX, startY, w, h); + } +} + + +//writes info about the current player +function writeInfo() { + + push(); + textAlign(LEFT); + textSize(20); + fill(0,100); + noStroke(); + strokeWeight(1); + + let startingY = 100; + let gap = 30; + let startingX = 20; + + + //if replaying generation/evolution highlights the show the score of the current player and the generation it belongs to + if (genReplay) { + + text("Generation: " + genPlayer.gen, startingX, startingY); + text("Score: " + genPlayer.score.toFixed(2), startingX, startingY + gap); + + //if replaying popular species then show the species number and the current score + } else if (speciesReplay) { + + text(`Species: ${speciesPlayersCounter+1}/${population.bestOfPopularSpeciesSorted.length}`, startingX, startingY); + text("Score: " + speciesPlayer.score.toFixed(2), startingX, startingY + gap); + + + } else { + //show gen number + text("Generation: " + population.gen, startingX, startingY); + + //get the current and previous best score and line them up so the decimal point is in the same place + let currentBestScoreText = "" + population.getCurrentBestPlayerInBatch().score.toFixed(2); + let previousBestScoreText = "" + population.bestScoreOfAPreviousBatch.toFixed(2); + + while (currentBestScoreText.length < previousBestScoreText.length) { + currentBestScoreText = " " + currentBestScoreText; + } + while (previousBestScoreText.length < currentBestScoreText.length) { + previousBestScoreText = " " + previousBestScoreText; + } + + //if batches are a thing then show which batch we're on + if (population.numberOfBatches > 1) { + text(`Batch Number: ${population.batchNo + 1}/${population.numberOfBatches}`, startingX, startingY + gap); + text("Current Best Score : " + currentBestScoreText, startingX, startingY + gap * 2); + text("Previous Best Score: " + previousBestScoreText, startingX, startingY + gap * 3); + startingY = startingY + gap * 4; + } else { + text("Current Best Score : " + currentBestScoreText, startingX, startingY + gap * 1); + text("Previous Best Score: " + previousBestScoreText, startingX, startingY + gap * 2); + startingY = startingY + gap * 3; + } + + + // show the showing mode + switch (showMode) { + case 0: + text("Showing: All", startingX, startingY); + break; + case 1: + text("Showing: Best of previous generation", startingX, startingY); + break; + case 2: + text("Showing: Best of each species", startingX, startingY); + break; + case 3: + text("Showing: 20% of players", startingX, startingY); + break; + case 4: + text("Showing: Nothing (max performance)", startingX, startingY); + break; + + } + } + + + + // //display frame rate in the bottom left + // fill(255,255,0,200); + // noStroke(0); + // if(frameCount%10===0){ + // previousFrameRate = frameRate(); + // } + // text("FPS: " + round(previousFrameRate),20,canvas.height-30); + // + // pop(); + + +} + +let previousFrameRate = 60; + +//// TODO: +/* +- idea for new video is to have the brain just be a series of moves like the WHG AI, this ai could navigate much more difficult terrain and even play games like +mario or something +- another video idea is having players which evolve their bodies as well as their brains + + + + + +*/ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/LazerSoundEffect.wav b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/LazerSoundEffect.wav new file mode 100644 index 0000000..bf56e27 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/LazerSoundEffect.wav differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/burnedByLazer.mp3 b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/burnedByLazer.mp3 new file mode 100644 index 0000000..e3bc10e Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/burnedByLazer.mp3 differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/lazerShot.mp3 b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/lazerShot.mp3 new file mode 100644 index 0000000..3589136 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/lazerShot.mp3 differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/screamingSoundWithPause.wav b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/screamingSoundWithPause.wav new file mode 100644 index 0000000..f330791 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/AI learns to walk/sounds/screamingSoundWithPause.wav differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Body.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Body.js new file mode 100644 index 0000000..5c9d5f8 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Body.js @@ -0,0 +1,514 @@ +//this class contains all the logic for creating and controlling box2d bodies, it also contains all fixtures in this body and all body images attached to this body +class Body { + constructor(x, y, angle, isDynamic, worldParameter) { + this.x = x; + this.y = y; + this.angle = angle; + + this.isDynamic = isDynamic;//dynamic objects are effected by gravity and forces + //create body defintion + var bodyDef = new b2BodyDef(); + if (isDynamic) { + bodyDef.type = b2DynamicBody; + } else { + bodyDef.type = b2StaticBody; + } + + bodyDef.position.x = x / SCALE; + bodyDef.position.y = y / SCALE; + bodyDef.angle = angle; + this.bodyDef = bodyDef; + + if (worldParameter) {//if the box2dWorld parameter is not null then this body has its own box2dWorld + this.world = worldParameter; + + } else { //otherwise it uses the global box2dWorld + this.world = world.box2dWorld; + } + + //add this body to the box2d world + this.body = this.world.CreateBody(this.bodyDef); + this.body.SetUserData(this); + + this.fixtures = [];// all the fixtures that make up this body + + //selection variables + this.selected = false; + this.selectedAsShape1 = false; + + this.removed = false; + + //collision logic variables + this.collideWithAll = true; + this.bodiesToNotCollideWith = []; + + this.categoryBits = 0; + this.categoryMask = 0; + + //id is used to identify the ground object from others + this.id = ""; + this.isDead = false; + this.deathIfTouchesGround = false; + + + this.bodyImages = []; + + this.showImageObjects = true; + this.isOnScreen = true; + } + + //adds an image to this body + addBodyImage(bodyImage) { + this.bodyImages.push(bodyImage); + } + + //adds a rectangle fixture to this body + addRectFixture(x, y, w, h, angle) { + let fixture = new RectangleFixture(x, y, w, h, angle); + fixture.addToBody(this); + this.fixtures.push(fixture); + } + + //adds a circle fixture to this body + addCircleFixture(x, y, radius) { + let fixture = new CircleFixture(x, y, radius); + fixture.addToBody(this); + this.fixtures.push(fixture); + } + + //adds an array fixture (polygon) to this body + addArrayFixture(arr) { + let fixture = new CompoundFixture(arr); + fixture.addToBody(this); + this.fixtures.push(fixture); + } + + //shows all fixtures + showFixtures() { + for (var f of this.fixtures) { + f.show(this.body); + } + } + + + //shows the body + show() { + //show fixtures + this.showFixtures(); + + //if in creature creator show center dot + if (inCreatureCreatorMode) { + let x = this.body.GetPosition().x * SCALE; + let y = this.body.GetPosition().y * SCALE; + + push(); + fill(0, 0, 0, 50); + if (this.selected && !buttonManager.isInMode("Delete")) { + fill(255, 255, 0, 200); + } + noStroke(); + ellipse(x, y, 3); + pop(); + } + + //if this body isn't dead (i.e. if the player controlling it isn't dead) show the body images + if (!this.isDead) { + let x = this.body.GetPosition().x * SCALE; + let y = this.body.GetPosition().y * SCALE; + let angle = this.body.GetAngle(); + + push(); + translate(x, y); + rotate(angle); + + for (let bodyImage of this.bodyImages) { + bodyImage.showRelativeToBody(); + } + pop(); + + this.isOnScreen = x + panX > -10; + } + + + } + + //gets the pixel coordinates of the body + getPixelCoordinates() { + let x = this.body.GetPosition().x * SCALE + getPannedOffset().x; + let y = this.body.GetPosition().y * SCALE + getPannedOffset().y; + return createVector(x, y); + } + + //gets the pixel position of the body without accounting for panning + getShiftedPixelCoordinates() { + let x = this.body.GetPosition().x * SCALE; + let y = this.body.GetPosition().y * SCALE; + return createVector(x, y); + } + + //sets the position of the body + setPosition(newVec) { + let newVec2 = new Vec2((newVec.x - getPannedOffset().x) / SCALE, (newVec.y - getPannedOffset().y) / SCALE); //windowOffset.x - windowPadding) / SCALE, (newVec.y - windowOffset.y - windowPadding) / SCALE); + this.body.SetPosition(newVec2); + this.x = newVec2.x * SCALE; + this.y = newVec2.y * SCALE; + + this.bodyDef.position = newVec2; + for (var f of this.fixtures) { + f.setPixelCenter(); + } + creature.resetJointsAttachedToBody(this); + } + + //checks each fixture to see if pos is within them, used for the mouse selecting bodies + isShiftedPixelPosWithinFixtures(shiftedPixelCoords) { + let localPos = this.getLocalPixelCoordinatesOfPixelLocation(shiftedPixelCoords); + for (var f of this.fixtures) { + if (f.isLocalPixelPosWithinFixture(localPos)) { + return true; + } + } + + return false; + } + + //returns the local world coordinates of the argument vector + getLocalWorldCoordinatesOfPixelLocation(shiftedPixelCoords) { + let bodyPos = this.getShiftedPixelCoordinates(); + let relativePixelCoords = p5.Vector.sub(shiftedPixelCoords, bodyPos); + relativePixelCoords.rotate(-this.body.GetAngle()); + return new Vec2(relativePixelCoords.x / SCALE, relativePixelCoords.y / SCALE); + } + + + //returns the local pixel coordinates of the argument vector + getLocalPixelCoordinatesOfPixelLocation(shiftedPixelCoords) { + let bodyPos = this.getShiftedPixelCoordinates(); + let relativePixelCoords = p5.Vector.sub(shiftedPixelCoords, bodyPos); + relativePixelCoords.rotate(-this.body.GetAngle()); + return relativePixelCoords; + } + + //rotates the body + rotate(rotateAmount) { + this.body.SetAngle(this.body.GetAngle() + rotateAmount); + this.bodyDef.angle += rotateAmount; + this.angle += rotateAmount; + + for (var f of this.fixtures) { + f.setPixelCenter(); + } + + creature.resetJointsAttachedToBody(this); + } + + //resets the body, returning it to its starting position in the world + reset() { + this.world.DestroyBody(this.body); + this.body = this.world.CreateBody(this.bodyDef); + this.body.SetUserData(this); + for (var f of this.fixtures) { + f.addToBody(this); + } + + } + + //destroy this body, removing it from the box2d world + + remove() { + this.world.DestroyBody(this.body); + this.removed = true; + //remove all joints that were attached to this body + creature.removeJointsAttachedToADestroyedBody(); + } + + //clones the body and all its fixtures and body images + clone() { + //clone body + let clone = new Body(this.x, this.y, this.angle, this.isDynamic); + //clone fixtures + for (var f of this.fixtures) { + if (f.fixtureType === "rectangle") { + clone.addRectFixture(f.x, f.y, f.w, f.h, f.angle); + } else if (f.fixtureType === "circle") { + clone.addCircleFixture(f.x, f.y, f.radius); + } else { + + let vectorCopy = []; + for (var v of f.pixelVectorPositions) { + vectorCopy.push(createVector().set(v)); + } + clone.addArrayFixture(vectorCopy); + } + + clone.fixtures[clone.fixtures.length - 1].fillColor = f.fillColor; + + } + //clone body images + for (let bodyImage of this.bodyImages) { + bodyImage.clone().addToBody(clone); + } + return clone; + } + + //add all the fixtures and joints from the argument body to this body + addAllFixturesAndJointsFromBody(body) { + + //add all the fixtures + for (var f of body.fixtures) { + f.setPixelCenter(); + + let diff = p5.Vector.sub(f.pixelCenter, createVector(this.x, this.y)); + + + if (f.fixtureType === "rectangle") {//add a new rectangle fixture + + let vectors = f.getPixelVectors(); + for (let v of vectors) { + v.rotate(body.angle); + v.add(createVector(body.x, body.y)); + v.sub(createVector(this.x, this.y)); + v.rotate(-this.angle); + } + + let vectorCopy = []; + for (let v of vectors) { + vectorCopy.push(createVector().set(v)); + } + this.addArrayFixture(vectorCopy); + + } else if (f.fixtureType === "circle") { //adds a circle fixture + let v = createVector(f.x, f.y); + v.rotate(body.angle); + v.add(createVector(body.x, body.y)); + v.sub(createVector(this.x, this.y)); + v.rotate(-this.angle); + this.addCircleFixture(v.x, v.y, f.radius); + } else {//adds a polygon (array) fixture + let vectorCopy = []; + for (let v of f.pixelVectorPositions) { + + v.rotate(body.angle); + v.add(createVector(body.x, body.y)); + v.sub(createVector(this.x, this.y)); + v.rotate(-this.angle); + vectorCopy.push(createVector().set(v)); + } + this.addArrayFixture(vectorCopy); + } + //make sure the colors match + this.fixtures[this.fixtures.length - 1].fillColor = f.fillColor; + } + + //add all the joints + for (var i = 0; i < creature.joints.length; i++) { + if (creature.joints[i].body1 === body) { + creature.joints[i].body1 = this; + } + if (creature.joints[i].body2 === body) { + creature.joints[i].body2 = this; + } + + if (creature.joints[i].body2 === this && creature.joints[i].body1 === this) { + creature.joints[i].remove(); + creature.joints.splice(i, 1); + i--; + continue; + } + creature.joints[i].reset(); + } + } + + //adds a fixture to a body + addFixtureToBody(f) { + if (f.fixtureType === "rectangle") { + this.addRectFixture(f.x, f.y, f.w, f.h, f.angle); + } else if (f.fixtureType === "circle") { + this.addCircleFixture(f.x, f.y, f.radius); + } else { + let vectorCopy = []; + for (var v of f.pixelVectorPositions) { + vectorCopy.push(createVector().set(v)); + } + this.addArrayFixture(vectorCopy); + } + } + + //scale the body and all its fixtures and body images + scale(multiplyAmount) { + for (var f of this.fixtures) { + f.scaleRelativeToBody(multiplyAmount); + + } + print("HERE"); + for (let bodyImage of this.bodyImages) { + bodyImage.scaleRelativeToBody(multiplyAmount); + } + creature.resetJointsAttachedToBody(this); + } + + //remove collisions with another body + removeCollisionsWith(body2) { + if (this.bodiesToNotCollideWith.contains(body2)) { + return; + } + + this.bodiesToNotCollideWith.push(body2); + } + + //enable collisions with another body + allowCollisionsWith(body2) { + + let index = this.bodiesToNotCollideWith.indexOf(body2); + if (index !== -1) { + this.bodiesToNotCollideWith.remove(index); + } + } + + resetCategoryBits() { + this.categoryBits = Math.pow(2, this.getBodyNoAccountingForFloor()); + } + + setCategoryBitsFromGroupNo(i) { + this.categoryBits = Math.pow(2, i + 1);//dont forget the ground + } + + //resets the colision logic of the body based on collision variables + resetCategoryMask() { + + this.categoryMask = 1;//always collide with the ground + + + let bodiesToCollideWith = [...creature.bodies]; + + //filter out all bodies which are in this.bodiesToNotCollideWith + + if (allowBodyCollisions) { + bodiesToCollideWith = bodiesToCollideWith.filter((b) => !this.bodiesToNotCollideWith.contains(b)); + } else { + bodiesToCollideWith = []; //collide with no bodies + } + + + for (let b of bodiesToCollideWith) { + this.categoryMask = this.categoryMask | b.categoryBits; + } + } + + //resets the collision logic of the floor body based on collision variables + resetFilterDataForGround() { + let filterData = new b2FilterData(); + filterData.categoryBits = 1; + + let colGroups = world.getCollisionGroups(); + + this.categoryMask = 1; + for (let i = 0; i < colGroups.length; i++) { + this.categoryMask = this.categoryMask | colGroups[0].categoryBits; + } + + filterData.maskBits = this.categoryMask; + + for (let f of this.fixtures) { + f.setFilterData(filterData); + } + } + + //resets the collision logic of the body based on collision variables + resetFilterData() { + this.filterData = new b2FilterData(); + this.filterData.categoryBits = this.categoryBits; + this.filterData.maskBits = this.categoryMask; + + for (let f of this.fixtures) { + f.setFilterData(this.filterData); + f.resetFixture(); + } + } + + //gets the body number that it is in the bodies array with the +1 added for the floor + getBodyNoAccountingForFloor() { + for (var i = 0; i < creature.bodies.length; i++) { + if (creature.bodies[i] === this) { + return i + 1; + } + } + } + + //get body number + getBodyNo() { + for (var i = 0; i < creature.bodies.length; i++) { + if (creature.bodies[i] === this) { + return i; + } + } + } + + //returns all body information as an object + getBodyInfoAsObject() { + let obj = { + x: 0, + y: 0, + angle: 0, + isDynamic: true, + categoryBits: 0, + categoryMask: 0, + deathIfTouchesGround: false + }; + + Object.assignMatching(obj, this); + + obj.fixtures = []; + for (let f of this.fixtures) { + obj.fixtures.push(f.getFixtureInfoAsObject()) + } + obj.bodiesToNotCollideWith = []; + for (let b of this.bodiesToNotCollideWith) { + obj.bodiesToNotCollideWith.push(creature.getBodyNo(b)); + } + + obj.bodyImages = []; + for (let bodyImage of this.bodyImages) { + obj.bodyImages.push(bodyImage.getBodyImageAsObject()); + } + + return obj; + } + + //sets the fill color of all the fixtures in this body + setFillColorOfAllFixtures(color) { + for (let f of this.fixtures) { + f.fillColor = color; + } + } + + //sets the outline color of all the fixtures in this body + setOutlineColorOfAllFixtures(color) { + for (let f of this.fixtures) { + f.outlineColor = color; + } + } +} + + +Object.prototype.assignMatching = function (to, from) { + Object.keys(from).forEach((key) => { + if (key in to) { + to[key] = from[key]; + } + }); +}; + +Array.prototype.contains = function (o) { + for (let i = 0; i < this.length; i++) { + if (this[i] === o) { + return true; + } + } + return false; +}; + + +Array.prototype.remove = function (index) { + this.splice(index, 1); +}; \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Box2dGlobals.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Box2dGlobals.js new file mode 100644 index 0000000..dfe1dbe --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Box2dGlobals.js @@ -0,0 +1,32 @@ +//all the boring global variables which make it easier to use box2d stuff +var Vec2 = Box2D.Common.Math.b2Vec2; +var b2BodyDef = Box2D.Dynamics.b2BodyDef; +var b2Body = Box2D.Dynamics.b2Body; +var b2FixtureDef = Box2D.Dynamics.b2FixtureDef; +var b2Fixture = Box2D.Dynamics.b2Fixture; +var b2World = Box2D.Dynamics.b2World; +var b2MassData = Box2D.Collision.Shapes.b2MassData; +var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape; +var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape; +var b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef; + +var b2DebugDraw = Box2D.Dynamics.b2DebugDraw; +var b2StaticBody = Box2D.Dynamics.b2Body.b2_staticBody; +var b2DynamicBody = Box2D.Dynamics.b2Body.b2_dynamicBody; +var b2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint; +var b2MouseJoint = Box2D.Dynamics.Joints.b2MouseJoint; + +var b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef; + +var b2PrismaticJoint = Box2D.Dynamics.Joints.b2PrismaticJoint; + +var b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef; +var b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef; + +var b2FilterData = Box2D.Dynamics.b2FilterData; + +var b2DistanceJoint = Box2D.Dynamics.Joints.b2DistanceJoint; +var b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef; + +var b2WeldJoint = Box2D.Dynamics.Joints.b2WeldJoint; +var b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef; diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/CompoundFixture.js b/websiteUsingBulma/ProjectSketches/Creature Creator/CompoundFixture.js new file mode 100644 index 0000000..8bb796d --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/CompoundFixture.js @@ -0,0 +1,445 @@ +//a compond fixture is an array fixture which can handle its input being concave +//it does this by splitting up the original concave polygon into a group of smaller polygons which arent concave +class CompoundFixture extends Fixture { + //information is relative to the body + constructor(arr) { + super(); + this.fixtures = []; + this.concavePoints = []; + this.maybeConcave = []; + this.fixtureType = "array"; + this.pixelVectorPositions = arr; + this.pixelVectorsLeft = []; + for (let a of arr) { + this.pixelVectorsLeft.push(cloneVector(a)); + } + this.center; + this.setCenter(); + this.ensureClockwise(); + //if this fixture is concave then we need to spit it into multiple sub Fixtures for the simulation to work + this.ensureConvex(); + + + } + + //scales the fixture(s) relative to the body + scaleRelativeToBody(multiplyAmount) { + + let zeroVector = createVector(0, 0); + let vectorsRelativeToCenter = []; + + for (let v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, zeroVector)); + } + + for (let v of vectorsRelativeToCenter) { + v.mult(multiplyAmount); + } + + for (let i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i] = p5.Vector.add(vectorsRelativeToCenter[i], zeroVector); + } + + + for (let f of this.fixtures) { + f.scaleRelativeToBody(multiplyAmount); + } + this.setCenter(); + } + + //returns whether or not the pixel position is within the fixture(s) + isLocalPixelPosWithinFixture(localPixelPosition) { + let distance = dist(localPixelPosition.x, localPixelPosition.y, this.center.x, this.center.y); + let vectorsRelativeToCenter = []; + + for (let v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, this.center)); + } + let totalLength = 0; + for (let v of vectorsRelativeToCenter) { + totalLength += v.mag(); + } + let averageLength = totalLength / this.pixelVectorPositions.length; + return (distance < averageLength); + } + + //ensures that the polygon is concave by breaking it up + ensureConvex() { + + //1. identify all concave points + this.identifyConcavePoints(); + //2. once every concave point is labled then for each concave point with a convex point after it we can create a triangle out of the next 2 points + for (let i = 0; i < this.concavePoints.length; i++) { + //if this point is eithe concave or maybe concave + if (this.concavePoints[i] || this.maybeConcave[i]) { + let convexNeighbour = this.getConvexNeighbour(i); + if (convexNeighbour !== 0) { + //break this puppy up + + //create a new triangle out of the convex neighbour and the point after that + let newArrayFixture = []; + newArrayFixture.push(cloneVector(this.pixelVectorsLeft[evansMod(i + convexNeighbour * 2, this.pixelVectorsLeft.length)])); + newArrayFixture.push(cloneVector(this.pixelVectorsLeft[evansMod(i + convexNeighbour, this.pixelVectorsLeft.length)])); + newArrayFixture.push(cloneVector(this.pixelVectorsLeft[i])); + + this.fixtures.push(new ArrayFixture(newArrayFixture)); + this.pixelVectorsLeft.splice(evansMod(i + convexNeighbour, this.pixelVectorsLeft.length), 1); + break; + } + } + //if reached the end of the array without finding a concave point then we are good + if (i === this.concavePoints.length - 1) { + this.fixtures.unshift(new ArrayFixture(this.pixelVectorsLeft)); + let thing = []; + thing.push(0); + thing.push(0); + + thing.push(0); + thing.push(0); + thing.push(0); + thing.push(0); + thing.push(0); + thing.unshift(1); + + + return; + } + } + + this.ensureConvex(); + //3. remove triangle from overall fixture and add it to subFixtures + //4. repeat until no concave points remain + } + + //returns -1 if counter clockwise neighbour is convex + //returns 1 if clockwise neighbour is convex + // otherwise returns 0 + getConvexNeighbour(i) { + + let mod = this.concavePoints.length; + if (!this.concavePoints[evansMod(i - 1, mod)] && !this.maybeConcave[evansMod(i - 1, mod)]) { + return -1; + } + if (!this.concavePoints[(i + 1) % this.concavePoints.length] && !this.maybeConcave[(i + 1) % this.concavePoints.length]) { + return 1; + } + return 0; + } + + + //identifies concave points in the main polygon + identifyConcavePoints() { + + //for each point in the fixture we need to remove it then calculate the area if the area is greater without it then that point was a concave point + this.concavePoints = []; + this.maybeConcave = []; + let originalArea = this.calculateArea(this.pixelVectorsLeft); + let concavePointLabels = []; + for (let i = 0; i < this.pixelVectorsLeft.length; i++) { + let newVecs = []; + for (let v of this.pixelVectorsLeft) { + if (v !== this.pixelVectorsLeft[i]) { + newVecs.push(v); + } + } + //if removing this point creates a line cross then our algorithm for calculating area doesnt work, so we need to assume its a concave point + this.maybeConcave.push(this.anyLinesCross(newVecs)); + //if removing this point increases the area of the shape then this point is concave + this.concavePoints.push(originalArea < this.calculateArea(newVecs)); + } + } + + //calculates the area of the input vectors + calculateArea(vectors) { + let newVecs = []; + for (let v of vectors) { + newVecs.push(v); + } + newVecs.push(vectors[0]); + let areaSum = 0; + for (let i = 0; i < newVecs.length - 1; i++) { + areaSum += (newVecs[i + 1].x + newVecs[i].x) * (newVecs[i + 1].y - newVecs[i].y); + } + areaSum /= 2; + return abs(areaSum); + } + + //determines if any lines cross in the input polygon + anyLinesCross(vectors) { + let newVecs = []; + for (let v of vectors) { + newVecs.push(v); + } + newVecs.push(vectors[0]); + for (let i = 0; i < newVecs.length - 1; i++) { + for (let j = i + 1; j < newVecs.length - 1; j++) { + if (i === j || j + 1 === i || i + 1 === j || (i === 0 && j === newVecs.length - 2)) { + continue; + } + if (this.linesCross(newVecs[i].x, newVecs[i].y, newVecs[i + 1].x, newVecs[i + 1].y, newVecs[j].x, newVecs[j].y, newVecs[j + 1].x, newVecs[j + 1].y)) { + return true; + } + } + } + return false; + } + + //returns whether the 2 lines defined by the arguments cross + linesCross(x1, y1, x2, y2, x3, y3, x4, y4) { + const uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + const uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + return (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1); + } + + //if the points are going anticlockwise box2d shits itself so we need to ensure that that doesn't happen + ensureClockwise() { + let vectorsRelativeToCenter = []; + let headingValues = []; + for (let v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, this.center)); + } + + + for (let v of vectorsRelativeToCenter) { + let temp = v.heading(); + if (temp < 0) { + temp += 2 * PI; + } + + headingValues.push(temp); + } + //print(headingValues); + + let rotationalDifferenceTotal = 0; + for (let i = 0; i < headingValues.length; i++) { + let difference = 0; + if (i === headingValues.length - 1) { + difference = headingValues[0] - headingValues[i]; + } else { + difference = headingValues[i + 1] - headingValues[i]; + } + if (difference > 0) { + rotationalDifferenceTotal += 1; + } else { + rotationalDifferenceTotal -= 1; + } + } + + if (rotationalDifferenceTotal < 0) { + this.pixelVectorPositions.reverse(); + } + } + + //sets the shape of each fixture + setShape() { + for (let f of this.fixtures) { + f.setShape(); + } + } + + //sets the center as the average position of all the points + setCenter() { + let x = 0; + let y = 0; + for (let v of this.pixelVectorPositions) { + x += v.x; + y += v.y; + } + + x /= this.pixelVectorPositions.length; + y /= this.pixelVectorPositions.length; + this.center = createVector(x, y); + } + + //shows a polygon + showFixtureClass() { + + beginShape(); + for (let v of this.pixelVectorPositions) { + vertex(v.x, v.y); + } + endShape(CLOSE); + + } + + //moves all the points so the center is now in the new position + setPosition(newPos) { + let difference = p5.Vector.sub(newPos, this.pixelCenter); + let unchangedDifference = p5.Vector.sub(newPos, this.pixelCenter); + difference.rotate(-this.body.angle); + + for (let i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i].x += difference.x; + this.pixelVectorPositions[i].y += difference.y; + } + + this.setCenter(); + this.setPixelCenter(); + this.setShape(); + + for (let f of this.fixtures) { + f.setPosition(p5.Vector.add(unchangedDifference, f.pixelCenter)); + } + } + + + //rotates the polygon by getting each point as a vector then rotating it + rotate(rotateAmount) { + + + let vectorsRelativeToCenter = []; + for (let v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, this.center)); + } + + for (let v of vectorsRelativeToCenter) { + v.rotate(rotateAmount); + } + + for (let i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i] = p5.Vector.add(vectorsRelativeToCenter[i], this.center); + } + + for (let f of this.fixtures) { + f.rotate(rotateAmount, this.center); + } + } + + //resizes the fixture + resize(multAmount) { + + + let vectorsRelativeToCenter = []; + + for (let v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, this.center)); + } + + for (let v of vectorsRelativeToCenter) { + v.mult(multAmount); + } + + for (let i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i] = p5.Vector.add(vectorsRelativeToCenter[i], this.center); + } + + + for (let f of this.fixtures) { + f.resize(multAmount, this.center); + } + } + + //adds an array fixture to this fixture + addArrayFixture(arr){ + this.fixtures.push(new ArrayFixture(arr)); + } + + + //-------------------------------------OVERWRITES=------------------------------------------------------------- + + //sets the friction for all the fixtures in this compound fixture + setFriction(val) { + this.fixDef.friction = val; + for (let f of this.fixtures) { + f.setFriction(val); + } + } + + //sets the density for all the fixtures in this compound fixture + setDensity(val) { + this.fixDef.density = val; + for (let f of this.fixtures) { + f.setDensity(val); + } + + } + //sets the restitution for all the fixtures in this compound fixture + setRestitution(val) { + this.fixDef.restitution = val; + for (let f of this.fixtures) { + f.setRestitution(val); + } + } + + + setValues(friction, density, restitution) { + this.setFriction(friction); + this.setDensity(density); + this.setRestitution(restitution); + } + + //adds all the fixtures in this compound fixture to a body + addToBody(body) { + this.body = body; + this.setPixelCenter(); + for (let f of this.fixtures) { + f.addToBody(body); + } + } + + //resets all the fixtures in this compound fixture + resetFixture() { + for (let f of this.fixtures) { + f.resetFixture(); + } + } + + //sets the filterData for all the fixtures in this compound fixture + setFilterData(fd) { + + this.filterData = fd; + + for (let f of this.fixtures) { + f.setFilterData(fd); + } + } + + //removes all the fixtures in this compound fixture + remove() { + for (let f of this.fixtures) { + f.remove(); + } + } + + //gets this fixture as an object + getFixtureInfoAsObject(){ + let obj = {fixtureType:"compound", pixelVectorPositions:[], fillColor: this.fillColor}; + obj.pixelVectorPositions = this.pixelVectorPositions.map((x) => new createEvanVector(x)); + return obj; + + } + + //returns whether or not this fixture is hit by the lazer + hitByLazer(lazerX) { + + for (let pos of this.pixelVectorPositions) { + let pos2 = cloneVector(pos); + pos2.rotate(this.body.body.GetAngle()); + if (this.body.getShiftedPixelCoordinates().x + pos2.x < lazerX) { + return true; + } + } + return false; + } +} + +function p5VectorsToVec2(vectors) { + let newVecs = []; + for (let i = 0; i < vectors.length; i++) { + newVecs.push(new Vec2(vectors[i].x / SCALE, vectors[i].y / SCALE)); + } + return newVecs; +} + + +//mode function but its good +function evansMod(i, mod) { + while (i < 0) { + i += mod; + } + return i % mod; +} + +function cloneVector(vec) { + return createVector(vec.x, vec.y); +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/BodyImage.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/BodyImage.js new file mode 100644 index 0000000..c653a70 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/BodyImage.js @@ -0,0 +1,191 @@ +//this class holds the logic for displaying an image on an object +class BodyImage { + constructor(image, x, y, w, h, isScreaming) { + this.image = image; + this.x = x; + this.y = y; + this.w = w; + this.h = h; + + this.center = createVector(this.x + this.w / 2, this.y + this.h / 2); + this.angle = 0; + + this.centerRelativeToBody = createVector(0, 0); + this.angleRelativeToBody = 0; + this.body; + + this.selected = false; + this.selectedForDelete = false; + + this.isScreaming = isScreaming; + + } + + //returns the body image as a an object (like JSON) + getBodyImageAsObject(){ + this.updateGlobalPositionBasedOnBodyAndRelativePositioning(); + let obj = {image: null, x: 0, y: 0, w: 0, h: 0, angle: 0, angleRelativeToBody:0,centerRelativeToBody: 0}; + + Object.keys(this).forEach((key) => { + if (key in obj) { + obj[key] = this[key]; + } + }); + return obj; + } + + //creates a copy of ths body image + clone(){ + this.updateGlobalPositionBasedOnBodyAndRelativePositioning(); + let clone = new BodyImage(this.image,this.x,this.y,this.w,this.h, this.isScreaming); + clone.angle = this.angle; + return clone; + } + + //adds this image to the body and set the relative positioning of this image + addToBody(body) { + + this.body = body; + this.updateRelativePositioningBasedOnBodyAndGlobalPositioning(); + body.addBodyImage(this); + } + + //uses the global positioning and the position of the body to calculate the relative positioning of the image + updateRelativePositioningBasedOnBodyAndGlobalPositioning() { + + this.centerRelativeToBody = p5.Vector.sub(this.center, createVector(this.body.x, this.body.y)); + this.centerRelativeToBody.rotate(-this.body.angle); + this.angleRelativeToBody = this.angle - this.body.angle; + } + + //uses the relativve positioning and the position of the body to calculate the global positioning of the image + updateGlobalPositionBasedOnBodyAndRelativePositioning() { + let temp = createVector(this.centerRelativeToBody.x, this.centerRelativeToBody.y) + temp.rotate(this.body.angle); + this.center = p5.Vector.add(temp, createVector(this.body.x, this.body.y)); + this.angle = this.angleRelativeToBody + this.body.angle; + this.x = this.center.x - this.w / 2; + this.y = this.center.y - this.h / 2; + + + } + + //adds the image to a new body + addToNewBody(newBody) { + this.updateGlobalPositionBasedOnBodyAndRelativePositioning(); + this.addToBody(newBody); + } + + //shows the image + show() { + push(); + translate(this.x + this.w / 2, this.y + this.h / 2); + rotate(this.angle); + + if (this.selected) { + rectMode(CENTER); + stroke(255, 255, 0, 100); + fill(255, 255, 0, 50); + rect(0, 0, this.w, this.h); + } + if (this.selectedForDelete) { + rectMode(CENTER); + stroke(255, 0, 0, 100); + fill(255, 0, 0, 50); + rect(0, 0, this.w, this.h); + } + imageMode(CENTER); + image(this.image, 0, 0, this.w, this.h); + pop(); + } + + //displays the image using its relative positioning + //assumed that the program has already translated to the bodies position + showRelativeToBody() { + push(); + translate(this.centerRelativeToBody.x, this.centerRelativeToBody.y); + rotate(this.angleRelativeToBody); + + if (this.selected) { + rectMode(CENTER); + stroke(255, 255, 0, 100); + fill(255, 255, 0, 50); + rect(0, 0, this.w, this.h); + } + if (this.selectedForDelete) { + rectMode(CENTER); + stroke(255, 0, 0, 100); + fill(255, 0, 0, 50); + rect(0, 0, this.w, this.h); + } + imageMode(CENTER); + image(this.image, 0, 0, this.w, this.h); + pop(); + + } + + //scales the image + scale(scaleAmount) { + + this.w *= scaleAmount; + this.h *= scaleAmount; + this.x = this.center.x - this.w / 2; + this.y = this.center.y - this.h / 2; + + + } + + //scales the image relative to the body, this means that it position changes as well + scaleRelativeToBody(scaleAmount) { + this.centerRelativeToBody.x *= scaleAmount; + this.centerRelativeToBody.y *= scaleAmount; + this.w *= scaleAmount; + this.h *= scaleAmount; + this.updateGlobalPositionBasedOnBodyAndRelativePositioning(); + } + + + //updates the global position of the image + setPosition(newX, newY) { + this.center = createVector(newX, newY); + this.x = this.center.x - this.w / 2; + this.y = this.center.y - this.h / 2; + if (this.body) + this.updateRelativePositioningBasedOnBodyAndGlobalPositioning(); + + } + + //rotates the body + rotate(rotateAmount) { + this.angle += rotateAmount; + + if (this.body) + this.updateRelativePositioningBasedOnBodyAndGlobalPositioning(); + + + } + + //returns whether the mouse is over the iamge + mouseIsOverImage() { + if (this.body) { + //make sure to update the global position just incase the body has moved + this.updateGlobalPositionBasedOnBodyAndRelativePositioning(); + + } + + + let mousePosition = createVector(mouseX, mouseY); + let positionRelativeToCenter = p5.Vector.sub(mousePosition, this.center); + positionRelativeToCenter.rotate(-this.angle); + let pos = p5.Vector.add(this.center, positionRelativeToCenter); + + return (pos.x > this.x && pos.x < this.x + this.w && pos.y > this.y && pos.y < this.y + this.h); + + + } + + //moves the image to the mouses positiion + moveToMousePosition() { + this.setPosition(mouseX - this.w / 2, mouseY - this.h / 2); + } +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Button.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Button.js new file mode 100644 index 0000000..027294d --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Button.js @@ -0,0 +1,376 @@ +class Button { + constructor(x, y, w, h, text) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.text = text; + + + //button background colors + this.inactiveColor = color(40); + this.hoverColor = color(20); + this.activeColor = color(0); + + //button text colors + this.inactiveTextColor = color(255, 255, 0,); + this.hoverTextColor = color(255, 255, 0); + this.activeTextColor = color(255, 255, 0); + + + this.maxTextSize = 18; + this.isClicked = false; + this.textSizeVariable = this.maxTextSize; + } + + //returns whether or not the mouse is over the button + mouseOverButton() { + return (this.x < mouseX && this.x + this.w > mouseX) && (this.y < mouseY && this.y + this.h > mouseY); + } + + //sets the fill() function based on the state of the button + setFillColor() { + switch (true) { + case this.mouseOverButton(): + fill(this.hoverColor); + break; + case this.isClicked: + fill(this.activeColor); + break; + case !this.isClicked: + fill(this.inactiveColor); + break; + } + } + + //sets the fill() function based on the state of the button + setTextColor() { + switch (true) { + case this.mouseOverButton(): + fill(this.hoverTextColor); + break; + case this.isClicked: + fill(this.activeTextColor); + break; + case !this.isClicked: + fill(this.inactiveTextColor); + break; + } + noStroke(); + } + + + //reduces text size to fit the text within the maxzTextWidth + setTextWidthToBeLessThan(maxTextWidth) { + //while the text width is greater than the width of the button reduce its size + let fullText = this.getLongestPossibleText(); + this.textSizeVariable = this.maxTextSize; + textSize(this.textSizeVariable); + while (textWidth(fullText) > maxTextWidth) { + this.textSizeVariable--; + textSize(this.textSizeVariable); + } + } + + + //shows the button + show() { + push(); + //show background color based on button activity + this.setFillColor(); + stroke(255, 255, 0); + strokeWeight(1); + if (this.isClicked) + strokeWeight(3); + rect(this.x, this.y, this.w, this.h, 2); + + //show image + //image(buttonImage, this.x, this.y, this.w, this.h); + + //show text + textAlign(LEFT, CENTER); + this.setTextColor(); + this.setTextWidthToBeLessThan(this.w - 20); + + if (this.textSizeVariable < 12) + text(this.getText(), this.x + 10, this.y + this.h / 2 - 2); + else + text(this.getText(), this.x + 10, this.y + this.h / 2 - 3); + pop(); + } + + + getText() { + return this.text; + } + + //some buttons have changing text therefore to stop the text size and position from constantly changing then use the longest possible text to set these values + getLongestPossibleText() { + return this.text; + } + + //overwrite + onClick() { + } + + +} + +//a mode button is a button which once pressed enters a mode +//e.g. add circle is a mode +class ModeButton extends Button { + constructor(x, y, w, h, text, mode, buttonNo) { + super(x, y, w, h, text); + + this.isClicked = false; + this.mode = mode; + mode.button = this; + mode.modeNumber = buttonNo; + } + + + //if unclicked then deactivate mdoe + //else active this mode + onClick() { + this.isClicked = !this.isClicked; + if (this.isClicked) { + world.reset(); + buttonManager.deactivateActiveModes(); + this.mode.activate(); + } else { + this.mode.deactivate(); + } + } +} + +//mode class has many functions to be overwriten by each mode +class Mode { + constructor() { + this.isActive = false; + this.instructions = new Instruction(); + + //called when the mode is first entered + this.onActivate = function () { + }; + + //called when the mode is deactivated + this.onDeactivate = function () { + }; + + //called every frame, used for drawing effects of the mode + this.drawEffects = function () { + }; + + //called every frame + this.everyFrame = function () { + }; + + //called whenever the user clicks + this.onClick = function () { + }; + + //called whenever a key is pressed + this.buttonPressed = function () { + }; + + //called whenever the mouse wheel is moved + this.scrollWheel = function () { + }; + + + this.button = null; + this.modeNumber = 0; + + //deactivate this mode + this.deactivate = function () { + this.isActive = false; + this.button.isClicked = false; + buttonManager.modeNo = -1; + this.onDeactivate(); + }; + + //activate this mode + this.activate = function () { + this.isActive = true; + this.button.isClicked = true; + buttonManager.modeNo = this.modeNumber; + this.onActivate(); + }; + + //shows the instructions for this mode + this.showInstructions = function () { + this.instructions.show(); + }; + + this.name = ""; + } +} + +//a button which allows the user to control a value with - + buttons +class ValueButton extends Button { + /* + button design + | text | - | + | + + sizes + || h | h | + + */ + constructor(x, y, textWidth, h, text) { + + super(x, y, textWidth + 2 * h, h, text); + + this.textWidth = textWidth; + this.minusStartX = x + textWidth; + this.plusStartX = x + textWidth + h; + this.hoverColor = color(0); + + } + + + //overwrites button.show() + show() { + push(); + //---------text box + //show text background box + fill(this.inactiveColor); + stroke(255, 255, 0); + strokeWeight(1); + rect(this.x, this.y, this.textWidth, this.h, 2); + //show text + textAlign(LEFT, CENTER); + fill(this.inactiveTextColor); + noStroke(); + this.setTextWidthToBeLessThan(this.textWidth - 20); + text(this.getText(), this.x + 10, this.y + this.h / 2 - 2); + + //---------Minus Box + //show minus box + stroke(255, 255, 0); + this.mouseOverMinus() ? fill(this.hoverColor) : fill(this.inactiveColor); + rect(this.minusStartX, this.y, this.h, this.h); + //show minus text + noStroke(); + textSize(25); + this.mouseOverMinus() ? fill(this.hoverTextColor) : fill(this.inactiveTextColor); + text(`-`, this.minusStartX + 7, this.y + this.h / 2 - 5); + + //--------Plus Box + //show plus box + stroke(255, 255, 0); + this.mouseOverPlus() ? fill(this.hoverColor) : fill(this.inactiveColor); + rect(this.plusStartX, this.y, this.h, this.h, 0, 2, 2, 0); + //show minus text + textSize(25); + noStroke(); + this.mouseOverPlus() ? fill(this.hoverTextColor) : fill(this.inactiveTextColor); + text(`+`, this.plusStartX + 8, this.y + this.h / 2 - 5); + + pop(); + } + + //overwrites button super class + getLongestPossibleText() { + return this.text + ": 999"; + } + + //overwrites button super class + + getText() { + return `${this.text}: ${this.getValue()}`; + } + + mouseOverMinus() { + return (this.minusStartX < mouseX && this.plusStartX > mouseX) && (this.y < mouseY && this.y + this.h > mouseY); + } + + mouseOverPlus() { + return (this.plusStartX < mouseX && this.x + this.w > mouseX) && (this.y < mouseY && this.y + this.h > mouseY); + } + + //overwrites button super class + onClick() { + if (this.mouseOverMinus()) { + this.decreaseValue(); + } else if (this.mouseOverPlus()) { + this.increaseValue(); + } + } + + //overwrite + getValue() { + return 0; + } + + //overwrite + increaseValue() { + }; + + //overwrite + decreaseValue() { + }; + +} + +//a toogle button allows the user to control a boolean value +class ToggleButton extends Button { + + constructor(x, y, w, h, text, isClicked) { + super(x, y, w, h, text); + this.isClicked = isClicked; + } + + //overwrites button super class + show() { + push(); + //show background color based on button activity + this.setFillColor(); + stroke(255, 255, 0); + strokeWeight(1); + rect(this.x, this.y, this.w, this.h, 2); + + //show text + textAlign(LEFT, CENTER); + this.setTextColor(); + this.setTextWidthToBeLessThan(this.w - this.h - 10); + + if (this.textSizeVariable < 12) + text(this.getText(), this.x + 10, this.y + this.h / 2 - 2); + else + text(this.getText(), this.x + 10, this.y + this.h / 2 - 3); + + + //show isClicked square + this.isClicked ? fill(255, 255, 0) : noFill(); + stroke(255, 255, 0); + + let padding = this.h / 4; + rect(this.x + this.w - this.h + padding, this.y + padding, this.h - 2 * padding, this.h - 2 * padding); + + + pop(); + } + + + //overwrites button super class + getValue() { + return this.isClicked; + } + + //overwrite + toggleOn() { + }; + + //overwrite + toggleOff() { + }; + + //overwrites button super class + onClick() { + if (this.mouseOverButton()) { + this.isClicked ? this.toggleOff() : this.toggleOn(); + this.isClicked = !this.isClicked; + } + } +} + diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/AllowBodyCollisionsToggleButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/AllowBodyCollisionsToggleButton.js new file mode 100644 index 0000000..cb50f77 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/AllowBodyCollisionsToggleButton.js @@ -0,0 +1,16 @@ +//this button toggles the allow body collisions boolean +function generateAllowBodyCollisionsToggleButton(x, y, w, h){ + let toggleButton = new ToggleButton(x, y, w, h, "Allow Body Collisions", true); + + toggleButton.toggleOn = () => { + allowBodyCollisions = true; + world.resetBodyCollisions(); + }; + + toggleButton.toggleOff = () => { + allowBodyCollisions = false; + world.resetBodyCollisions(); + }; + + return toggleButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/BasicOptionsButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/BasicOptionsButton.js new file mode 100644 index 0000000..c63b7c1 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/BasicOptionsButton.js @@ -0,0 +1,12 @@ +//returns to the default buttons +function generateBasicOptionsButton(x, y, w, h, modeNumber){ + let buttonText = "Basic Options"; + let mode = new Mode(); + mode.onActivate = function () { + buttonManager.deactivateActiveModes(); + buttonManager.activateDefaultButtons(); + this.deactivate(); + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/DeathUponFloorButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/DeathUponFloorButton.js new file mode 100644 index 0000000..fdf3003 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/DeathUponFloorButton.js @@ -0,0 +1,46 @@ +//this button allows the user to set the death upon floor property for certain bodies in the creature, during training if these bodies touch the floor then the player instantly dies +function generateDeathUponFloorButton(x, y, w, h, modeNumber){ + + let buttonText = "Death upon floor"; + let mode = new Mode(); + mode.onActivate = function () { + + }; + + mode.name = "Death upon floor"; + + mode.everyFrame = function () { + creature.selectBodyMouseIsOverExcluding(); + }; + + mode.onDeactivate = function () { + + this.bodies = []; + creature.unselectAllBodies(); + + }; + mode.onClick = function () { + + if (creature.selectedBody !== -1) { + creature.getSelectedBody().deathIfTouchesGround = !creature.getSelectedBody().deathIfTouchesGround; + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + } + }; + + mode.instructions.getMessages = function () { + let messages = []; + + messages.push("Select shapes which will kill the player if they touch the ground") + messages.push("CLICK: toggle ground kill on shape"); + messages.push("ESC: exit "); + return messages; + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/EditShapeLayersButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/EditShapeLayersButton.js new file mode 100644 index 0000000..2c6b153 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/EditShapeLayersButton.js @@ -0,0 +1,63 @@ +//this button allows the user to reorder the bodies so they appear in different orders +function generateEditShapeLayersButton(x, y, w, h, modeNumber){ + let buttonText = "Edit shape layers"; + let mode = new Mode(); + mode.bodies = []; + + mode.onActivate = function () { + + }; + + + mode.everyFrame = function () { + creature.selectBodyMouseIsOverExcluding(...this.bodies.map((b) => creature.getBodyNo(b))); + }; + + mode.onDeactivate = function () { + + this.bodies = []; + creature.unselectAllBodies(); + + }; + mode.onClick = function () { + + if (creature.selectedBody !== -1) { + this.bodies.push(creature.getSelectedBody()); + creature.getSelectedBody().selectedAsShape1 = true; + + if (this.bodies.length === creature.bodies.length) { + creature.bodies = this.bodies; + + buttonManager.deactivateActiveModes(); + } + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + if (this.bodies.length === 0) { + buttonManager.deactivateActiveModes(); + } else { + this.bodies[this.bodies.length - 1].selectedAsShape1 = false; + this.bodies.splice(this.bodies.length - 1, 1);//remove last element of this.bodies + + } + break; + } + }; + + mode.instructions.getMessages = function () { + let messages = []; + + messages.push("Select the order the shapes should be displayed from back to front"); + messages.push("CLICK: select shape"); + if (buttonManager.getCurrentMode().bodies.length === 0) { + messages.push("ESC: exit "); + } else { + messages.push("ESC: unselect shape") + } + return messages; + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/LimitJointsButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/LimitJointsButton.js new file mode 100644 index 0000000..728a256 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/LimitJointsButton.js @@ -0,0 +1,162 @@ +//this button allows the user to set limits for all the joints to prevent all creatures from becoming cars +function generateLimitJointsButton(x, y, w, h, modeNumber){ + + let buttonText = "Limit Joints"; + + let mode = new Mode(); + mode.selectedJoint = -1; + mode.onActivate = function () { + if (creature.joints.length === 0) { + warning = new Warning("Limiting joints requires joints so add joints", 250); + } + + }; + + //either select a joint if one isnt already selected or select a joint limit or move a limit + mode.everyFrame = function () { + + if (this.selectedJoint === -1) { + creature.selectJointMouseIsOver(); + } else { + if (!dragging) { + this.selectJointLimitMouseIsOver(); + + } else { + + creature.getSelectedJoint().moveSelectedLimitToMousePosition(); + } + } + }; + //selects the limit which is closes to the mouse, also "fake" selects another joint + mode.selectJointLimitMouseIsOver = function () { + for (let j of creature.joints) { + j.lookLikeSelected = false; + } + creature.joints[this.selectedJoint].selectLimitClosestToMousePosition(); + }; + + //unselect everything + mode.onDeactivate = function () { + if (this.selectedJoint !== -1) { + creature.joints[this.selectedJoint].body2.selected = false; + creature.joints[this.selectedJoint].selectedLimit = -1; + } + this.selectedJoint = -1; + creature.unselectAllJoints(); + for (let j of creature.joints) { + j.lookLikeSelected = false; + } + + }; + + + mode.onClick = function () { + + if (this.selectedJoint === -1) { + + + if (creature.selectedJoint !== -1) { + this.selectedJoint = creature.selectedJoint; + creature.getSelectedJoint().enableLimits(true); + creature.getSelectedJoint().body2.selected = true; + dragging = false; + + } + + + } else { + + + //if another joint is clicked on then select it instead. + + + let minIndex = -1; + let min = 100000; + + + for (var i = 0; i < creature.joints.length; i++) { + let pos = creature.joints[i].getPixelCenter(); + let distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && distance < 8) { + min = distance; + minIndex = i; + } + } + + + if (minIndex !== -1 && minIndex !== this.selectedJoint) { + + //unselect the current joint + //just copied from enter + creature.joints[this.selectedJoint].body2.selected = false; + creature.joints[this.selectedJoint].selectedLimit = -1; + this.selectedJoint = -1; + creature.unselectAllJoints(); + + //select the joint the mouse is over + creature.selectedJoint = minIndex; + this.selectedJoint = creature.selectedJoint; + creature.getSelectedJoint().enableLimits(true); + creature.getSelectedJoint().body2.selected = true; + dragging = false; + + } + + + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + + case ESCAPE://exits + case ENTER: + if (this.selectedJoint === -1) { + buttonManager.deactivateActiveModes(); + } else { + creature.joints[this.selectedJoint].body2.selected = false; + creature.joints[this.selectedJoint].selectedLimit = -1; + this.selectedJoint = -1; + creature.unselectAllJoints(); + } + break; + case TAB://changes focus shape + if (this.selectedJoint != -1) { + creature.joints[this.selectedJoint].switchBodies(); + creature.joints[this.selectedJoint].body1.selected = false; + creature.joints[this.selectedJoint].body2.selected = true; + } + break; + } + switch (key) { + case 'D'://removes joint limits + if (this.selectedJoint !== -1) { + creature.getSelectedJoint().enableLimits(false); + creature.joints[this.selectedJoint].selectedLimit = -1; + this.selectedJoint = -1; + creature.unselectAllJoints(); + warning = new Warning("Selected joint's limits are disabled", 100, true); + } + break; + + } + }; + + mode.instructions.getMessages = function () { + let messages = []; + + if (buttonManager.getCurrentMode().selectedJoint === -1) { + messages.push("Select Joint to edit limits"); + messages.push("CLICK: select joint"); + messages.push("ESC: exit "); + } else { + messages.push("Edit Joint Limits"); + messages.push("CLICK AND DRAG: move highlighted limit"); + messages.push("D: disable limits for this joint"); + messages.push("TAB: change focused shape"); + messages.push("ESC/ENTER: unselect joint") + } + return messages; + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/ShapeCollisionsButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/ShapeCollisionsButton.js new file mode 100644 index 0000000..3775bc8 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/ShapeCollisionsButton.js @@ -0,0 +1,102 @@ +//this button allows the user to remove collisions between certain bodies +function generateShapeCollisionsButton(x, y, w, h, modeNumber){ + let buttonText = "Shape Collisions"; + let mode = new Mode(); + mode.selectedShape1 = -1; + mode.selectedShape2 = -1; + mode.removeCollision = true; + + mode.onActivate = function () { + if (creature.bodies.length < 2) { + warning = new Warning("This button is used for adding or removing collisions between 2 shapes \n " + + "therefore you are gonna need 2 shapes", 400); + } + }; + + mode.everyFrame = function () { + creature.selectBodyMouseIsOverExcluding(this.selectedShape1); + }; + + mode.onDeactivate = function () { + this.selectedShape1 = -1; + this.selectedShape2 = -1; + }; + + mode.onClick = function () { + + if (creature.selectedBody !== -1) { + if (this.selectedShape1 === -1) { + this.selectedShape1 = creature.selectedBody; + creature.getSelectedBody().selectedAsShape1 = true; + } else { + this.selectedShape2 = creature.selectedBody; + + + if (this.removeCollision) { + creature.bodies[this.selectedShape1].removeCollisionsWith(creature.bodies[this.selectedShape2]); + creature.bodies[this.selectedShape2].removeCollisionsWith(creature.bodies[this.selectedShape1]); + } else { + creature.bodies[this.selectedShape1].allowCollisionsWith(creature.bodies[this.selectedShape2]); + creature.bodies[this.selectedShape2].allowCollisionsWith(creature.bodies[this.selectedShape1]); + } + let clusterFuck = world.getCollisionGroups().length > 12; + + world.resetBodyCollisions(); + creature.unselectEverything(); + + this.selectedShape1 = -1; + this.selectedShape2 = -1; + + if (clusterFuck) { + warning = new Warning("Wow nice clusterfuck of collision logic you got there\n prepare for shit to hit the fan if you keep this up", 500); + + } else { + if (this.removeCollision) { + warning = new Warning("Collisions between shapes removed", 100, true); + } else { + warning = new Warning("Collisions between shapes added", 100, true); + } + } + } + } + }; + + + mode.buttonPressed = function () { + switch (keyCode) { + case TAB: + this.removeCollision = !this.removeCollision; + + break; + + case ESCAPE: + + if (this.selectedShape1 === -1) { + buttonManager.deactivateActiveModes(); + } else { + this.selectedShape1 = -1; + creature.unselectAllBodies(); + } + break; + } + }; + mode.instructions.getMessages = function () { + let messages = []; + let onClickDescription = (buttonManager.getCurrentMode().removeCollision) ? "remove a collision from" : "add a collision to"; + let addOrRemove = (buttonManager.getCurrentMode().removeCollision) ? "Remove" : "Add"; + + messages.push(`${addOrRemove} collisions between 2 shapes`); + if (buttonManager.getCurrentMode().selectedShape1 === -1) { + messages.push(`CLICK: select first shape to ${onClickDescription}`); + messages.push("TAB: toggle between adding and removing collisions"); + messages.push("ESC: cancel"); + } else { + messages.push(`CLICK: select second shape to ${onClickDescription}`); + messages.push("TAB: toggle between adding and removing collisions"); + messages.push("ESC: unselect first shape"); + } + return messages; + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/ShowJointLimitsToggleButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/ShowJointLimitsToggleButton.js new file mode 100644 index 0000000..f1ec41e --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/AdvancedButtons/ShowJointLimitsToggleButton.js @@ -0,0 +1,14 @@ +//toggles the show joint limits boolean +function generateShowJointLimitsToggleButton(x, y, w, h){ + let toggleButton = new ToggleButton(x, y, w, h, "Show Joint Limits", true); + + toggleButton.toggleOn = () => { + showJointLimits = true; + }; + + toggleButton.toggleOff = () => { + showJointLimits = false; + }; + + return toggleButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/ConstructionButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/ConstructionButton.js new file mode 100644 index 0000000..1601d4d --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/ConstructionButton.js @@ -0,0 +1,17 @@ +//this button returns the player to the default buttons +function generateConstructionButton(x, y, w, h, modeNumber) { + + let buttonText = "Construction"; + let mode = new Mode(); + + mode.onActivate = function () { + buttonManager.deactivateActiveModes(); + buttonManager.activateDefaultButtons(); + if (creature.selectedBodyToEditCosmetically !== -1) { + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = false; + + } + + }; + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/AddImagesToShapeButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/AddImagesToShapeButton.js new file mode 100644 index 0000000..2842ad4 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/AddImagesToShapeButton.js @@ -0,0 +1,123 @@ +//this button allows the user to add images to the creature +function generateAddImageToShapeButton(x, y, w, h, modeNumber) { + let buttonText = "Add Image To Shape"; + let mode = new Mode(); + mode.hotBar = new HotBar(200, 70, canvas.width - 400, 60, 7); + mode.hotBar.addImageButtons(); + mode.inScaleMode = true; + + //on activation automatically select the bodypart last selected during the previous activation + mode.onActivate = function () { + this.hotBar.unclickAllButtons(); + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = true; + + + }; + mode.onDeactivate = function () { + this.hotBar.unclickAllButtons(); + }; + + //if the hotbar has not selected a button then select the body part the mouse is over, + mode.everyFrame = function () { + if (!this.hotBar.hasSelectedItem()) { + creature.selectBodyMouseIsOverExcluding(creature.selectedBodyToEditCosmetically); + } + }; + + //draws the hotbar + mode.drawEffects = function () { + this.hotBar.show(); + }; + + //if no hotbar button is selected then select the body the mouse is over as shape 1 and as selecedBodyTOEditCosmetically + //if a hotbar button is selected and the mouse is not over the hotbar(this prevents the item from being immediately placed) the image is attached to the selected body + mode.onClick = function () { + this.hotBar.onClick(); + + if (this.hotBar.selectedButton === -1) { + if (creature.selectedBody === -1) {//if the mouse isnt over a body + return; + } + + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = false; + + creature.selectedBodyToEditCosmetically = creature.selectedBody; + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = true; + creature.bodies[creature.selectedBodyToEditCosmetically].selected = false; + } else if (!this.hotBar.mouseOverHotBar()) { + let button = this.hotBar.getCurrentlySelectedButton(); + button.mode.bodyImage.addToBody(creature.bodies[creature.selectedBodyToEditCosmetically]); + button.onClick(); + this.hotBar.unclickAllButtons(); + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + case TAB: + this.inScaleMode = !this.inScaleMode; + break; + case ESCAPE: + if (this.hotBar.hasSelectedItem()) { + this.hotBar.unclickAllButtons(); + } else { + buttonManager.deactivateActiveModes(); + } + break; + } + }; + + + //when the scroll wheel is moved either scale or rotate the image depending on what state the mode is in + mode.scrollWheel = function (mouseDirection) { + if (this.hotBar.selectedButton !== -1) { + if (this.inScaleMode) { + this.hotBar.getCurrentlySelectedButton().mode.bodyImage.scale(1 + (0.02 * shiftIncrease) * mouseDirection); + + } else { + this.hotBar.getCurrentlySelectedButton().mode.bodyImage.rotate(PI / 200 * shiftIncrease * mouseDirection); + } + + } + }; + + mode.instructions.getMessages = function () { + let messages = []; + + messages.push("Add Images To Selected Shape"); + + messages.push("TAB: Change Mouse Wheel Mode"); + + if (buttonManager.getCurrentMode().inScaleMode) { + messages.push("MOUSE WHEEL: SCALE picture"); + messages.push("HOLD SHIFT: increase scale speed"); + + } else { + messages.push("MOUSE WHEEL: ROTATE picture"); + messages.push("HOLD SHIFT: increase rotation speed"); + } + + if (buttonManager.getCurrentMode().hotBar.hasSelectedItem()) + messages.push("CLICK: add image"); + else + messages.push("CLICK: Select Shape"); + + + + + if (buttonManager.getCurrentMode().hotBar.hasSelectedItem()) { + messages.push("ESC: cancel"); + } else { + messages.push("ESC: exit"); + + } + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/ChangeFillColorButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/ChangeFillColorButton.js new file mode 100644 index 0000000..2c0acec --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/ChangeFillColorButton.js @@ -0,0 +1,69 @@ +function generateChangeFillColorButton(x, y, w, h, modeNumber) { + let buttonText = "Change Fill Color"; + let mode = new Mode(); + mode.hotBar = new HotBar(200, 70, canvas.width - 400, 60, 7); + mode.hotBar.addColorButtons(); + + //on activation automatically select the bodypart last selected during the previous activation + mode.onActivate = function () { + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = true; + + }; + mode.onDeactivate = function () { + }; + + //if the hotbar has not selected a button then select the body part the mouse is over, + mode.everyFrame = function () { + creature.selectBodyMouseIsOverExcluding(creature.selectedBodyToEditCosmetically); + + }; + + //draws the hotbar + mode.drawEffects = function () { + this.hotBar.show(); + }; + + //if no hotbar button is selected then select the body the mouse is over as shape 1 and as selecedBodyTOEditCosmetically + //if a hotbar button is selected and the mouse is not over the hotbar(this prevents the item from being immediately placed) the image is attached to the selected body + mode.onClick = function () { + this.hotBar.onClick(); + + if (this.hotBar.selectedButton === -1) { + if (creature.selectedBody === -1) {//if the mouse isnt over a body + return; + } + + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = false; + + creature.selectedBodyToEditCosmetically = creature.selectedBody; + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = true; + creature.bodies[creature.selectedBodyToEditCosmetically].selected = false; + } else { + if (creature.selectedBodyToEditCosmetically !== -1) { + let button = this.hotBar.getCurrentlySelectedButton(); + creature.bodies[creature.selectedBodyToEditCosmetically].setFillColorOfAllFixtures(button.color); + } + this.hotBar.unclickAllButtons(); + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + } + }; + + + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Change the fill Color of the selected shape"); + messages.push("CLICK: Select Shape To add Images To"); + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/DeleteImageButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/DeleteImageButton.js new file mode 100644 index 0000000..1164230 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/DeleteImageButton.js @@ -0,0 +1,60 @@ +//this button lets the user remove images +function generateDeleteImageButton(x, y, w, h, modeNumber) { + let buttonText = "Delete Image"; + let mode = new Mode(); + mode.selectedBody = -1; + mode.selectedBodyImage = -1; + + //on activation automatically select the body part last selected during the previous activation + mode.onActivate = function () { + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = false; + }; + + //when deactivated unselect the current image + mode.onDeactivate = function () { + this.selectedBodyImage = -1; + this.selectedBody = -1; + }; + + //if currently dragging and an image is selected then set the new position of the image + //else select images the mouse is over + mode.everyFrame = function () { + + creature.unselectAllBodyImages(); + let obj = creature.getBodyImageNoMouseIsOver(); + if (obj.bodyNo !== -1) { + this.selectedBody = obj.bodyNo; + this.selectedBodyImage = obj.bodyImageNo; + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].selectedForDelete = true; + } + + }; + + //when the mouse is clicked start dragging if an image is selected + mode.onClick = function () { + if (this.selectedBodyImage !== -1) { + creature.bodies[this.selectedBody].bodyImages.splice(this.selectedBodyImage, 1); + warning = new Warning("Image Deleted", 100, true); + this.selectedBodyImage = -1; + this.selectedBody = -1; + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + } + }; + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Delete Images"); + messages.push("CLICK: delete image"); + messages.push("ESC: exit "); + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/MoveResizeImageButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/MoveResizeImageButton.js new file mode 100644 index 0000000..85b81fe --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/MoveResizeImageButton.js @@ -0,0 +1,88 @@ +//this button lets the user move and resize images +function generateMoveResizeImageButton(x, y, w, h, modeNumber) { + let buttonText = "Move/Resize Image"; + let mode = new Mode(); + mode.selectedBody = -1; + mode.selectedBodyImage = -1; + + //on activation automatically select the body part last selected during the previous activation + mode.onActivate = function () { + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = false; + + + }; + + //when deactivated unselect the current image + mode.onDeactivate = function () { + this.selectedBodyImage = -1; + this.selectedBody = -1; + }; + + //if currently dragging and an image is selected then set the new position of the image + //else select images the mouse is over + mode.everyFrame = function () { + if (dragging && this.selectedBodyImage !== -1) { + let difference = createVector(mouseX - dragMouseFrom.x, mouseY - dragMouseFrom.y); + let newPosition = createVector(startingBodyPos.x + difference.x, startingBodyPos.y + difference.y); + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].setPosition(newPosition.x,newPosition.y); + } else { + creature.unselectAllBodyImages(); + let obj = creature.getBodyImageNoMouseIsOver(); + if (obj.bodyNo !== -1) { + this.selectedBody = obj.bodyNo; + this.selectedBodyImage = obj.bodyImageNo; + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].selected = true; + } + + } + + }; + + //when the mouse is clicked start dragging if an image is selected + mode.onClick = function () { + if (this.selectedBodyImage !== -1) { + dragMouseFrom = createVector(mouseX, mouseY); + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].updateGlobalPositionBasedOnBodyAndRelativePositioning(); + window.startingBodyPos = creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].center; + } + }; + + + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + + case ESCAPE: + + buttonManager.deactivateActiveModes(); + + break; + } + }; + + + //when the scroll wheel is moved scale the image which is currently selected + mode.scrollWheel = function (mouseDirection) { + if (this.selectedBodyImage !== -1) { + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].scale(1 + (0.02 * shiftIncrease) * mouseDirection); + } + }; + + mode.instructions.getMessages = function () { + let messages = []; + + messages.push("Move And Resize Images"); + messages.push("CLICK AND DRAG: move image"); + messages.push("MOUSE WHEEL: scale image"); + messages.push("HOLD SHIFT: increase scale speed"); + messages.push("ESC: exit "); + + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/MoveRotateImageButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/MoveRotateImageButton.js new file mode 100644 index 0000000..a34ffbc --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticButtons/MoveRotateImageButton.js @@ -0,0 +1,88 @@ +//this button lets the user move and rotate iamges +function generateMoveRotateImageButton(x, y, w, h, modeNumber) { + let buttonText = "Move/Rotate Image"; + let mode = new Mode(); + mode.selectedBody = -1; + mode.selectedBodyImage = -1; + + //on activation automatically select the body part last selected during the previous activation + mode.onActivate = function () { + if (creature.selectedBodyToEditCosmetically !== -1) + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = false; + + + }; + + //when deactivated unselect the current image + mode.onDeactivate = function () { + this.selectedBodyImage = -1; + this.selectedBody = -1; + }; + + //if currently dragging and an image is selected then set the new position of the image + //else select images the mouse is over + mode.everyFrame = function () { + if (dragging && this.selectedBodyImage !== -1) { + let difference = createVector(mouseX - dragMouseFrom.x, mouseY - dragMouseFrom.y); + let newPosition = createVector(startingBodyPos.x + difference.x, startingBodyPos.y + difference.y); + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].setPosition(newPosition.x,newPosition.y); + } else { + creature.unselectAllBodyImages(); + let obj = creature.getBodyImageNoMouseIsOver(); + if (obj.bodyNo !== -1) { + this.selectedBody = obj.bodyNo; + this.selectedBodyImage = obj.bodyImageNo; + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].selected = true; + } + + } + + }; + + //when the mouse is clicked start dragging if an image is selected + mode.onClick = function () { + if (this.selectedBodyImage !== -1) { + dragMouseFrom = createVector(mouseX, mouseY); + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].updateGlobalPositionBasedOnBodyAndRelativePositioning(); + window.startingBodyPos = creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].center; + } + }; + + + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + + case ESCAPE: + + buttonManager.deactivateActiveModes(); + + break; + } + }; + + + //when the scroll wheel is moved rotate the image which is currently selected + mode.scrollWheel = function (mouseDirection) { + if (this.selectedBodyImage !== -1) { + creature.bodies[this.selectedBody].bodyImages[this.selectedBodyImage].rotate(PI / 200 * shiftIncrease * mouseDirection); + } + }; + + mode.instructions.getMessages = function () { + let messages = []; + + messages.push("Move And Rotate Images"); + messages.push("CLICK AND DRAG: move image"); + messages.push("MOUSE WHEEL: rotate image"); + messages.push("HOLD SHIFT: increase rotation speed"); + messages.push("ESC: exit "); + + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticsButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticsButton.js new file mode 100644 index 0000000..88f98a9 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/CosmeticsButton.js @@ -0,0 +1,27 @@ + +//this button activated the cosmetic buttons +function generateCosmeticsButton(x, y, w, h, modeNumber) { + + let buttonText = "Cosmetics"; + let mode = new Mode(); + + mode.onActivate = function () { + + if (creature.bodies.length === 0) { + warning = new Warning("Ur gonna need to add some shapes and shit before you make it pretty", 250); + this.deactivate(); + return; + } + + buttonManager.deactivateActiveModes(); + buttonManager.activateCosmeticButtons(); + if(creature.bodies.length>0){ + creature.selectedBodyToEditCosmetically = 0; + creature.bodies[creature.selectedBodyToEditCosmetically].selectedAsShape1 = true; + } + + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddCircleButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddCircleButton.js new file mode 100644 index 0000000..eebad80 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddCircleButton.js @@ -0,0 +1,49 @@ +//adds a circle to the creature +function generateAddCircleButton(x, y, w, h, modeNumber) { + let buttonText = "Add Circle"; + let mode = new Mode(); + + mode.drawEffects = function () { + fill(selectedBodyFillColor); + stroke(selectedStrokeColor); + ellipse(mouseX, mouseY, newCircleSize * 2); + }; + + + mode.onClick = function () { + let circleBody = new Body(mouseX, mouseY, 0, true); + circleBody.addCircleFixture(0, 0, newCircleSize); + + creature.bodies.push(circleBody); + warning = new Warning("Circle Added", 100, true); + + }; + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + + } + }; + mode.scrollWheel = function (mouseDirection) { + newCircleSize = max(newCircleSize + (shiftIncrease) * mouseDirection, 1); + }; + + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Add Circle"); + messages.push("MOUSE WHEEL: scale circle"); + messages.push("HOLD SHIFT: increase scale speed"); + messages.push("ESC: cancel"); + return messages; + }; + + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddJointButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddJointButton.js new file mode 100644 index 0000000..e7dffe3 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddJointButton.js @@ -0,0 +1,101 @@ +//creates a joint between 2 bodies +function generateAddJointButton(x, y, w, h, modeNumber) { + let buttonText = "Add Joint"; + let mode = new Mode(); + + mode.selectedBody1 = -1; + mode.selectedBody2 = -1; + mode.jointAnchor = null; + + mode.onActivate = function () { + this.selectedBody1 = -1; + this.selectedBody2 = -1; + + + if (creature.bodies.length === 0) { + warning = new Warning("Mate, just a hint, why don't you add some shapes before you try and join them?", 300); + } + if (creature.bodies.length === 1) { + warning = new Warning("So you've got one shape, which is fantastic. \n However what are you gonna join it to?", 400); + } + }; + + mode.drawEffects = function () { + if (this.selectedBody2 !== -1) { + push(); + fill(0, 0, 0); + stroke(255, 255, 0); + strokeWeight(2); + ellipse(mouseX, mouseY, 5); + pop(); + } + }; + + mode.everyFrame = function () { + if (this.selectedBody2 === -1) { + creature.selectBodyMouseIsOverExcluding(this.selectedBody1); + } + }; + + + mode.onClick = function () { + if (creature.selectedBody !== -1) { + if (this.selectedBody1 === -1) { + this.selectedBody1 = creature.selectedBody; + creature.getSelectedBody().selectedAsShape1 = true; + } else if (this.selectedBody2 === -1) { + this.selectedBody2 = creature.selectedBody; + + } else if (this.jointAnchor == null) { + this.jointAnchor = getShiftedMousePos(); + var newRevoluteJoint = new RevoluteJoint(creature.bodies[this.selectedBody1], creature.bodies[this.selectedBody2], this.jointAnchor.x, this.jointAnchor.y); + creature.joints.push(newRevoluteJoint); + this.selectedBody1 = -1; + this.selectedBody2 = -1; + this.jointAnchor = null; + creature.unselectEverything(); + warning = new Warning("Joint Added", 100, true); + } + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + + case ESCAPE: + + if (this.selectedBody1 === -1) { + buttonManager.deactivateActiveModes(); + } else if (this.selectedBody2 === -1) { + this.selectedBody1 = -1; + creature.unselectAllBodies(); + } else { + this.selectedBody2 = -1; + } + + break; + + } + }; + mode.instructions.getMessages = function () { + + let currentMode = buttonManager.getCurrentMode(); + let messages = []; + if (currentMode.selectedBody1 === -1) { + messages.push("Select first shape to join"); + messages.push(" CLICK: select shape"); + messages.push(" ESC: cancel"); + } else if (currentMode.selectedBody2 === -1) { + messages.push("Select second shape to join"); + messages.push("CLICK: select shape"); + messages.push("ESC: unselect first shape"); + } else { + messages.push("Position rotation point"); + messages.push("CLICK: place rotation point"); + messages.push("ESC: unselect second shape"); + } + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddPolygonButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddPolygonButton.js new file mode 100644 index 0000000..4b620e5 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddPolygonButton.js @@ -0,0 +1,142 @@ +// adds a polygon to the creature +function generateAddPolygonButton(x, y, w, h, modeNumber) { + let buttonText = "Add Polygon"; + let mode = new Mode(); + + mode.polygonPositions = []; + + //draws the current lines of the polygon + mode.drawEffects = function () { + push(); + strokeWeight(2); + stroke(0, 0, 0); + for (var i = 0; i < this.polygonPositions.length - 1; i++) { + line(this.polygonPositions[i].x, this.polygonPositions[i].y, this.polygonPositions[i + 1].x, this.polygonPositions[i + 1].y); + } + if (this.polygonPositions.length > 0) { + line(this.polygonPositions[this.polygonPositions.length - 1].x, this.polygonPositions[this.polygonPositions.length - 1].y, mouseX, mouseY); + } + pop(); + }; + + + mode.onDeactivate = function () { + this.polygonPositions = []; + + }; + + //adds a point to the polygon, if the user clicks on the starting position and the shape has more than 2 points add the polygon to the world + mode.onClick = function () { + if (this.polygonPositions.length > 2 && dist(this.polygonPositions[0].x, this.polygonPositions[0].y, mouseX, mouseY) < 10) { + this.createPolygonShape(); + return; + } + + if (this.polygonPositions.length < maxPointsOnPoly) { + this.polygonPositions.push(getShiftedMousePos()); + if (this.finalLineCrosses(this.polygonPositions)) { + this.polygonPositions.pop(); + warning = new Warning("Error: Polygons cannot have lines which cross over", 200); + } + } else { + warning = new Warning("Error: Shit hits the fan if the polygons have too many sides, so I made the limit " + maxPointsOnPoly + " points", 200); + } + }; + + + mode.buttonPressed = function () { + switch (keyCode) { + case ENTER://closes the shape and adds the polygon + this.createPolygonShape(); + + break; + case ESCAPE: + if (this.polygonPositions.length > 0) { + this.polygonPositions.splice(this.polygonPositions.length - 1, 1); + } else { + buttonManager.deactivateActiveModes(); + } + break; + } + }; + + + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Add Polygon"); + messages.push("CLICK: add vertex to polygon"); + messages.push("ENTER: finish polygon"); + if (buttonManager.getCurrentMode().polygonPositions.length === 0) { + messages.push("ESC: cancel"); + } else { + messages.push("ESC: remove last vertex"); + } + + return messages; + }; + + //closes the shape and adds it to the creature + mode.createPolygonShape = function () { + if (this.polygonPositions.length > 2) {//polygons need atleast 3 points + this.polygonPositions.push(cloneVector(this.polygonPositions[0])); + //if the lines cross then error + if (this.finalLineCrosses(this.polygonPositions, true)) { + this.polygonPositions.pop(); + warning = new Warning("Error: Closing the shape would introduce crossed lines", 200); + return; + } + this.polygonPositions.pop(); + + let x = 0; + let y = 0; + for (var v of this.polygonPositions) { + x += v.x; + y += v.y; + } + x /= this.polygonPositions.length; + y /= this.polygonPositions.length; + + let body = new Body(x, y, 0, true); + for (var p of this.polygonPositions) { + p.x -= x; + p.y -= y; + } + body.addArrayFixture(this.polygonPositions); + creature.addBody(body); + this.polygonPositions = []; + buttonManager.getCurrentMode().deactivate(); + warning = new Warning("Polygon Added", 100, true); + + } + + + }; + + //determines whether or not the final line of the polygon crosses + mode.finalLineCrosses = function (vectors, ignoreFirst) { + let newVecs = []; + for (var v of vectors) { + newVecs.push(v); + } + let j = newVecs.length - 2; + for (var i = 0; i < newVecs.length - 3; i++) { + if (i === 0 && ignoreFirst) { + continue; + } + if (this.linesCross(newVecs[i].x, newVecs[i].y, newVecs[i + 1].x, newVecs[i + 1].y, newVecs[j].x, newVecs[j].y, newVecs[j + 1].x, newVecs[j + 1].y)) { + return true; + } + } + + return false; + }; + + + mode.linesCross = function (x1, y1, x2, y2, x3, y3, x4, y4) { + var uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + var uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); + return (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1); + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddRectangleButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddRectangleButton.js new file mode 100644 index 0000000..1f59f63 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AddRectangleButton.js @@ -0,0 +1,137 @@ +//allows the user to add rectangles to the creature +function generateAddRectangleButton(x, y, w, h, modeNumber) { + + let buttonText = "Add Rectangle"; + let mode = new Mode(); + mode.cornerMode = true; + + mode.doneFirstClick = false; + mode.firstClick = createVector(0, 0); + mode.lineWidth = 30; + + mode.drawEffects = function () { + push(); + fill(selectedBodyFillColor); + stroke(selectedStrokeColor); + + if (this.doneFirstClick) { + if (this.cornerMode) { + rectMode(CORNERS); + rect(this.firstClick.x, this.firstClick.y, mouseX, mouseY); + } else { + + /* + assume line looks like this for variable naming purposes + + ------------------------------------------------------- + | | + . firstClick .mousePos + | | + ------------------------------------------------------- + */ + let lengthVector = createVector(mouseX - this.firstClick.x, mouseY - this.firstClick.y); + let heightVector = createVector().set(lengthVector); + heightVector.rotate(-PI / 2.0); + heightVector.normalize(); + heightVector.mult(this.lineWidth / 2.0); + + beginShape(); + vertex(this.firstClick.x + heightVector.x, this.firstClick.y + heightVector.y); + vertex(this.firstClick.x + heightVector.x + lengthVector.x, this.firstClick.y + heightVector.y + lengthVector.y); + vertex(this.firstClick.x - heightVector.x + lengthVector.x, this.firstClick.y - heightVector.y + lengthVector.y); + vertex(this.firstClick.x - heightVector.x, this.firstClick.y - heightVector.y); + endShape(CLOSE); + } + } + pop(); + }; + + //either set the first click or create the rectangle + mode.onClick = function () { + if (!this.doneFirstClick) { + this.doneFirstClick = true; + this.firstClick = createVector(mouseX, mouseY); + } else { + + let body = new Body((this.firstClick.x + mouseX) / 2.0, (this.firstClick.y + mouseY) / 2.0, 0, true); + + if (this.cornerMode) { + let w = abs(mouseX - this.firstClick.x); + let h = abs(mouseY - this.firstClick.y); + body.addRectFixture(-w / 2.0, -h / 2.0, w, h, 0); + + + } else { + let arr = []; + let lengthVector = createVector(mouseX - this.firstClick.x, mouseY - this.firstClick.y); + let heightVector = createVector().set(lengthVector); + heightVector.rotate(-PI / 2.0); + heightVector.normalize(); + heightVector.mult(this.lineWidth / 2.0); + arr.push(createVector(this.firstClick.x + heightVector.x, this.firstClick.y + heightVector.y)); + arr.push(createVector(this.firstClick.x + heightVector.x + lengthVector.x, this.firstClick.y + heightVector.y + lengthVector.y)); + arr.push(createVector(this.firstClick.x - heightVector.x + lengthVector.x, this.firstClick.y - heightVector.y + lengthVector.y)); + arr.push(createVector(this.firstClick.x - heightVector.x, this.firstClick.y - heightVector.y)); + + arr = arr.map((vec) => createVector(vec.x - body.x, vec.y - body.y)); + body.addArrayFixture(arr); + + } + this.doneFirstClick = false; + creature.bodies.push(body); + warning = new Warning("Rectangle Added", 100, true); + } + + }; + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + case TAB: + this.cornerMode = !this.cornerMode; + break; + case ESCAPE: + if (this.doneFirstClick) { + this.doneFirstClick = false; + } else { + buttonManager.deactivateActiveModes(); + } + break; + } + }; + + //adjusts line width + mode.scrollWheel = function (mouseDirection) { + if (!this.cornerMode) { + this.lineWidth = max(this.lineWidth + (2 * shiftIncrease) * mouseDirection, 3); + } + + }; + + + mode.instructions.getMessages = function () { + let messages = []; + let currentMode = buttonManager.getCurrentMode(); + currentMode.cornerMode ? messages.push("Corners mode") : messages.push("Line Mode"); + + messages.push("CLICK: you figure it out"); + messages.push("TAB: change mode"); + + if (!currentMode.cornerMode) { + messages.push("MOUSE WHEEL: scale width"); + messages.push("HOLD SHIFT: increase scale speed"); + } + + if (!currentMode.doneFirstClick) { + messages.push("ESC: exit "); + } else { + messages.push("ESC: cancel"); + } + return messages; + }; + mode.onActivate = function(){ + this.doneFirstClick =false; + }; + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AdvancedButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AdvancedButton.js new file mode 100644 index 0000000..6929920 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/AdvancedButton.js @@ -0,0 +1,12 @@ +//activates the advanced buttons +function generateAdvancedButton(x, y, w, h, modeNumber) { + let buttonText = "Advanced"; + let mode = new Mode(); + mode.onActivate = function () { + buttonManager.deactivateActiveModes(); + buttonManager.activateAdvancedButtons(); + this.deactivate(); + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/CloneShapeButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/CloneShapeButton.js new file mode 100644 index 0000000..d2dccc3 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/CloneShapeButton.js @@ -0,0 +1,92 @@ +//clones a body and allows the user to place it where they want +//also clones any images attached to the body +function generateCloneShapeButton(x, y, w, h, modeNumber) { + + + let buttonText = "Clone Shape"; + let mode = new Mode(); + mode.bodyIsCloned = false; + mode.onActivate = function () { + if (creature.bodies.length === 0) { + warning = new Warning("There is nothing to clone mate", 400); + } + }; + + mode.everyFrame = function () { + if (this.bodyIsCloned) { + creature.moveSelectedBodyOrJointToMousePos(); + } else { + creature.selectBodyMouseIsOverExcluding(); + } + }; + + mode.onDeactivate = function () { + if (this.bodyIsCloned) { + this.removeClone(); + } + }; + + mode.onClick = function () { + if (creature.selectedBody !== -1) { + if (!this.bodyIsCloned) { + + creature.bodies.push(creature.getSelectedBody().clone()); + creature.getSelectedBody().selected = false; + creature.selectBody(creature.bodies.length - 1); + this.bodyIsCloned = true; + dragMouseFrom = createVector(mouseX, mouseY); + window.startingBodyPos = creature.getSelectedBody().getPixelCoordinates(); + } else { + this.bodyIsCloned = false; + warning = new Warning("Shape Cloned", 100, true); + + } + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + + case ESCAPE: + if (this.bodyIsCloned) { + this.removeClone(); + } else { + buttonManager.deactivateActiveModes(); + } + break; + } + }; + mode.scrollWheel = function (mouseDirection) { + if (creature.selectedBody !== -1 && this.bodyIsCloned) { + creature.getSelectedBody().rotate(PI / 200 * shiftIncrease * mouseDirection); + } + }; + mode.instructions.getMessages = function () { + let messages = []; + let currentMode = buttonManager.getCurrentMode(); + if (!currentMode.bodyIsCloned) { + messages.push("Select shape to clone"); + messages.push(" CLICK: select shape"); + messages.push(" ESC: cancel"); + } else { + messages.push("Position Clone"); + messages.push("CLICK: confirm position of clone"); + messages.push("MOUSE WHEEL: rotate clone"); + messages.push("HOLD SHIFT: increase rotation speed"); + messages.push("ESC: remove clone"); + } + return messages; + }; + + mode.removeClone= function() { + creature.getSelectedBody().remove(); + creature.bodies.splice(creature.selectedBody, 1); + creature.selectedBody = -1; + this.bodyIsCloned = false; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/DeleteButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/DeleteButton.js new file mode 100644 index 0000000..c5c6ac5 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/DeleteButton.js @@ -0,0 +1,48 @@ +//allows the user to delete bodies and joints +function generateDeleteButton(x, y, w, h, modeNumber) { + let buttonText = "Delete"; + let mode = new Mode(); + mode.onActivate = function () { + if (creature.bodies.length === 0) { + warning = new Warning("I'm curious, what exactly do you plan on deleting?", 150); + } + }; + + mode.everyFrame = function () { + creature.selectBodyOrJointMouseIsOver(); + }; + + + mode.onClick = function () { + if (creature.selectedBody !== -1) { + creature.getSelectedBody().remove(); + creature.bodies.splice(creature.selectedBody, 1); + creature.selectedBody = -1; + warning = new Warning("Shape Deleted", 100, true); + + } else if (creature.selectedJoint !== -1) { + creature.getSelectedJoint().remove(); + creature.joints.splice(creature.selectedJoint, 1); + creature.selectedJoint = -1; + warning = new Warning("Joint Deleted", 100, true); + + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + } + }; + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Delete shapes and joints"); + messages.push("CLICK: delete shape or joint"); + messages.push("ESC: exit "); + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/FuseShapesButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/FuseShapesButton.js new file mode 100644 index 0000000..a00ccf8 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/FuseShapesButton.js @@ -0,0 +1,82 @@ +//allows the user to fuse 2 bodies together, which adds all the fixtures of one body to the other and then deletes the first body +function generateFuseShapesButton(x, y, w, h, modeNumber) { + + + let buttonText = "Fuse Shapes"; + let mode = new Mode(); + + mode.selectedBody1 = -1; + mode.selectedBody2 = -1; + + + mode.onActivate = function () { + this.selectedBody1 = -1; + this.selectedBody2 = -1; + if (creature.bodies.length === 0) { + warning = new Warning("What's the plan chief? There isn't anything to fuse", 200); + } + if (creature.bodies.length === 1) { + warning = new Warning("Notice the S at the end of 'FUSE SHAPES' that means you're gonna need at least 2 shapes. \n It's what we call a plural.", 400); + } + }; + + + mode.everyFrame = function () { + creature.selectBodyMouseIsOverExcluding(this.selectedBody1); + }; + + mode.onClick = function () { + if (creature.selectedBody !== -1) { + if (this.selectedBody1 === -1) { + this.selectedBody1 = creature.selectedBody; + creature.getSelectedBody().selectedAsShape1 = true; + } else { + this.selectedBody2 = creature.selectedBody; + creature.fuseBodies(this.selectedBody1,this.selectedBody2); + + creature.unselectAllBodies(); + + this.selectedBody1 = -1; + this.selectedBody2 = -1; + warning = new Warning("Selected shapes are now fused", 100, true); + + } + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + + case ESCAPE: + + if (this.selectedBody1 === -1) { + buttonManager.deactivateActiveModes(); + + } else { + this.selectedBody1 = -1; + creature.unselectAllBodies(); + } + + break; + + + } + }; + mode.instructions.getMessages = function () { + let messages = []; + let currentMode = buttonManager.getCurrentMode(); + if (currentMode.selectedBody1 === -1) { + messages.push("Select first shape to fuse"); + messages.push(" CLICK: select shape"); + messages.push(" ESC: cancel"); + } else { + messages.push("Select second shape to fuse"); + messages.push("CLICK: select shape"); + messages.push("ESC: unselect first shape"); + } + return messages; + }; + + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/MoveResizeButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/MoveResizeButton.js new file mode 100644 index 0000000..04adbb9 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/MoveResizeButton.js @@ -0,0 +1,64 @@ +//allows the user to move and resize shit +function generateMoveResizeButton(x, y, w, h, modeNumber) { + + let buttonText = "Move/Resize"; + let mode = new Mode(); + mode.onActivate = function () { + if (creature.bodies.length === 0) { + warning = new Warning("I worked on this for A YEAR and you're here trying to resize nothing \n I clearly overestimated my audience", 400); + } + }; + + + mode.everyFrame = function () { + if (!dragging) { + creature.selectBodyOrJointMouseIsOver(); + } else { + creature.moveSelectedBodyOrJointToMousePos(); + + } + + + }; + + + mode.onClick = function () { + if (creature.selectedBody !== -1) { + dragMouseFrom = createVector(mouseX, mouseY); + window.startingBodyPos = creature.bodies[creature.selectedBody].getPixelCoordinates(); + + } else if (creature.selectedJoint !== -1) { + dragMouseFrom = createVector(mouseX, mouseY); + window.startingAnchorPos = creature.joints[creature.selectedJoint].getPixelCoordinatesOfAnchor(0); + + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + + } + }; + mode.scrollWheel = function (mouseDirection) { + if (creature.selectedBody !== -1) { + creature.bodies[creature.selectedBody].scale(1 + (0.02 * shiftIncrease) * mouseDirection); + } + }; + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Move and Resize"); + messages.push("CLICK AND DRAG: move shape or joint"); + messages.push("MOUSE WHEEL: scale shape"); + messages.push("HOLD SHIFT: increase scale speed"); + messages.push("ESC: exit "); + return messages; + }; + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/MoveRotateButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/MoveRotateButton.js new file mode 100644 index 0000000..c5fc8fa --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/DefaultButtons/MoveRotateButton.js @@ -0,0 +1,71 @@ +//allows the user to move and rotate shit +function generateMoveRotateButton(x, y, w, h, modeNumber) { + let buttonText = "Move/Rotate"; + let mode = new Mode(); + mode.onActivate = function () { + if (creature.bodies.length === 0) { + warning = new Warning("You can't move anything if there is nothing there genius", 150); + } + + }; + + + mode.everyFrame = function () { + if (!dragging) { + creature.selectBodyOrJointMouseIsOver(); + } else { + creature.moveSelectedBodyOrJointToMousePos(); + + } + + + }; + + + mode.onClick = function () { + if (creature.selectedBody !== -1) { + dragMouseFrom = createVector(mouseX, mouseY); + window.startingBodyPos = creature.bodies[creature.selectedBody].getPixelCoordinates(); + + } else if (creature.selectedJoint !== -1) { + dragMouseFrom = createVector(mouseX, mouseY); + window.startingAnchorPos = creature.joints[creature.selectedJoint].getPixelCoordinatesOfAnchor(0); + + } + }; + mode.buttonPressed = function () { + switch (keyCode) { + case SHIFT: + shiftIncrease = 10; + break; + + case ESCAPE: + + buttonManager.deactivateActiveModes(); + + break; + + + } + }; + mode.scrollWheel = function (mouseDirection) { + if (creature.selectedBody !== -1) { + if (creature.bodies[creature.selectedBody].fixtures.length === 1 && creature.bodies[creature.selectedBody].fixtures[0].fixtureType === "circle" && creature.bodies[creature.selectedBody].bodyImages.length === 0) { + warning = new Warning("Did you really just try to rotate a circle", 100); + } + creature.bodies[creature.selectedBody].rotate(PI / 200 * shiftIncrease * mouseDirection); + } + }; + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Move and Rotate"); + messages.push("CLICK AND DRAG: move shape or joint"); + messages.push("MOUSE WHEEL: rotate shape"); + messages.push("HOLD SHIFT: increase rotation speed"); + messages.push("ESC: exit "); + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/EvolveButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/EvolveButton.js new file mode 100644 index 0000000..78e6197 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/EvolveButton.js @@ -0,0 +1,50 @@ +//this button starts the evolution process +function generateEvolveButton(x, y, w, h, modeNumber) { + + let buttonText = "Evolve"; + let mode = new Mode(); + mode.countDown = 1; + mode.onActivate = function () { + if (creature.bodies.length === 0) { + warning = new Warning("NO", 50); + this.deactivate(); + return; + } + + if (creature.joints.length === 0) { + warning = new Warning("The whole point this entire thing if for the AI to control the creature \n you are going to need some joints for the creature to be able to move", 500); + this.deactivate(); + return; + } + this.countDown =1; + }; + + mode.drawEffects = function(){ + push(); + textSize(50); + fill(0); + noStroke(); + text("LOADING...", canvas.width/2,canvas.height/2); + pop(); + + }; + + mode.everyFrame = function(){ + if(this.countDown-- <= 0 ){ + world.reset(); + isCreatureScreaming = creature.isScreaming(); + creatureObject = creature.getCreatureAsObject(); + inCreatureCreatorMode = false; + world.paused = false; + buttonManager.activateLearningButtons(); + AILearnsToWalkSetup(); + this.deactivate(); + paused = false; + } + }; + + + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/DeathLazerSpeedValueButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/DeathLazerSpeedValueButton.js new file mode 100644 index 0000000..2b5a0f1 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/DeathLazerSpeedValueButton.js @@ -0,0 +1,38 @@ +//lets the user adjust the death lazer speed +function generateDeathLazerSpeedValueButton(x, y, w, h) { + let valueButton = new ValueButton(x, y, w, h, "DEATH Lazer Speed"); + valueButton.getValue = () => deathLazerSpeed; + valueButton.increaseValue = () => { + deathLazerSpeed++; + + //reset everything since all previous scores are invalid + + population.bestOfEachSpecies=[]; + population.bestScore = 0; + population.globalBestScore = 0; + for (let p of population.players) { + p.bestScore = 0; + } + + speciesNumber =0; + for (let s of population.species) { + s.rep.bestScore = 0; + s.champ.bestScore = 0; + s.bestFitness = 0; + s.maxPlayerLength = 0; + + s.speciesNumber = speciesNumber; + speciesNumber++; + population.bestOfEachSpecies.push({champ: s.champ.cloneForReplay(), maxPlayerLength: 1}); + + + + } + population.bestScoreOfAPreviousBatch = 0; + population.resetGeneration(); + resetAudio(); + warning = new Warning("Generation Reset, all previous scores reset to 0 as they are now invalid", 300, true); + }; + valueButton.decreaseValue = () => deathLazerSpeed = max(1, deathLazerSpeed - 1); + return valueButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/EditCreatureButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/EditCreatureButton.js new file mode 100644 index 0000000..0e35fdc --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/EditCreatureButton.js @@ -0,0 +1,20 @@ +//this button returns the program to the creature creator section +function generateEditCreatureButton(x, y, w, h, modeNumber) { + + let buttonText = "Edit Creature"; + let mode = new Mode(); + + mode.onActivate = function () { + world.reset(); + inCreatureCreatorMode = true; + setDefaultInstructions(); + world.paused = true; + panX = 0; + buttonManager.activateDefaultButtons(); + this.deactivate(); + resetAudio(); + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/MuteButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/MuteButton.js new file mode 100644 index 0000000..2f2b9dc --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/MuteButton.js @@ -0,0 +1,19 @@ +//this button allows the user to mute the audio because it kinda annoying +function generateMuteButton(x, y, w, h) { + let button = new Button(x, y, w, h, ""); + button.show = function(){ + if (muted) { + image(mutedImage,this.x,this.y,this.w,this.h); + } else { + image(unmutedImage,this.x,this.y,this.w,this.h); + } + + }; + + button.onClick = function(){ + muted = !muted; + manageSounds(); + }; + + return button; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/NumberOfBatchesValueButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/NumberOfBatchesValueButton.js new file mode 100644 index 0000000..38e96b5 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/NumberOfBatchesValueButton.js @@ -0,0 +1,23 @@ +//this button allows the user to adjust the number of batches +function generateNumberOfBatchesValueButton(x, y, w, h) { + let valueButton = new ValueButton(x, y, w, h, "Number of Batches"); + valueButton.getValue = () => population.numberOfBatches; + valueButton.increaseValue = () => { + + population.setNumberOfBatches(population.numberOfBatches+1); + population.resetGeneration(); + resetAudio(); + warning = new Warning(`Number of Batches: ${population.numberOfBatches}, Players Per Batch: ${population.playersPerBatch}`, 100, true); + }; + valueButton.decreaseValue = () =>{ + if(population.numberOfBatches===1){ + return; + } + + population.setNumberOfBatches( population.numberOfBatches -1); + population.resetGeneration(); + resetAudio(); + warning = new Warning(`Number of Batches: ${population.numberOfBatches}, Players Per Batch: ${population.playersPerBatch}`, 100, true); + }; + return valueButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/PopulationSizeValueButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/PopulationSizeValueButton.js new file mode 100644 index 0000000..5ec9f97 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/PopulationSizeValueButton.js @@ -0,0 +1,20 @@ +//this button allows the user to adjust the number of players in the popyulation +function generatePopulationSizeValueButton(x, y, w, h) { + let valueButton = new ValueButton(x, y, w, h, "Population Size"); + valueButton.getValue = () => populationSize; + valueButton.increaseValue = () => { + populationSize += 20; + warning = new Warning("Population size will be updated at the start of the next generation", 300, true); + + + }; + valueButton.decreaseValue = () => { + if(populationSize ===20) + return; + populationSize = max(20, populationSize - 20); + warning = new Warning("Population size will be updated at the start of the next generation", 300, true); + + + }; + return valueButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/ReplayEvolutionHighlightsButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/ReplayEvolutionHighlightsButton.js new file mode 100644 index 0000000..2017ec7 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/ReplayEvolutionHighlightsButton.js @@ -0,0 +1,132 @@ +//this button replays all the important improvements the player made over the generations +function generateReplayEvolutionHighlightsButton(x, y, w, h, modeNumber) { + let buttonText = "Replay Highlights"; + let mode = new Mode(); + + mode.onActivate = function () { + if (population.gen === 1) {//cant replay gens which haven't happened, so deactive this bad boy + warning = new Warning("Haven't had any generations to replay yet", 200, false); + storeGenLifespan = genLifespan; + storeMoveCounter = moveCounter; + buttonManager.deactivateActiveModes(); + return; + } + //sets up variables and stuff + genReplay = true; + genPlayer = population.genPlayers[0].cloneForReplay(); + storeGenLifespan = genLifespan; + storeMoveCounter = moveCounter; + genLifespan = 0; + moveCounter = 0; + genPlayersCounter = 0; + resetAudio(); + + }; + + mode.onDeactivate = function () { + genReplay = false; + moveCounter = storeMoveCounter; + genLifespan = storeGenLifespan; + genPlayersCounter = 0; + + }; + + + mode.everyFrame = function () { + if (!genPlayer.dead || genPlayer.deathCounter > 0) { //if current gen player is not dead then update it + let speedUpAmount = simulationSpeed; + for (let m = 0; m < speedUpAmount; m++) { + moveCounter++; + genLifespan++; + if (moveCounter % thinkEveryXFrames === 0) { + genPlayer.look(); + genPlayer.think(); + } + genPlayer.update(); + } + + + //show the gen player + push(); + translate(0, 2 * 376 * (1 - playerScaleAmount)); + scale(playerScaleAmount); + genPlayer.show(); + pop(); + + + } else { //if dead move on to the next generation + + this.nextGenPlayer(); + + + } + + }; + //draw the player + mode.drawEffects = function () { + push(); + translate(0, 2 * 376 * (1 - playerScaleAmount)); + scale(playerScaleAmount); + genPlayer.show(); + pop(); + }; + + + //go to the next player + mode.nextGenPlayer = function () { + + genPlayersCounter++; + if (genPlayersCounter >= population.genPlayers.length) { //if at the end then deactivate current mode + warning = new Warning("Replay is up to date, returning to normal evolution", 200, true); + buttonManager.deactivateActiveModes(); + } else { //if not at the end then get the next generation + genPlayer = population.genPlayers[genPlayersCounter].cloneForReplay(); + genLifespan = 0; + moveCounter = 0; + panX = 0; + } + resetAudio(); + }; + + //go to the previous player (if one exists) + mode.previousGenPlayer = function () { + if (genPlayersCounter === 0) { + return; + } + genPlayersCounter--; + genPlayer = population.genPlayers[genPlayersCounter].cloneForReplay(); + genLifespan = 0; + moveCounter = 0; + panX = 0; + resetAudio(); + }; + + + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + + case RIGHT_ARROW: + this.nextGenPlayer(); + break; + case LEFT_ARROW: + this.previousGenPlayer(); + break; + + } + }; + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Replaying Evolution Highlights"); + messages.push(" LEFT: Play previous highlight"); + messages.push(" RIGHT: Play next highlight"); + messages.push(" SPACE: play/pause"); + messages.push(" ESC: exit"); + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/ReplayPopularSpeciesButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/ReplayPopularSpeciesButton.js new file mode 100644 index 0000000..b0cb747 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/ReplayPopularSpeciesButton.js @@ -0,0 +1,131 @@ +//this button allows the user to replay popular species +function generateReplayPopularSpeciesButton(x, y, w, h, modeNumber) { + let buttonText = "Replay Species"; + let mode = new Mode(); + mode.onActivate = function () { + if (population.gen === 1 || population.bestOfEachSpecies === [] || !population.bestOfPopularSpeciesSorted || population.bestOfPopularSpeciesSorted ===[]) { + warning = new Warning("You need to wait for a few generations before popular species emerge", 200, false); + storeGenLifespan = genLifespan; + storeMoveCounter = moveCounter; + buttonManager.deactivateActiveModes(); + return; + } + + speciesReplay = true; + speciesPlayer = population.bestOfPopularSpeciesSorted[0].champ.cloneForReplay(); + storeGenLifespan = genLifespan; + storeMoveCounter = moveCounter; + genLifespan = 0; + moveCounter = 0; + speciesPlayersCounter = 0; + resetAudio(); + + }; + + mode.onDeactivate = function () { + speciesReplay = false; + moveCounter = storeMoveCounter; + genLifespan = storeGenLifespan; + speciesPlayersCounter = 0; + speciesPlayer = null; + }; + + + mode.everyFrame = function () { + if (!speciesPlayer.dead || speciesPlayer.deathCounter > 0) { //if current gen player is not dead then update it + let speedUpAmount = simulationSpeed; + for (let m = 0; m < speedUpAmount; m++) { + moveCounter++; + genLifespan++; + if (moveCounter % thinkEveryXFrames === 0) { + speciesPlayer.look(); + speciesPlayer.think(); + } + speciesPlayer.update(); + } + + + //show the gen player + push(); + translate(0, 2 * 376 * (1 - playerScaleAmount)); + scale(playerScaleAmount); + speciesPlayer.show(); + pop(); + + + } else { //if dead move on to the next generation + + this.nextSpeciesPlayer(); + + + } + + }; + + //draw the player + mode.drawEffects = function () { + push(); + translate(0, 2 * 376 * (1 - playerScaleAmount)); + scale(playerScaleAmount); + speciesPlayer.show(); + pop(); + }; + + //play next species + mode.nextSpeciesPlayer = function () { + + speciesPlayersCounter++; + if (speciesPlayersCounter >= population.bestOfPopularSpeciesSorted.length) { //if at the end then deactivate current mode + warning = new Warning("Replay is up to date, returning to normal evolution", 200, true); + buttonManager.deactivateActiveModes(); + } else { //if not at the end then get the next generation + speciesPlayer = population.bestOfPopularSpeciesSorted[speciesPlayersCounter].champ.cloneForReplay(); + genLifespan = 0; + moveCounter = 0; + panX = 0; + } + resetAudio(); + }; + + //play previous species + mode.previousSpeciesPlayer = function () { + if (speciesPlayersCounter === 0) { + return; + } + speciesPlayersCounter--; + speciesPlayer = population.bestOfPopularSpeciesSorted[speciesPlayersCounter].champ.cloneForReplay(); + genLifespan = 0; + moveCounter = 0; + panX = 0; + resetAudio(); + }; + + + mode.buttonPressed = function () { + switch (keyCode) { + case ESCAPE: + buttonManager.deactivateActiveModes(); + break; + + case RIGHT_ARROW: + this.nextSpeciesPlayer(); + break; + case LEFT_ARROW: + this.previousSpeciesPlayer(); + break; + + } + }; + mode.instructions.getMessages = function () { + let messages = []; + messages.push("Replaying Popular Species"); + messages.push(" LEFT: Play previous species"); + messages.push(" RIGHT: Play next species"); + messages.push(" SPACE: play/pause"); + messages.push(" ESC: exit"); + return messages; + }; + + + return new ModeButton(x, y, w, h, buttonText, mode, modeNumber); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/SimulationSpeedValueButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/SimulationSpeedValueButton.js new file mode 100644 index 0000000..ae75836 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/SimulationSpeedValueButton.js @@ -0,0 +1,8 @@ +////this button allows the user to adjust the simulation speed +function generateSimulationSpeedValueButton(x, y, w, h) { + let valueButton = new ValueButton(x, y, w, h, "Simulation Speed"); + valueButton.getValue = ()=>simulationSpeed; + valueButton.increaseValue = ()=>simulationSpeed++; + valueButton.decreaseValue = ()=>simulationSpeed = max(1,simulationSpeed-1); + return valueButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/SpeedUpWhenPlayersDieToggleButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/SpeedUpWhenPlayersDieToggleButton.js new file mode 100644 index 0000000..28a8351 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/LearningButtons/SpeedUpWhenPlayersDieToggleButton.js @@ -0,0 +1,18 @@ +//this button allows the user to toggle the auto speed up boolean, which will increase the simulation speed in proportion to the number of players which are dead. +function generateSpeedUpWhenPlayersDieToggleButton(x, y, w, h) { + let toggleButton = new ToggleButton(x, y, w, h, "Speed Up When Players Die", false); + + toggleButton.toggleOn = () => { + autoSpeedUp = true; + showDeath = false; + warning = new Warning("Speed will increase as players die, Death animation removed for better performance", 300, true); + + }; + + toggleButton.toggleOff = () => { + autoSpeedUp = false; + showDeath = true; + }; + + return toggleButton; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/PlayPauseButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/PlayPauseButton.js new file mode 100644 index 0000000..b38be4b --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/PlayPauseButton.js @@ -0,0 +1,31 @@ +//this button allows the user to play/pause the simulation +function generatePlayPauseButton(x, y, w, h) { + let button = new Button(x, y, w, h, ""); + button.show = function(){ + push(); + !this.mouseOverButton()? fill(255,241,0):fill(150,150,0); + strokeWeight(2); + stroke(0); + rectMode(CORNER); + + if (world.paused) { + beginShape(); + vertex(this.x, this.y); + vertex(this.x, this.y + this.h); + vertex(this.x + this.w, this.y + this.w / 2.0); + endShape(CLOSE); + + } else { + rect(this.x, this.y, 2 * this.w / 5, this.h); + rect(this.x + 3 * this.w / 5, y, 2 * this.w / 5, this.h); + + } + pop(); + }; + + button.onClick = function(){ + world.togglePause(); + }; + + return button; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/StopButton.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/StopButton.js new file mode 100644 index 0000000..a7bf08e --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonCreationMethods/StopButton.js @@ -0,0 +1,19 @@ +//this button allows the user to stop the simulation +function generateStopButton(x, y, w, h) { + let button = new Button(x, y, w, h, ""); + button.show = function(){ + push(); + !this.mouseOverButton()? fill(255,241,0):fill(150,150,0); + strokeWeight(2); + stroke(0); + rectMode(CORNER); + rect(x, y, this.w, this.h); + pop(); + }; + + button.onClick = function(){ + world.reset(); + }; + + return button; +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonManager.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonManager.js new file mode 100644 index 0000000..1c91e73 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/ButtonManager.js @@ -0,0 +1,377 @@ +//this class handles all the logic involving displaying and interacting with buttons and modes +class ButtonManager { + constructor() { + this.defaultButtons = []; + this.advancedButtons = []; + this.cosmeticButtons = []; + this.learningButtons = []; + this.activeButtons = []; + + + this.instantiateDefaultButtons(); + this.instantiateAdvancesButtons(); + this.instantiateLearningButtons(); + this.instantiateCosmeticButtons(); + this.activateDefaultButtons(); + this.modeNo = -1; + } + + + instantiateDefaultButtons() { + + const buttonHeight = 40; + const buttonWidth = 160; + const buttonGap = 20; + const startX = 10; + const startY = 80; + let buttonNo = 0; + let modeNumber = 0; + + //LEFT FROM TOP TO BOTTOM _________________________________________________________________________________________________________________________________________________________________________________ + //Add Rectangle + this.defaultButtons.push(generateAddRectangleButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Add Circle + this.defaultButtons.push(generateAddCircleButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Add Polygon + this.defaultButtons.push(generateAddPolygonButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Add Joint + this.defaultButtons.push(generateAddJointButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Limit Joints + this.defaultButtons.push(generateLimitJointsButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Move/Rotate + this.defaultButtons.push(generateMoveRotateButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Resize + this.defaultButtons.push(generateMoveResizeButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Fuse Shapes + this.defaultButtons.push(generateFuseShapesButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Clone Shape + this.defaultButtons.push(generateCloneShapeButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + buttonNo++;//have the delete button at the bottom + modeNumber++; + + //Delete + this.defaultButtons.push(generateDeleteButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo ++; + modeNumber++; + + //BOTTOM RIGHT_____________________________________________________________________________________________________________________________________________________________________________________________ + //Advanced + this.defaultButtons.push(generateAdvancedButton(canvas.width - buttonWidth - 10, startY + 8 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Cosmetics + this.defaultButtons.push(generateCosmeticsButton(canvas.width - buttonWidth - 10, startY + 9 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Evolve + this.defaultButtons.push(generateEvolveButton(canvas.width - buttonWidth - 10, startY + 10 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + + //Top RIGHT Play pause and shit______________________________________________________________________________________________________________________________________________________________________________ + //Play Pause button + this.defaultButtons.push(generatePlayPauseButton(canvas.width - 70, 80, 25, 25)); + //Stop button + this.defaultButtons.push(generateStopButton(canvas.width - 110, 80, 25, 25)); + + + } + + instantiateCosmeticButtons() { + + const buttonHeight = 40; + const buttonWidth = 160; + const buttonGap = 20; + const startX = 10; + const startY = 80; + let buttonNo = 0; + let modeNumber = 0; + + //LEFT FROM TOP TO BOTTOM _________________________________________________________________________________________________________________________________________________________________________________ + //Add Images to shape + this.cosmeticButtons.push(generateAddImageToShapeButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + //Move Resize + this.cosmeticButtons.push(generateMoveResizeImageButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + //Move Rotate + this.cosmeticButtons.push(generateMoveRotateImageButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //change fill color + this.cosmeticButtons.push(generateChangeFillColorButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //DeleteImage + this.cosmeticButtons.push(generateDeleteImageButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + + //BOTTOM RIGHT_____________________________________________________________________________________________________________________________________________________________________________________________ + //Construction + this.cosmeticButtons.push(generateConstructionButton(canvas.width - buttonWidth - 10, startY + 9 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Evolve + this.cosmeticButtons.push(generateEvolveButton(canvas.width - buttonWidth - 10, startY + 10 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Top RIGHT Play pause and shit______________________________________________________________________________________________________________________________________________________________________________ + //Play Pause button + this.cosmeticButtons.push(generatePlayPauseButton(canvas.width - 50, 80, 25, 25)); + //Stop button + this.cosmeticButtons.push(generateStopButton(canvas.width - 90, 80, 25, 25)); + + } + + + instantiateAdvancesButtons() { + + const buttonHeight = 40; + const buttonWidth = 160; + const buttonGap = 20; + const startX = 10; + const startY = 80; + let buttonNo = 0; + let modeNumber = 0; + + + //LEFT FROM TOP TO BOTTOM _________________________________________________________________________________________________________________________________________________________________________________ + //Shape Collisions + this.advancedButtons.push(generateShapeCollisionsButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + + //Edit Shape Layers + this.advancedButtons.push(generateEditShapeLayersButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + //Death Upon Floor + this.advancedButtons.push(generateDeathUponFloorButton(startX, startY + buttonNo * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + buttonNo++; + modeNumber++; + + + //BOTTOM RIGHT_____________________________________________________________________________________________________________________________________________________________________________________________ + this.advancedButtons.push(generateBasicOptionsButton(canvas.width - buttonWidth - 10, startY + 8*(buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + modeNumber++; + //Cosmetics + this.advancedButtons.push(generateCosmeticsButton(canvas.width - buttonWidth - 10, startY + 9 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + modeNumber++; + //Evolve + this.advancedButtons.push(generateEvolveButton(canvas.width - buttonWidth - 10, startY + 10 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + modeNumber++; + + //TOP RIGHT TOGGLE BUTTONS_____________________________________________________________________________________________________________________________________________________________________________________________ + let toggleButtonWidth = 220; + let toggleButtonHeight = 40; + let toggleButtonGap = 10; + let toggleButtonNo = 0; + let toggleButtonsStartingX = canvas.width - (toggleButtonWidth + toggleButtonGap); + let toggleButtonsStartingY = 80 + toggleButtonHeight + toggleButtonGap; + + // Show Joint Limits Toggle + this.advancedButtons.push(generateShowJointLimitsToggleButton(toggleButtonsStartingX, toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth, toggleButtonHeight)); + toggleButtonNo++; + // Allow Body Collisions Toggle + this.advancedButtons.push(generateAllowBodyCollisionsToggleButton(toggleButtonsStartingX, toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth, toggleButtonHeight)); + + //Top RIGHT Play pause and shit______________________________________________________________________________________________________________________________________________________________________________ + //Play Pause button + this.advancedButtons.push(generatePlayPauseButton(canvas.width - 50, 80, 25, 25)); + //Stop button + this.advancedButtons.push(generateStopButton(canvas.width - 90, 80, 25, 25)); + + + } + + instantiateLearningButtons() { + + const buttonHeight = 40; + const buttonWidth = 160; + const buttonGap = 20; + const startX = 10; + const startY = 80; + let buttonNo = 0; + let modeNumber = 0; + + //BOTTOM RIGHT_____________________________________________________________________________________________________________________________________________________________________________________________ + this.learningButtons.push(generateReplayPopularSpeciesButton(canvas.width - buttonWidth - 10, startY + 8 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, this.learningButtons.length)); + modeNumber++; + + this.learningButtons.push(generateReplayEvolutionHighlightsButton(canvas.width - buttonWidth - 10, startY + 9 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, this.learningButtons.length)); + modeNumber++; + //Edit Creature + this.learningButtons.push(generateEditCreatureButton(canvas.width - buttonWidth - 10, startY + 10 * (buttonHeight + buttonGap), buttonWidth, buttonHeight, modeNumber)); + + + + //TOP RIGHT BUTTONS_____________________________________________________________________________________________________________________________________________________________________________________________ + //mute button + this.learningButtons.push(generateMuteButton(canvas.width - 70, 10, 40, 40)); + let toggleButtonWidth = 220; + let toggleButtonHeight = 30; + let toggleButtonGap = 10; + let toggleButtonNo = 0; + let toggleButtonsStartingX = canvas.width - (toggleButtonWidth + toggleButtonGap); + let toggleButtonsStartingY = 70 + 0 * (toggleButtonHeight + toggleButtonGap); + + + // Replay Evolution hightlights button + + toggleButtonNo = 0; + //Death Lazer Speed Value Button + this.learningButtons.push(generateDeathLazerSpeedValueButton(toggleButtonsStartingX, toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth - 2 * toggleButtonHeight, toggleButtonHeight)); + toggleButtonNo++; + //Simulation speed value button + this.learningButtons.push(generateSimulationSpeedValueButton(toggleButtonsStartingX, toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth - 2 * toggleButtonHeight, toggleButtonHeight)); + toggleButtonNo++; + //number of batches value button + this.learningButtons.push(generateNumberOfBatchesValueButton(toggleButtonsStartingX, toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth - 2 * toggleButtonHeight, toggleButtonHeight)); + toggleButtonNo++; + //population size value button + this.learningButtons.push(generatePopulationSizeValueButton(toggleButtonsStartingX, toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth - 2 * toggleButtonHeight, toggleButtonHeight)); + toggleButtonNo++; + // Speed up when players die Toggle + this.learningButtons.push(generateSpeedUpWhenPlayersDieToggleButton(toggleButtonsStartingX , toggleButtonsStartingY + toggleButtonNo * (toggleButtonHeight + toggleButtonGap), toggleButtonWidth, toggleButtonHeight)); + toggleButtonNo++; + } + + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + //most of these are not worth explaining + activateAdvancedButtons() { + this.activeButtons = this.advancedButtons; + } + + activateDefaultButtons() { + this.activeButtons = this.defaultButtons; + } + + activateCosmeticButtons() { + this.activeButtons = this.cosmeticButtons; + } + + activateLearningButtons() { + this.activeButtons = this.learningButtons; + } + + showActiveButtons() { + for (let button of this.activeButtons) { + button.show(); + } + } + + showCurrentModeEffects() { + if (this.modeNo !== -1) { + this.getCurrentMode().drawEffects(); + } + } + + updateCurrentMode() { + if (this.modeNo !== -1) { + this.getCurrentMode().everyFrame(); + } + } + + onKeyPressed() { + if (this.modeNo !== -1) { + this.getCurrentMode().buttonPressed(); + } + } + + onMouseWheelMove(mouseDirection) { + if (this.modeNo !== -1) { + this.getCurrentMode().scrollWheel(mouseDirection); + } + } + + //check if any buttons are clicked if not then check if the current mode needs the input + onClick() { + for (let button of this.activeButtons) { + if (button.mouseOverButton()) { + button.onClick(); + return;// if a button is clicked then leave + } + } + + + if (this.modeNo !== -1) { + this.getCurrentMode().onClick(); + } + + + } + + getCurrentMode() { + return this.activeButtons[this.modeNo].mode; + } + + + getCurrentModeNumber() { + return this.modeNo; + } + + //returns whether the modeString matches the button text of the currently active mode + isInMode(modeString) { + return (this.modeNo !== -1 && this.activeButtons[this.modeNo].text === modeString); + } + + //finds allmodes which are active and deactivates them + deactivateActiveModes() { + for (let b of this.activeButtons) { + + if (b.mode && b.mode.isActive) { + b.mode.deactivate(); + } + } + this.modeNo = -1; + //unselect everything just in case + creature.unselectEverything(); + + } + + //returns whether we are currently in cosmetics mode + areCosmeticsActive(){ + return this.cosmeticButtons === this.activeButtons; + } +} + + + diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CommonColorsArray.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CommonColorsArray.js new file mode 100644 index 0000000..d32e1c3 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CommonColorsArray.js @@ -0,0 +1,132 @@ +// a list of common colors ripped from the internet + +let commonColors = [[128, 0, 0] + , [139, 0, 0] + , [165, 42, 42] + , [178, 34, 34] + , [220, 20, 60] + , [255, 0, 0] + , [255, 99, 71] + , [255, 127, 80] + , [205, 92, 92] + , [240, 128, 128] + , [233, 150, 122] + , [250, 128, 114] + , [255, 160, 122] + , [255, 69, 0] + , [255, 140, 0] + , [255, 165, 0] + , [255, 215, 0] + , [184, 134, 11] + , [218, 165, 32] + , [238, 232, 170] + , [189, 183, 107] + , [240, 230, 140] + , [128, 128, 0] + , [255, 255, 0] + , [154, 205, 50] + , [85, 107, 47] + , [107, 142, 35] + , [124, 252, 0] + , [127, 255, 0] + , [173, 255, 47] + , [0, 100, 0] + , [0, 128, 0] + , [34, 139, 34] + , [0, 255, 0] + , [50, 205, 50] + , [144, 238, 144] + , [152, 251, 152] + , [143, 188, 143] + , [0, 250, 154] + , [0, 255, 127] + , [46, 139, 87] + , [102, 205, 170] + , [60, 179, 113] + , [32, 178, 170] + , [47, 79, 79] + , [0, 128, 128] + , [0, 139, 139] + , [0, 255, 255] + , [0, 255, 255] + , [224, 255, 255] + , [0, 206, 209] + , [64, 224, 208] + , [72, 209, 204] + , [175, 238, 238] + , [127, 255, 212] + , [176, 224, 230] + , [95, 158, 160] + , [70, 130, 180] + , [100, 149, 237] + , [0, 191, 255] + , [30, 144, 255] + , [173, 216, 230] + , [135, 206, 235] + , [135, 206, 250] + , [25, 25, 112] + , [0, 0, 128] + , [0, 0, 139] + , [0, 0, 205] + , [0, 0, 255] + , [65, 105, 225] + , [138, 43, 226] + , [75, 0, 130] + , [72, 61, 139] + , [106, 90, 205] + , [123, 104, 238] + , [147, 112, 219] + , [139, 0, 139] + , [148, 0, 211] + , [153, 50, 204] + , [186, 85, 211] + , [128, 0, 128] + , [216, 191, 216] + , [221, 160, 221] + , [238, 130, 238] + , [255, 0, 255] + , [218, 112, 214] + , [199, 21, 133] + , [219, 112, 147] + , [255, 20, 147] + , [255, 105, 180] + , [255, 182, 193] + , [255, 192, 203] + , [250, 235, 215] + , [245, 245, 220] + , [255, 228, 196] + , [255, 235, 205] + , [245, 222, 179] + , [255, 248, 220] + , [255, 250, 205] + , [250, 250, 210] + , [255, 255, 224] + , [139, 69, 19] + , [160, 82, 45] + , [210, 105, 30] + , [205, 133, 63] + , [244, 164, 96] + , [222, 184, 135] + , [210, 180, 140] + , [188, 143, 143] + , [255, 228, 181] + , [255, 222, 173] + , [255, 218, 185] + , [255, 228, 225] + , [255, 240, 245] + , [250, 240, 230] + , [253, 245, 230] + , [255, 239, 213] + , [255, 245, 238] + , [245, 255, 250] + , [112, 128, 144] + , [119, 136, 153] + , [176, 196, 222] + , [230, 230, 250] + , [255, 250, 240] + , [240, 248, 255] + , [248, 248, 255] + , [240, 255, 240] + , [255, 255, 240] + , [240, 255, 255] + , [255, 250, 250]]; diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/CBHead.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/CBHead.png new file mode 100644 index 0000000..e948102 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/CBHead.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/Hat 1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/Hat 1.png new file mode 100644 index 0000000..c7f6e37 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/Hat 1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/L eye 1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/L eye 1.png new file mode 100644 index 0000000..93e0c52 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/L eye 1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/R eye 1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/R eye 1.png new file mode 100644 index 0000000..92bffe4 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/R eye 1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/eyes1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/eyes1.png new file mode 100644 index 0000000..53519b8 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/eyes1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 1.png new file mode 100644 index 0000000..d493b7f Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 10.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 10.png new file mode 100644 index 0000000..ee8d55e Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 10.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 2.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 2.png new file mode 100644 index 0000000..a1416b5 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 2.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 3.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 3.png new file mode 100644 index 0000000..21aef61 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 3.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 4.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 4.png new file mode 100644 index 0000000..08c0582 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 4.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 5.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 5.png new file mode 100644 index 0000000..cd75d95 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 5.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 6.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 6.png new file mode 100644 index 0000000..fb82488 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 6.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 7.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 7.png new file mode 100644 index 0000000..778bf20 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 7.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 8.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 8.png new file mode 100644 index 0000000..f014967 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 8.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 9.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 9.png new file mode 100644 index 0000000..febe11a Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/face 9.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth 1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth 1.png new file mode 100644 index 0000000..fb59715 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth 1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth 2.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth 2.png new file mode 100644 index 0000000..ba7863e Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth 2.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth1.png new file mode 100644 index 0000000..5707497 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mouth1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mustache 1.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mustache 1.png new file mode 100644 index 0000000..643d188 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mustache 1.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mustache 2.png b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mustache 2.png new file mode 100644 index 0000000..37b40db Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/CosmeticImages/mustache 2.png differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Creature.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Creature.js new file mode 100644 index 0000000..82e9db2 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Creature.js @@ -0,0 +1,399 @@ +//this class holds the bodies and joints and handles the logic for them + +class Creature { + constructor() { + this.bodies = [];//list of body objects that create the creature + this.joints = [];//list of joint objects that create the creature + this.selectedJoint = -1; + this.selectedBody = -1; + this.selectedBodyToEditCosmetically = -1; + } + + + //draws the creature to the screen + show() { + push(); + translate(panX, panY); + for (let b of this.bodies) { + b.show(); + } + for (let j of this.joints) { + j.show(); + } + pop(); + } + + + update() { + } + + //adds a body shape to the creature + addBody(body) { + this.bodies.push(body); + } + + //adds a joint to the creature + addJoint(joint) { + this.joints.push(joint); + } + + //returns the body object which is currently selected + getSelectedBody() { + if (this.selectedBody !== -1) { + return this.bodies[this.selectedBody]; + } + return null; + } + + //returns the joint object which is currently selected + getSelectedJoint() { + if (this.selectedJoint !== -1) { + return this.joints[this.selectedJoint]; + } + return null; + } + + //unselects all bodies + unselectAllBodies() { + for (let b of this.bodies) { + b.selectedAsShape1 = false; + b.selected = false; + } + this.selectedBody = -1; + } + + + + unselectEverythingExceptForSelectedAsShape1s() { + for (let b of this.bodies) { + b.selected = false; + } + this.selectedBody = -1; + + for (let j of this.joints) { + j.selected = false; + j.selectedJoint = -1; + } + + this.selectedJoint = -1; + } + + //unselects all joints + unselectAllJoints() { + for (let j of this.joints) { + j.selected = false; + j.selectedJoint = -1; + } + + this.selectedJoint = -1; + } + + //unselects all joints and bodies + unselectEverything() { + this.unselectAllBodies(); + this.unselectAllJoints(); + } + + //removes all joints attached to body which has recently been destroyed + removeJointsAttachedToADestroyedBody() { + for (var i = 0; i < this.joints.length; i++) { + if (this.joints[i].body1.removed || this.joints[i].body2.removed) { + this.joints[i].remove(); + this.joints.splice(i, 1); + i -= 1; + } + } + } + + //resets all joints attached to this body. + //used when the body is moved/ rotated + resetJointsAttachedToBody(body) { + for (var i = 0; i < this.joints.length; i++) { + if (this.joints[i].body1 === body || this.joints[i].body2 === body) { + this.joints[i].reset(); + } + } + } + + //returns the index of the argument body + getBodyNo(body) { + for (var i = 0; i < this.bodies.length; i++) { + if (this.bodies[i] === body) { + return i; + } + } + return -1; + } + + //adds all the fixtures in body2 to body1 and deletes body 2 + fuseBodies(bodyNo1, bodyNo2) { + for (let bodyImage of this.bodies[bodyNo2].bodyImages) { + bodyImage.addToNewBody(this.bodies[bodyNo1]); + } + + this.bodies[bodyNo1].addAllFixturesAndJointsFromBody(this.bodies[bodyNo2]); + this.bodies[bodyNo2].remove(); + this.bodies.splice(bodyNo2, 1); + } + + + //select a joint the mouse is over + selectJointMouseIsOver() { + this.unselectEverything(); + + let minIndex = -1; + let min = 100000; + + + for (let i = 0; i < this.joints.length; i++) { + let pos = this.joints[i].getPixelCenter(); + let distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && distance < 5) { + min = distance; + minIndex = i; + } + } + + this.selectJoint(minIndex); + + } + + //select a body or joint the mouse is over + selectBodyOrJointMouseIsOver() { + this.unselectEverything(); + + let minIndex = -1; + let min = 100000; + let selectJoint = false; + let distance = 0; + //check bodies + for (let i = 0; i < this.bodies.length; i++) { + let pos = this.bodies[i].getPixelCoordinates(); + distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && this.bodies[i].isShiftedPixelPosWithinFixtures(getShiftedMousePos())) { + min = distance; + minIndex = i; + } + } + + //check joints + for (let i = 0; i < this.joints.length; i++) { + let pos = this.joints[i].getPixelCenter(); + distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && distance < 5) { + //if a joint is closer to the mouse than the closest body then select it. + min = distance; + minIndex = i; + selectJoint = true; + } + } + + //select the closest shit + + selectJoint ? this.selectJoint(minIndex) : this.selectBody(minIndex); + return minIndex; + } + + //returns the index of the body the mouse is over, -1 if no body is moused over + getBodyNoMouseIsOver() { + let minIndex = -1; + if (this.bodies.length > 0) { + let min = 100000; + for (var i = 0; i < this.bodies.length; i++) { + let pos = this.bodies[i].getPixelCoordinates(); + let distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && this.bodies[i].isShiftedPixelPosWithinFixtures(getShiftedMousePos())) { + min = distance; + minIndex = i; + } + } + } + return minIndex; + } + + + //returns an Object holding the index of the image the mouse is over as well as the index of the body the image is attached to, -1 if no image is moused over + getBodyImageNoMouseIsOver() { + let minBodyIndex = -1; + let minBodyImageIndex = -1; + if (this.bodies.length > 0) { + let min = 100000; + for (let i = 0; i < this.bodies.length; i++) { + for (let j = 0; j < this.bodies[i].bodyImages.length; j++) { + let bodyImage = this.bodies[i].bodyImages[j]; + if (bodyImage.mouseIsOverImage()) { + let distance = dist(mouseX, mouseY, bodyImage.center.x, bodyImage.center.y); + if (distance < min) { + min = distance; + minBodyIndex = i; + minBodyImageIndex = j; + } + + } + + } + + + } + } + return {bodyNo: minBodyIndex, bodyImageNo: minBodyImageIndex}; + } + + //unselects all the body images from all the bodies+ + unselectAllBodyImages() { + for (let body of this.bodies) { + for (let bodyImage of body.bodyImages) { + bodyImage.selected = false; + bodyImage.selectedForDelete = false; + } + } + + + } + + //select the argument body no, this includes setting this.selectedBody and setting the body object to have selected = true + selectBody(bodyNo) { + this.selectedBody = bodyNo; + if (this.selectedBody !== -1) { + this.bodies[this.selectedBody].selected = true; + } + } + + //select the argument joint no, this includes setting this.selectedJoint and setting the joint object to have selected = true + selectJoint(jointNo) { + this.selectedJoint = jointNo; + if (this.selectedJoint !== -1) { + this.joints[this.selectedJoint].selected = true; + } + } + + //select the closest body the mouse is over except any body numbers included in the dontCheck list + selectBodyMouseIsOverExcluding(...dontCheck) { + this.unselectEverythingExceptForSelectedAsShape1s(); + this.selectBody(this.getBodyNoMouseIsOverExcluding(...dontCheck)); + } + + //get the body number of the closest body the mouse is over except any body numbers included in the dontCheck list + getBodyNoMouseIsOverExcluding(...dontCheck) { + let minIndex = -1; + if (this.bodies.length > 0) { + let min = 100000; + for (var i = 0; i < this.bodies.length; i++) { + if (dontCheck.contains(i)) { + continue; + } + let pos = this.bodies[i].getPixelCoordinates(); + let distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && this.bodies[i].isShiftedPixelPosWithinFixtures(getShiftedMousePos())) { + min = distance; + minIndex = i; + } + } + } + return minIndex; + } + + + //move the selected joint or body to the current mouse position + //this requires dragMouseFrom and startingBodyPos vectors to be set. + moveSelectedBodyOrJointToMousePos() { + //difference between current mouse position and the starting dragging position + + if (this.selectedBody !== -1) { + let difference = createVector(mouseX - dragMouseFrom.x, mouseY - dragMouseFrom.y); + let newPosition = createVector(startingBodyPos.x + difference.x, startingBodyPos.y + difference.y); + this.bodies[this.selectedBody].setPosition(newPosition); + } else if (this.selectedJoint !== -1) { + let difference = createVector(mouseX - dragMouseFrom.x, mouseY - dragMouseFrom.y); + let newPosition = createVector(startingAnchorPos.x + difference.x, startingAnchorPos.y + difference.y); + this.joints[this.selectedJoint].setPositionOfAnchor(0, newPosition); + } + } + + //returns the creature as an object (like JSON) + getCreatureAsObject() { + let obj = {bodies: [], joints: []}; + + for (let b of this.bodies) { + obj.bodies.push(b.getBodyInfoAsObject()); + } + + for (let j of this.joints) { + + obj.joints.push(j.getJointInfoAsObject()); + } + + + let totalMass = 0; + for (let b of this.bodies) { + totalMass += b.body.GetMass(); + } + //scale the creature down a bit; + + let scaleDownAmount = 0.75; + if (totalMass < 60) { + scaleDownAmount = 1; + } + for (let b of obj.bodies) { + b.x *= scaleDownAmount; + b.y *= scaleDownAmount; + for (let f of b.fixtures) { + switch (f.fixtureType) { + case "rectangle": + f.x *= scaleDownAmount; + f.y *= scaleDownAmount; + f.w *= scaleDownAmount; + f.h *= scaleDownAmount; + break; + case "circle": + f.x *= scaleDownAmount; + f.y *= scaleDownAmount; + f.radius *= scaleDownAmount; + break; + case "compound": + + for (let i = 0; i < f.pixelVectorPositions.length; i++) { + f.pixelVectorPositions[i].x *= scaleDownAmount; + f.pixelVectorPositions[i].y *= scaleDownAmount; + } + break; + + } + + + } + + + for (let bodyImage of b.bodyImages) { + bodyImage.x *= scaleDownAmount; + bodyImage.y *= scaleDownAmount; + bodyImage.w *= scaleDownAmount; + bodyImage.h *= scaleDownAmount; + } + + } + + for (let j of obj.joints) { + j.anchorX *= scaleDownAmount; + j.anchorY *= scaleDownAmount; + } + + + return obj; + } + + + //returns whether the creature has a screaming image on it + isScreaming(){ + for(let b of this.bodies){ + for(let bodyImage of b.bodyImages){ + if(bodyImage.isScreaming){ + return true; + } + } + } + + return false; + + } +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/HotBar.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/HotBar.js new file mode 100644 index 0000000..492a310 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/HotBar.js @@ -0,0 +1,425 @@ +//a hotbar contains a list of buttons and displays them on a scrolling hotbar +class HotBar { + constructor(x, y, w, h, padding) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.padding = padding; + this.buttons = []; + this.numberOfButtons = 12; + this.currentButtonPosition = 0; // a pointer to the leftmost button, so by default this is 0 so the first button in the array + this.buttonWidth = h - 2 * padding; + this.buttonHeight = h - 2 * padding; + this.gapBetweenButtons = (this.w - (this.numberOfButtons + 2) * (this.buttonWidth + (padding * 2))) / (this.numberOfButtons + 1); + + this.modeNo = -1; + //create the left arrow button + this.leftArrow = new Button(x + padding, y + padding, this.buttonWidth, this.buttonHeight); + this.leftArrow.show = function (isFaded) { + push(); + this.mouseOverButton() ? fill(255, 247, 0) : fill(255, 247, 0, 100); + if (isFaded) fill(255, 247, 0, 100); + noStroke(); + + beginShape(); + vertex(this.x + this.w, this.y); + vertex(this.x, this.y + this.w / 2.0); + vertex(this.x + this.w, this.y + this.h); + endShape(CLOSE); + pop(); + + }; + //create the right arrow button + this.rightArrow = new Button(x + w - h + padding, y + padding, this.buttonWidth, this.buttonHeight); + this.rightArrow.show = function (isFaded) { + push(); + + this.mouseOverButton() ? fill(255, 247, 0) : fill(255, 247, 0, 100); + if (isFaded) fill(255, 247, 0, 100); + noStroke(); + + beginShape(); + vertex(this.x, this.y); + vertex(this.x, this.y + this.h); + vertex(this.x + this.w, this.y + this.w / 2.0); + endShape(CLOSE); + pop(); + + }; + + + this.conveyerBeltSectionImage = loadImage("pics/ConveryerBeltSection.png"); + + this.amountToMove = 0; + this.moveSpeed = 10; + + this.selectedButton = -1; + + this.holdCount = 0;//a counter for telling how long the mouse has held down left/right arrow button + + } + + //returns whether an item is selected + hasSelectedItem() { + return this.selectedButton !== -1; + } + + //returns the currently selected button + getCurrentlySelectedButton() { + + if (this.selectedButton === -1) + return null; + return this.buttons[this.selectedButton]; + } + + mouseOverHotBar() { + return (this.x < mouseX && this.x + this.w > mouseX) && (this.y < mouseY && this.y + this.h > mouseY); + } + + //this function is a fucking mess but it works + getButtonPosition(buttonNo) { + let obj = {}; + obj.x = this.x + this.buttonWidth + this.padding + this.padding + this.gapBetweenButtons + this.padding + (buttonNo) * (2 * this.padding + this.gapBetweenButtons + this.buttonWidth);//+1 to button number to account for left arrow + obj.y = this.y + this.padding; + obj.w = this.buttonWidth; + obj.h = this.buttonHeight; + return obj; + + } + + //adds all the image buttons and loads the images from file + addImageButtons() { + this.addImageButton(loadImage("Creature creator/CosmeticImages/CBHead.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 1.png"), true); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 2.png"), true); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 3.png"), true); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 4.png"), true); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 5.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 6.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 7.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 8.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 9.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/face 10.png")); + + + this.addImageButton(loadImage("Creature creator/CosmeticImages/eyes1.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/R eye 1.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/L eye 1.png")); + + this.addImageButton(loadImage("Creature creator/CosmeticImages/mouth1.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/mouth 1.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/mouth 2.png"), true); + this.addImageButton(loadImage("Creature creator/CosmeticImages/mustache 1.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/mustache 2.png")); + this.addImageButton(loadImage("Creature creator/CosmeticImages/Hat 1.png")); + + + } + + + //adds the color buttons from the common colors array and a few hand picked ones + addColorButtons() { + this.addColorButton(0, 0, 0); //black + + this.addColorButton(105, 105, 105); + this.addColorButton(128, 128, 128); + this.addColorButton(169, 169, 169); + this.addColorButton(192, 192, 192); + this.addColorButton(211, 211, 211); + this.addColorButton(220, 220, 220); + this.addColorButton(245, 245, 245); + + this.addColorButton(255, 255, 255); //white + + + this.addColorButton(255, 0, 0); //red + this.addColorButton(255, 69, 0); + this.addColorButton(255, 99, 71); + this.addColorButton(250, 128, 114); + this.addColorButton(255, 140, 0); + this.addColorButton(255, 165, 0); + this.addColorButton(255, 215, 0); + + + this.addColorButton(0, 255, 0); //Green + this.addColorButton(127, 255, 0); + this.addColorButton(173, 255, 47); + this.addColorButton(152, 251, 152); + this.addColorButton(50, 205, 50); + this.addColorButton(0, 255, 127); + + this.addColorButton(0, 0, 255); //Blue + this.addColorButton(65, 105, 225); + this.addColorButton(135, 206, 250); + this.addColorButton(0, 191, 255); + this.addColorButton(127, 255, 212); + + + this.addColorButton(255, 219, 172); //Skin + + + //add like 200 other colors + for (let i = 0; i < commonColors.length; i++) { + this.addColorButton(commonColors[i][0], commonColors[i][1], commonColors[i][2]); + } + } + + + //creates an image button from the input image and adds it to the buttons array + addImageButton(image, isScreaming) { + let mode = new Mode(); + let buttonPositions = this.getButtonPosition(this.buttons.length); + let newButton = new hotBarButton(buttonPositions.x, buttonPositions.y, buttonPositions.w, buttonPositions.h, image, mode, this.buttons.length, this.x, this.x + this.w, isScreaming); + this.buttons.push(newButton); + } + + //adds a color button to the buttons array + addColorButton(red, green, blue, opacity) { + if (!opacity) + opacity = 255; + + + red = Math.min(255, red + 50); + green = Math.min(255, green + 50); + blue = Math.min(255, blue + 50); + + let mode = new Mode(); + let buttonPositions = this.getButtonPosition(this.buttons.length); + let tempColor = color(red, green, blue, opacity); + let newButton = new colorHotBarButton(buttonPositions.x, buttonPositions.y, buttonPositions.w, buttonPositions.h, tempColor, mode, this.buttons.length, this.x, this.x + this.w); + this.buttons.push(newButton); + + } + + //shows the hotbar + show() { + + + push(); + //if the arrow buttons are held down then move it + if (dragging && (this.leftArrow.mouseOverButton() || this.rightArrow.mouseOverButton())) { + if (this.holdCount < 20) { + this.holdCount++; + } else { + this.moveSpeed = 20; + if (Math.abs(this.amountToMove) < this.moveSpeed) { + this.onClick(); + } + } + } else { + this.holdCount = 0; + } + this.moveAllButtons(); + this.moveSpeed = 10; + + + //show the background + fill(100); + noStroke(); + rectMode(CORNER); + rect(this.x, this.y, this.w, this.h); + let numberOfconveyerBeltSections = Math.floor(this.w / this.conveyerBeltSectionImage.width); + let conveyerBeltSectionWidth = this.w / numberOfconveyerBeltSections; + for (let i = 0; i < numberOfconveyerBeltSections; i += 1) { + image(this.conveyerBeltSectionImage, this.x + i * conveyerBeltSectionWidth, this.y, conveyerBeltSectionWidth, this.h); + } + + //show the currently showing buttons + for (let button of this.getCurrentlyShowingButtons()) { + button.show(); + } + + + //show some of the backgound to cover up buttons which are under the left and right arrows + image(this.conveyerBeltSectionImage, this.x + 0 * conveyerBeltSectionWidth, this.y, conveyerBeltSectionWidth, this.h); + image(this.conveyerBeltSectionImage, this.x + (numberOfconveyerBeltSections - 1) * conveyerBeltSectionWidth, this.y, conveyerBeltSectionWidth, this.h); + + //show the arrow buttons + this.rightArrow.show(this.currentButtonPosition >= this.buttons.length - this.numberOfButtons); + this.leftArrow.show(this.currentButtonPosition === 0); + + //draw the mode effects if a button is clicked + for (let button of this.getCurrentlyShowingButtons()) { + if (button.isClicked) { + button.mode.drawEffects(); + break; + } + } + + + pop(); + } + + + //moves all the buttons movespeed pixels if neeeded + moveAllButtons(direction) { + if (this.amountToMove === 0) return; + + let amountToMoveThisFrame = (this.amountToMove < 0) ? -1 * this.moveSpeed : this.moveSpeed; + if (Math.abs(this.amountToMove) < this.moveSpeed) amountToMoveThisFrame = this.amountToMove; + this.amountToMove -= amountToMoveThisFrame; + + for (let button of this.buttons) { + button.x += amountToMoveThisFrame; + } + } + + + //returns all the buttons currently visible on the hotbar + getCurrentlyShowingButtons() { + return this.buttons.filter((b, index) => index >= this.currentButtonPosition - 1 && index < this.currentButtonPosition + 1 + this.numberOfButtons); + } + + + onClick() { + //click the left arrow + if (this.leftArrow.mouseOverButton()) { + + this.unclickAllButtons(); + + //move all buttons + if (this.currentButtonPosition !== 0) { + this.currentButtonPosition--; + this.amountToMove += 2 * this.padding + this.gapBetweenButtons + this.buttonWidth; + + } + return; + + } + //click the right button + if (this.rightArrow.mouseOverButton()) { + this.unclickAllButtons(); + //move all buttons + if (this.currentButtonPosition < this.buttons.length - this.numberOfButtons) { + this.currentButtonPosition++; + this.amountToMove -= 2 * this.padding + this.gapBetweenButtons + this.buttonWidth; + + } + return; + } + + //if any buttons are clicked then unclick all other buttons and click that button + for (let button of this.getCurrentlyShowingButtons()) { + if (button.mouseOverButton()) { + //unclick all other buttons + let temp = button.isClicked; + this.unclickAllButtons(); + button.isClicked = temp; + button.onClick(); + + if (button.isClicked) + this.selectedButton = button.mode.modeNumber; + return;// if a button is clicked then leave + } + } + + //if user clicked on hot bar (but not on any buttons) then unclick all buttons + if (this.mouseOverHotBar()) { + this.unclickAllButtons(); + } + + } + + //unclicks all the buttons on the hotbar + unclickAllButtons() { + for (let button of this.buttons) { + button.isClicked = false; + } + + this.selectedButton = -1; + } + + +} + +//a button on the hotbar +//this is for image buttons +class hotBarButton extends ModeButton { + constructor(x, y, w, h, buttonImage, mode, modeNumber, leftMostPoint, rightMostPoint, isScreaming) { + super(x, y, w, h, "", mode, modeNumber); + this.image = buttonImage; + this.mode.bodyImage = new BodyImage(buttonImage, 0, 0, 50, 50, isScreaming); + //draw the image at the mouse position as the user is placing the image + this.mode.drawEffects = function () { + this.bodyImage.moveToMousePosition(); + this.bodyImage.show(); + }; + + //the left and rightmost point are used to not show buttons which are off the hotbar + this.leftMostPoint = leftMostPoint; + this.rightMostPoint = rightMostPoint; + + this.isScreaming = isScreaming; + + } + + //show the button + show() { + if (this.x + this.w > this.rightMostPoint || this.x < this.leftMostPoint) return; + + push(); + + let fillOpacity = this.getOpacity(); + fill(100, fillOpacity); + stroke(255, fillOpacity); + + + if (this.isClicked) fill(180, fillOpacity); + if (this.mouseOverButton()) fill(150, fillOpacity); + rect(this.x, this.y, this.w, this.h); + + + image(this.image, this.x, this.y, this.w, this.h); + pop(); + } + + //get the opacity based on how far it is from the edges, this will fade it out + getOpacity() { + let fillOpacity = 255; + let smallestDistanceToEdge = Math.min(this.x - this.leftMostPoint, this.rightMostPoint - (this.x + this.w)); + if (smallestDistanceToEdge < 70) { + fillOpacity = map(smallestDistanceToEdge, 70, 20, 200, 0); + } + + return fillOpacity; + + } + + //overwrites button super class + onClick() { + + this.isClicked = !this.isClicked; + this.mode.bodyImage = new BodyImage(this.image, 0, 0, 50, 50, this.isScreaming); + + } +} + +//a button on the hotbar +//this is for color buttons +class colorHotBarButton extends ModeButton { + constructor(x, y, w, h, color, mode, modeNumber, leftMostPoint, rightMostPoint) { + super(x, y, w, h, "", mode, modeNumber); + this.color = color; + this.leftMostPoint = leftMostPoint; + this.rightMostPoint = rightMostPoint; + } + + //shows the color button + show() { + if (this.x + this.w > this.rightMostPoint || this.x < this.leftMostPoint) return; + push(); + stroke(0); + if (this.mouseOverButton()) + stroke(230); + strokeWeight(4); + fill(this.color); + rect(this.x, this.y, this.w, this.h); + pop(); + } + + //overwrites the buttons implementation + onClick() { + this.isClicked = !this.isClicked; + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Instruction.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Instruction.js new file mode 100644 index 0000000..1237de5 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Instruction.js @@ -0,0 +1,95 @@ +//a class designed to have its getMessages function overwritten so it displays different messages depending on what mode its in +class Instruction { + + getMessages() { + return [] + } + + + //writes the defaultInstructions to the screen. + show() { + + let messages = this.getMessages(); + if (messages.length === 0) { + return; + } + + //full message is the everything except for the heading + let fullMessage = ""; + for (var i = 1; i < messages.length; i++) { + fullMessage += messages[i] + "\t\t\t"; + } + + + push(); + //show Heading + textAlign(CENTER, CENTER); + + fill(255,255,0); + stroke(255,255,0); + strokeWeight(1); + textSize(20); + noStroke(); + + + textSize(25); + text(messages[0], canvas.width / 2, 15); + + + + //show defaultInstructions + textFont(smallFont); + + textSize(15); + noStroke(); + text(fullMessage, canvas.width / 2+25, 45); + pop(); + } +} +//sets the default defaultInstructions when the player is not in a mode, for when they just chillin +function setDefaultInstructions() { + defaultInstructions = new Instruction(); + if(inCreatureCreatorMode) { + defaultInstructions.getMessages = function () { + let messages = []; + messages.push("Creature Creator"); + messages.push("SPACE: play/pause simulation"); + if (!world.isReset) { + messages.push("CLICK AND DRAG: move shapes"); + messages.push("R: reset/stop simulation"); + } + return messages; + } + }else{ + defaultInstructions.getMessages = function () { + let messages = []; + messages.push("Training your creature"); + messages.push("TAB: change view mode"); + messages.push("SPACE: play/pause"); + + + return messages; + } + } +} + +//show the bar and the defaultInstructions at the top of the screen +function showInstructions() { + + //draw black bar at top of screen + push(); + strokeWeight(2); + stroke(0, 0, 0); + fill(0,50); + image(topImage, 0,0,canvas.width,60); + rect(1, 1, canvas.width-2, 60); + pop(); + + //write the default Instructions to the screen if not in a mode + if (buttonManager.modeNo === -1) { + defaultInstructions.show(); + } else {//otherwise show the modes instructions + buttonManager.getCurrentMode().instructions.show(); + } + +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/MouseJoint.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/MouseJoint.js new file mode 100644 index 0000000..0f60f75 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/MouseJoint.js @@ -0,0 +1,48 @@ +//a class which handles all the mouse Joint logic which is used to drag bodies around in the creature creator mode +class MouseJoint { + + constructor() { + this.mouseJoint; + this.isActive = false; + + } + + //create a new mouse joint if a body is clicked on + onClick(){ + this.destroyJoint(); + let bodyClickedOn = creature.getBodyNoMouseIsOver(); + + if (bodyClickedOn === -1) { + return; + } + + //create mouse joint definition with the clicked on body at the point where the mouse clicked + let mouseJointDef = new b2MouseJointDef(); + let groundBody = new Body(0, 0, 0, false);// the mouse joint needs a static object to "ground it" whatever that means, all i know is that it works. + mouseJointDef.bodyA = groundBody.body; + mouseJointDef.bodyB = creature.bodies[bodyClickedOn].body; + mouseJointDef.collideConnected = true; + mouseJointDef.maxForce = 10000000; + mouseJointDef.dampingRatio = 0; + this.mouseJoint = world.box2dWorld.CreateJoint(mouseJointDef); + this.mouseJoint.m_localAnchor = creature.bodies[bodyClickedOn].getLocalWorldCoordinatesOfPixelLocation(getShiftedMousePos()); + + this.updateTarget(); + this.isActive = true; + } + + //updates the target of the joint to the new mouse position + updateTarget(){ + if(this.isActive) { + this.mouseJoint.SetTarget(new Vec2((getShiftedMousePos().x) / SCALE, (getShiftedMousePos().y) / SCALE)); + } + } + + //removes the joint from the box2dWorld + destroyJoint(){ + if (this.isActive) { + world.box2dWorld.DestroyJoint(this.mouseJoint); + this.isActive = false; + } + } +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Warning.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Warning.js new file mode 100644 index 0000000..8636b47 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/Warning.js @@ -0,0 +1,65 @@ +//a warning is a message to the user which will appear at the bottom of the screen for lifespan frames. +class Warning { + + constructor(message, lifespan, isMessage) { + this.message = message;//if the warning is a message then it will be yellow, otherwise it will be red + this.lifespan = lifespan; + + if (isMessage) { + this.isMessage = isMessage; + } else { + this.isMessage = false; + } + + } + + //show the warning and reduce its lifespan + show() { + if (this.lifespan > 0) { + this.lifespan--; + push(); + textAlign(CENTER, CENTER); + this.isMessage ? fill(255, 255, 0, 230) : fill(255, 0, 0, 255); + + stroke(0, 200); + + strokeWeight(3); + textSize(25); + + text(this.message, canvas.width / 2, canvas.height - 50); + pop(); + } + } + + //returns whether or not the lifespan is 0 + isFinished() { + return this.lifespan <= 0; + } +} + +let warningFade = 0; +let maxWarningFade = 100; +let warningFadeSpeed = 20; + +//shows the current warning +function showWarning() { + if (!warning) { + return; + } + //fade the warning out + if (warning.isFinished()) { + warningFade = max(0, warningFade - warningFadeSpeed); + warning.isMessage ? fill(100, 100, 0, warningFade) : fill(100, 0, 0, warningFade); + noStroke(); + rect(0, canvas.height - 95, canvas.width, 100); + return; + } + + //fade the warning in + warningFade = min(maxWarningFade, warningFade + warningFadeSpeed); + warning.isMessage ? fill(100, 100, 0, warningFade) : fill(100, 0, 0, warningFade); + noStroke(); + rect(0, canvas.height - 95, canvas.width, 100); + + warning.show(); +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/World.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/World.js new file mode 100644 index 0000000..46ac3ac --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/World.js @@ -0,0 +1,130 @@ +//this class contains the box2d world for the creature and handles all logic involving it +class World{ + + constructor(){ + this.box2dWorld = new b2World(new Vec2(0, 10), true);//the box2dWorld that the created creature is in. + this.paused = true; + this.isReset = true; + + } + + //needs to happen after the constructor because this world object is used within generateFloorBody so yeah + addFloorToWorld(){ + this.floorBody = this.generateFloorBody(); + + } + + //show the floor + show(){ + this.floorBody.show(); + } + + //progress the world if its not paused + update(){ + if (!this.paused) { + this.box2dWorld.Step(1 / 30, 10, 10); + } + }; + + //generates the static body which all the creatures walk on. + generateFloorBody(){ + let body = new Body(-100, 764, 0, false); + body.addRectFixture(-531, -15, 12000, 120, 0); + body.fixtures[body.fixtures.length - 1].setValues(0.1, 1, 0.45); + return body; + } + + //resets the world to its start state + reset(){ + this.isReset = true; + this.paused = true; + + for (let b of creature.bodies) { + b.reset(); + } + this.resetBodyCollisions(); + for (let j of creature.joints) { + j.reset(); + } + } + + //pauses/unpauses the world + togglePause(){ + this.isReset = false; + this.paused = !this.paused; + mouseJoint.destroyJoint(); + buttonManager.deactivateActiveModes(); + } + + + //updates the collision information for all the bodies + resetBodyCollisions() { + + this.setBodyCollisionGroupsAndSetCategoryBits(); + + for (let b of creature.bodies) { + b.resetCategoryMask(); + } + for (let b of creature.bodies) { + b.resetFilterData(); + } + this.floorBody.resetFilterDataForGround(); + } + + + //set this collision group for each body + setBodyCollisionGroupsAndSetCategoryBits() { + + if(allowBodyCollisions) { + let collisionGroups = this.getCollisionGroups(); + + for (let i = 0; i < collisionGroups.length; i++) { + for (var b of collisionGroups[i]) { + b.setCategoryBitsFromGroupNo(i); + } + } + }else{ + //everything is in one group where they can only contact the floor + for(let b of creature.bodies){ + b.setCategoryBitsFromGroupNo(0); + } + } + } + + //returns a list of all groups of bodies which have the same collisions + // [ group1, group2, group3] + getCollisionGroups() { + let collisionGroups = []; + for (let b of creature.bodies) { + + let added = false; + for (let i = 0; i < collisionGroups.length; i++) { + if (this.haveSameCollisions(collisionGroups[i][0], b)) { + collisionGroups[i].push(b); + added = true; + break; + } + } + if (!added) { + collisionGroups.push([b]); + } + } + collisionGroups.sort((a, b) => b.length - a.length); + return collisionGroups; + } + + + //returns whether or not 2 bodies have the same collision bits, i.e. collide with the same bodies + haveSameCollisions(body1, body2) { + if (body1.bodiesToNotCollideWith.length !== body2.bodiesToNotCollideWith.length) { + return false; + } + for (let b of body1.bodiesToNotCollideWith) { + if (!body2.bodiesToNotCollideWith.contains(b)) { + return false; + } + } + return true; + } + +} \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/sketch.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/sketch.js new file mode 100644 index 0000000..9e5fc29 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Creature creator/sketch.js @@ -0,0 +1,167 @@ +const SCALE = 30;// ratio between box2d box2dWorld coordinates(in meters) and pixel coordinates, so x meters = Scale*x pixels +let world;//the box2dWorld that the created creature is in. +let creature; //the creature that is be creating. +const maxPointsOnPoly = 10; // after an amount points the polygons get too complicated to be accurately simulated therefore i added a limit + + + +let dragMouseFrom; // a vector used when clicking and dragging shit + +let newCircleSize = 30;// the starting size for created circles + +let shiftIncrease = 1; //for a lot acts involving the mouse wheel shift can be held to increase speed. + +let mouseJoint;//a mouse joint is used to allow the user to drag around and fuck with their creation in creature creator mode + +let dragging = false;//a boolean used to tell if the mouse is down + +let warning;//the current waring which is displayed at the bottom of the screen. this can be overwritten with a new message from anywhere and it will be shown for its given lifespan + +let showJointLimits = true;//a boolean (toggleable by the user) which will allow or stop the joint limit arc from showing as its kinda annoying + +let allowBodyCollisions = true;//a boolean (toggleable by the user) which will deactivate collisions between all body object(exept for the floor object) + +let buttonManager;//manages all the buttons and mode stuff +let defaultInstructions;//the current set of defaultInstructions that appear on the the top bar of the screen, different modes will overwrite the getMessages() function of the defaultInstructions to tell the user what to do in each mode + + +//called on startup only +function creatureCreatorSetup() { + + //create the world for the creature + world = new World(); + world.addFloorToWorld(); + + //create the creature + creature = new Creature(); + + window.canvas = createCanvas(1500, 850); + canvas.parent('canvas'); + + + canvas.mouseWheel(mouseWheelMoved); + frameRate(60); + + + buttonManager = new ButtonManager(); + + setDefaultInstructions(); + + mouseJoint = new MouseJoint(); + + //prevents the window from moving from the arrow keys or the spacebar + window.addEventListener("keydown", function (e) { + // space and arrow keys + if ([32, 37, 38, 39, 40, 9].indexOf(e.keyCode) > -1) { + e.preventDefault(); + } + }, false); + +} + +//called every frame when in creature creator +function creatureCreatorDraw() { + + buttonManager.updateCurrentMode(); + mouseJoint.updateTarget(); + world.update(); + + drawBackground(); + + creature.show(); + + buttonManager.showCurrentModeEffects(); + buttonManager.showActiveButtons(); + + showWarning(); + showInstructions(); + +} + +//draws the background color and floor +function drawBackground() { + + let tiledPanX = panX * playerScaleAmount; + while (tiledPanX < 0) { + tiledPanX += canvas.width; + } + tiledPanX = tiledPanX % canvas.width; + + background(200); + + + image(floorTilesImage, tiledPanX, 150+594, canvas.width, canvas.height - 150-594); + image(floorTilesImage, tiledPanX - canvas.width, 150+594, canvas.width, canvas.height - 150-594); + + image(dangerFloor, tiledPanX, canvas.height - 232, canvas.width + 90, 700 / 4); + image(dangerFloor, tiledPanX - canvas.width, canvas.height - 232, canvas.width + 90, 700 / 4); +} + +//called when a key is pressed while in creature creator mode +function creatureCreatorKeyPressed() { + switch (key) { + case ' '://play/pause + world.togglePause(); + break; + case 'R'://resets the world + world.reset(); + break; + } + + //see if the current mode can use the input + buttonManager.onKeyPressed(); + +} + + +//called when a key is released when in creature creator mode, this is used to turn off shift increase +function creatureCreatorKeyReleased() { + switch (keyCode) { + case SHIFT: + shiftIncrease = 1; + break; + } +} + +//called whenever the mouse is pressed in creature creator mode +function creatureCreatorMousePressed() { + dragging = true; + + buttonManager.onClick(); + + if (!world.paused) { + mouseJoint.onClick(); + } +} + +//used mostly for dragging rated tasks +function mouseReleased() { + dragging = false; + mouseJoint.destroyJoint(); +} + + +//called when the mouse wheel is moved +function mouseWheelMoved(event) { + mouseDirection = 1; + if (event.deltaY > 0) { + mouseDirection = -1; + } + + //check if the current mode needs the input + buttonManager.onMouseWheelMove(mouseDirection); + +} + +//returns the pan offset as a vector +function getPannedOffset() { + return createVector(panX, panY); +} + +//gets the mouse position (in pixels) when the panning offset is taken into account, so a panX of -100 and a mouse posX of 300, means the mouse is really at 400 when shifted with the panning +function getShiftedMousePos() { + let x = mouseX - getPannedOffset().x; + let y = mouseY - getPannedOffset().y; + return createVector(x, y); +} + diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Fixtures.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Fixtures.js new file mode 100644 index 0000000..ccd4da1 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Fixtures.js @@ -0,0 +1,689 @@ +//fixtures are like shapes, they are what you attach to bodies to give them form +//this class (and its children classes) handle the creation of and logic for box2d fixtures + +class Fixture { + + constructor(center) { + + //create fixture definition + this.fixDef = new b2FixtureDef(); + this.fixDef.density = 2; + this.fixDef.friction = 0.1; + this.fixDef.restitution = 0.5; + + this.body = null; + + this.center = center; + this.pixelCenter; + + this.fixture; + + //set density and friction and restitution; + this.density = this.fixDef.density; + this.friction = this.fixDef.friction; + this.restitution = this.fixDef.restitution; + this.filterData = new b2FilterData(); + + this.fillColor = fillColor; // this can be overwritten by the user + this.outlineColor = outlineColor; + + } + + + setFilterData(fd) { + this.filterData = fd; + } + + getPixelCenter() { + return this.pixelCenter; + } + + //called whenever shit changes + //sets the pixel center based on the body and the relative positions + setPixelCenter() { + let bodyPos = this.body.getPixelCoordinates(); + let angle = this.body.angle; + let rotatedCenter = createVector(this.center.x, this.center.y); + rotatedCenter.rotate(angle); + let trueCenterPos = p5.Vector.add(bodyPos, rotatedCenter); + this.pixelCenter = trueCenterPos; + } + + + //sets the friction for the fixture + setFriction(val) { + this.fixDef.friction = val; + this.friction = val; + if (this.fixture != null) { + this.fixture.SetFriction(val); + } + + } + + //sets the density for the fixture + setDensity(val) { + this.fixDef.density = val; + this.density = val; + if (this.fixture != null) { + this.fixture.SetDensity(val); + } + + } + + + //sets the restitution (bouncyness) for the fixture + setRestitution(val) { + this.fixDef.restitution = val; + this.restitution = val; + if (this.fixture != null) { + this.fixture.SetRestitution(val); + } + } + + //set density and friction and restitution; + setValues(friction, density, restitution) { + this.setFriction(friction); + this.setDensity(density); + this.setRestitution(restitution); + } + + + //add this fixture to a body and ands it to the box2d world + addToBody(body) { + + this.body = body; + this.fixture = body.body.CreateFixture(this.fixDef); + this.fixture.SetFilterData(this.filterData); + this.setPixelCenter(); + } + + + //shows the fixture + show(body) { + + //set fill and outline based on the current state of the program before showing the fixture itself + + let alpha = 0; + !inCreatureCreatorMode || (buttonManager.areCosmeticsActive() && buttonManager.modeNo === -1) ? alpha = 255 : alpha = 200; + fill(this.fillColor._getRed(), this.fillColor._getGreen(), this.fillColor._getBlue(), alpha); + + stroke(this.outlineColor) + + if (inCreatureCreatorMode) { + + if (this.body.selected) { + fill(selectedBodyFillColor); + + if (!this.body.isDynamic) { + fill(selectedStaticBodyFillColor); + } + + if (buttonManager.isInMode("Delete")) { + fill(deleteColor); + } + + if (buttonManager.isInMode("Death upon floor")) { + fill(setDeathUponTouchingFloorColor); + } + + } else { + + if (!this.body.isDynamic) { + fill(staticBodyFillColor); + } + + if (this.body.deathIfTouchesGround && buttonManager.isInMode("Death upon floor")) { + fill(deathUponTouchingFloorColor); + } + } + + if (this.body.selectedAsShape1) { + + if (buttonManager.isInMode("Change Fill Color")) { + + stroke(255, 255, 0); + strokeWeight(2); + fill(this.fillColor._getRed(), this.fillColor._getGreen(), this.fillColor._getBlue(), 255); + + + } else if (buttonManager.areCosmeticsActive() && buttonManager.modeNo !== -1) { + stroke(255, 255, 0); + strokeWeight(2); + fill(this.fillColor._getRed(), this.fillColor._getGreen(), this.fillColor._getBlue(), 255); + + } else if (!buttonManager.areCosmeticsActive()) { + fill(selectedShape1FillColor); + } + } + + strokeWeight(3); + + } else { + + strokeWeight(3); + + if (this.body.isDead) { + fill(deadColor); + noStroke(); + } + } + + + let x = body.GetPosition().x * SCALE; + let y = body.GetPosition().y * SCALE; + let angle = body.GetAngle(); + push(); + + translate(x, y); + rotate(angle); + + //show the fixture + this.showFixtureClass(); + pop(); + strokeWeight(1); + + } + + + //removes the fixture from the body then adds the fixture back to it + resetFixture() { + this.body.body.DestroyFixture(this.fixture); + this.addToBody(this.body); + } + + //removes the fixture + remove() { + this.body.body.DestroyFixture(this.fixture); + } + + + //this function needs to be overwritten by each fixture class because each one looks different + showFixtureClass() { + } + + + //overwrite + setPosition(newPos) { + + } + + //overwrite + rotate(rotateAmount) { + } + + + //overwrite + getFixtureInfoAsObject() { + } + + //overwrite + hitByLazer(lazerX) { + return false; + } +} + + +class RectangleFixture extends Fixture { + //information is relative to the body + constructor(x, y, w, h, angle) { + super(createVector(x + w / 2, y + h / 2)); + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.angle = angle; + + this.pixelVectorPositions = []; + this.calculateVectorsAndSetShape();//calculates the corners based on the x,y,w,h and angle + this.fixtureType = "rectangle"; + + } + + //returns whether or not the pixel position is within the fixture + isLocalPixelPosWithinFixture(localPixelPosition) { + let positionRelativeToCenter = p5.Vector.sub(localPixelPosition, this.center); + positionRelativeToCenter.rotate(-this.angle); + let pos = p5.Vector.add(this.center, positionRelativeToCenter); + return (pos.x > this.x && pos.x < this.x + this.w && pos.y > this.y && pos.y < this.y + this.h); + } + + //get the pixel positions of the corners of the rectangle + getPixelVectors() { + this.rightVector = createVector(this.w / 2, 0).rotate(this.angle); + this.upVector = createVector(0, -this.h / 2).rotate(this.angle); + + this.pixelVectorPositions = []; + this.pixelVectorPositions.push(createVector().set(this.center).sub(this.rightVector).add(this.upVector)); + this.pixelVectorPositions.push(createVector().set(this.center).add(this.rightVector).add(this.upVector)); + this.pixelVectorPositions.push(createVector().set(this.center).add(this.rightVector).sub(this.upVector)); + this.pixelVectorPositions.push(createVector().set(this.center).sub(this.rightVector).sub(this.upVector)); + + return this.pixelVectorPositions; + } + + //calculates the pixel positions of the corners are sets the shape as a polygon with those points + calculateVectorsAndSetShape() { + + //angle is in radians rotating clock wise + this.getPixelVectors(); + this.rightVector = createVector(this.w / 2, 0).rotate(this.angle); + this.upVector = createVector(0, -this.h / 2).rotate(this.angle); + + this.vectors = []; + this.vectors.push(createVector().set(this.center).sub(this.rightVector).add(this.upVector)); + this.vectors.push(createVector().set(this.center).add(this.rightVector).add(this.upVector)); + this.vectors.push(createVector().set(this.center).add(this.rightVector).sub(this.upVector)); + this.vectors.push(createVector().set(this.center).sub(this.rightVector).sub(this.upVector)); + + + this.vectors = p5VectorsToVec2(this.vectors); + + this.fixDef.shape = new b2PolygonShape(); + this.fixDef.shape.SetAsArray(this.vectors, 4); + + } + + //resets the center vector based on variables + resetCenter() { + this.center = createVector(this.x + this.w / 2, this.y + this.h / 2); + } + + + //shows a rectangle + showFixtureClass() { + translate(this.center.x, this.center.y); + rotate(this.angle); + rect(-this.w / 2, -this.h / 2, this.w, this.h); + + } + + //sets the position of the fixture and resets the fixture + setPosition(newPos) { + let difference = p5.Vector.sub(newPos, this.pixelCenter); + difference.rotate(-this.body.angle); + this.x += difference.x; + this.y += difference.y; + this.center = createVector(this.x + this.w / 2, this.y + this.h / 2); + this.calculateVectorsAndSetShape(); + this.resetFixture(); + } + + + //rotates the fixture + rotate(rotateAmount) { + this.angle += rotateAmount; + this.calculateVectorsAndSetShape(); + this.resetFixture(); + } + + //resizes the fixture + resize(increaseWidthAmount, increaseHeightAmount) { + this.w += increaseWidthAmount; + this.h += increaseHeightAmount; + this.w = max(this.w, 1); + this.h = max(this.h, 1); + + this.x = this.center.x - this.w / 2; + this.y = this.center.y - this.h / 2; + this.calculateVectorsAndSetShape(); + this.resetFixture(); + } + + //scales the fixture relative to the center of the body + scaleRelativeToBody(multiplyAmount) { + this.x *= multiplyAmount; + this.y *= multiplyAmount; + this.w *= multiplyAmount; + this.h *= multiplyAmount; + this.calculateVectorsAndSetShape(); + this.resetFixture(); + } + + //scales the fixture relative to its center + scale(scaleAmount) { + + this.w *= scaleAmount; + this.h *= scaleAmount; + this.x = this.center.x - this.w / 2; + this.y = this.center.y - this.h / 2; + + this.calculateVectorsAndSetShape(); + this.resetFixture(); + } + + //returns the fixture information as an object (like JSON) + getFixtureInfoAsObject() { + let obj = {fixtureType: "", x: 0, y: 0, w: 0, h: 0, angle: 0, fillColor: 0}; + + Object.keys(this).forEach((key) => { + if (key in obj) { + obj[key] = this[key]; + } + }); + return obj; + } + + //returns whether or not this fixture was hit by the lazer + hitByLazer(lazerX) { + this.getPixelVectors(); + for (let pos of this.pixelVectorPositions) { + let pos2 = cloneVector(pos); + pos2.rotate(this.body.body.GetAngle()); + if (this.body.getShiftedPixelCoordinates().x + pos2.x < lazerX) { + return true; + } + } + return false; + } + +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------ + +class CircleFixture extends Fixture { + //information is relative to the body + constructor(x, y, radius) { + super(createVector(x, y)); + this.x = x; + this.y = y; + this.radius = radius; + this.setShape(); + this.fixtureType = "circle"; + + } + + //returns whether or not the pixel position is within the fixture + isLocalPixelPosWithinFixture(localPixelPosition) { + let distance = dist(localPixelPosition.x, localPixelPosition.y, this.center.x, this.center.y); + + return (distance < this.radius); + } + + //sets the shape of the fixture def as a circle + setShape() { + this.fixDef.shape = new b2CircleShape(this.radius / SCALE); + this.fixDef.shape.SetLocalPosition(new Vec2(this.x / SCALE, this.y / SCALE)); + } + + //draw a circle + showFixtureClass() { + ellipse(this.x, this.y, this.radius * 2); + } + + //sets the position of the fixture and resets the fixture + setPosition(newPos) { + let difference = p5.Vector.sub(newPos, this.pixelCenter); + difference.rotate(-this.body.angle); + + this.x += difference.x; + this.y += difference.y; + this.setCenter(); + this.setShape(); + this.resetFixture(); + } + + //sets the center using its x and y coordinates + setCenter() { + this.center = createVector(this.x, this.y); + } + + //doesnt do shit to a circle + rotate(rotateAmount) { + } + + //resizes the fixture + resize(increaseRadiusAmount) { + this.radius += increaseRadiusAmount; + this.radius = max(this.radius, 1); + this.setShape(); + this.resetFixture(); + } + + //removes it then adds it to body + resetFixture() { + this.body.body.DestroyFixture(this.fixture); + this.addToBody(this.body); + } + + //scales the fixture relative to the center of the body + scaleRelativeToBody(multiplyAmount) { + this.x *= multiplyAmount; + this.y *= multiplyAmount; + this.radius *= multiplyAmount; + this.setCenter(); + this.setShape(); + this.resetFixture(); + } + + //returns the fixture information as an object (like JSON) + getFixtureInfoAsObject() { + let obj = {fixtureType: "", x: 0, y: 0, radius: 0, fillColor: 0}; + Object.keys(this).forEach((key) => { + if (key in obj) { + obj[key] = this[key]; + } + }); + return obj; + } + + //returns whether or not this fixture was hit by the lazer + hitByLazer(lazerX) { + let pos = createVector(this.x, this.y); + pos.rotate(this.body.body.GetAngle()); + return (this.body.getShiftedPixelCoordinates().x + pos.x - this.radius < lazerX); + } + +} + +//---------------------------------------------------------------------------------------------------------------------------------------- +//an array fixture is a polygon, which is defined by an array of points +//ARRAY FIXTURES can NOT BE CONCAVE +//see compound fixtures to see how this is handled +class ArrayFixture extends Fixture { + //information is relative to the body + constructor(arr) { + super(); + this.fixtureType = "array"; + this.pixelVectorPositions = arr; + this.center; + this.setCenter(); + this.ensureClockwise(); + this.setShape(); + + + } + + //returns whether or not the pixel position is within the fixture + isLocalPixelPosWithinFixture(localPixelPosition) { + let distance = dist(localPixelPosition.x, localPixelPosition.y, this.center.x, this.center.y); + let vectorsRelativeToCenter = []; + + for (var v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, this.center)); + } + let totalLength = 0; + for (var v of vectorsRelativeToCenter) { + totalLength += v.mag(); + } + let averageLength = totalLength / this.pixelVectorPositions.length; + return (distance < averageLength); + } + + //if the points are going anticlockwise box2d shits itself so we need to ensure that that doesn't happen + ensureClockwise() { + let vectorsRelativeToCenter = []; + let headingValues = []; + for (var v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, this.center)); + } + + + for (var v of vectorsRelativeToCenter) { + let temp = v.heading(); + if (temp < 0) { + temp += 2 * PI; + } + + headingValues.push(temp); + } + + + let rotationalDifferenceTotal = 0; + for (var i = 0; i < headingValues.length; i++) { + let difference = 0; + if (i == headingValues.length - 1) { + difference = headingValues[0] - headingValues[i]; + } else { + difference = headingValues[i + 1] - headingValues[i]; + } + if (difference > 0) { + rotationalDifferenceTotal += 1; + } else { + rotationalDifferenceTotal -= 1; + } + } + + if (rotationalDifferenceTotal < 0) { + this.pixelVectorPositions.reverse(); + } + } + + //sets the shape of the fixture def as an array + setShape() { + this.vectors = p5VectorsToVec2(this.pixelVectorPositions); + this.fixDef.shape = new b2PolygonShape(); + this.fixDef.shape.SetAsArray(this.vectors, this.pixelVectorPositions.length); + } + + //sets the center as the average position of all the points + setCenter() { + let x = 0; + let y = 0; + for (var v of this.pixelVectorPositions) { + x += v.x; + y += v.y; + } + + x /= this.pixelVectorPositions.length; + y /= this.pixelVectorPositions.length; + this.center = createVector(x, y); + + } + + //shows a polygon + showFixtureClass() { + beginShape(); + for (var v of this.vectors) { + vertex(v.x * SCALE, v.y * SCALE); + } + endShape(CLOSE); + + + } + + //moves all the points so the center is now in the new position + setPosition(newPos) { + let difference = p5.Vector.sub(newPos, this.pixelCenter); + difference.rotate(-this.body.angle); + + for (var i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i].x += difference.x; + this.pixelVectorPositions[i].y += difference.y; + } + + this.setCenter(); + this.setShape(); + this.resetFixture(); + } + + //rotates the polygon by getting each point as a vector then rotating it + rotate(rotateAmount, rotateAround) { + if (!rotateAround) { + rotateAround = this.center; + } + let vectorsRelativeToCenter = []; + + for (let v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, rotateAround)); + } + + for (let v of vectorsRelativeToCenter) { + v.rotate(rotateAmount); + } + + for (var i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i] = p5.Vector.add(vectorsRelativeToCenter[i], rotateAround); + } + this.setCenter(); + this.setShape(); + this.resetFixture(); + + } + + //scales the fixture relative to the body + scaleRelativeToBody(multiplyAmount) { + this.resize(multiplyAmount, createVector(0, 0)); + } + + //resizes the fixture relative to the input vector + resize(multAmount, resizeRelativeTo) { + if (!resizeRelativeTo) { + resizeRelativeTo = this.center; + } + + let vectorsRelativeToCenter = []; + + for (var v of this.pixelVectorPositions) { + vectorsRelativeToCenter.push(p5.Vector.sub(v, resizeRelativeTo)); + } + + for (var v of vectorsRelativeToCenter) { + v.mult(multAmount); + } + + for (var i = 0; i < this.pixelVectorPositions.length; i++) { + this.pixelVectorPositions[i] = p5.Vector.add(vectorsRelativeToCenter[i], resizeRelativeTo); + } + this.setCenter(); + this.setShape(); + this.resetFixture(); + + } + + //returns the fixture information as an object (like JSON) + getFixtureInfoAsObject() { + let obj = {fixtureType: "", fillColor: 0}; + obj.pixelVectorPositions = this.pixelVectorPositions.map((x) => new createEvanVector(x)); + obj.fillColor = this.fillColor; + return obj; + } + + + //returns whether or not this fixture was hit by the lazer + hitByLazer(lazerX) { + + for (let pos of this.pixelVectorPositions) { + let pos2 = cloneVector(pos); + pos2.rotate(-this.body.body.GetAngle()); + if (this.body.getShiftedPixelCoordinates().x + pos2.x < lazerX) { + return true; + } + } + return false; + } + +} +//since p5s vectors cannot be added to an object becuase they have infinite loops i need to create my own with no fancy stuff +function createEvanVector(vec) { + this.x = vec.x; + this.y = vec.y; +} + +//converts a list of pixel positions to world positions +function p5VectorsToVec2(vectors) { + let newVecs = []; + for (var i = 0; i < vectors.length; i++) { + newVecs.push(new Vec2(vectors[i].x / SCALE, vectors[i].y / SCALE)); + } + return newVecs; +} diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Fonts/RobotoMono-Bold.ttf b/websiteUsingBulma/ProjectSketches/Creature Creator/Fonts/RobotoMono-Bold.ttf new file mode 100644 index 0000000..07ef607 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Fonts/RobotoMono-Bold.ttf differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Fonts/RobotoMono-Regular.ttf b/websiteUsingBulma/ProjectSketches/Creature Creator/Fonts/RobotoMono-Regular.ttf new file mode 100644 index 0000000..b158a33 Binary files /dev/null and b/websiteUsingBulma/ProjectSketches/Creature Creator/Fonts/RobotoMono-Regular.ttf differ diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/Joints.js b/websiteUsingBulma/ProjectSketches/Creature Creator/Joints.js new file mode 100644 index 0000000..565e221 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/Joints.js @@ -0,0 +1,382 @@ +//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +//revolute joints are joints which connect 2 bodies at a single point which can rotate, +//think of them like motors +//this is actually how the creatures move around and its these movements which are optimised so that bad boy can learn to walk +class RevoluteJoint { + + //the constructor will need 2 bodyies a anchor position(the position of the joint itself) and (optional) a box2d world that the joint should be placed in + constructor(body1, body2, anchorX, anchorY, worldParameter) { + this.anchor = new Vec2(anchorX / SCALE, anchorY / SCALE);//the anchor is the position of the joint (in world coordinates) + //define the revolute joint + this.revJointDef = new b2RevoluteJointDef(); + this.revJointDef.Initialize(body1.body, body2.body, this.anchor); + + //by default no limits are added to the joint + this.limitRevolution = false; + this.lowerLimit = -Math.PI / 4.0; + this.upperLimit = Math.PI / 4.0; + + this.joint = null;//the box2d joint object + + //the bodies this joint joins + this.body1 = body1; + this.body2 = body2; + + //selection variables used by various modes to edit this joint + this.selected = false; + this.lookLikeSelected = false; + this.selectedLimit = -1; + + + if (worldParameter) {//if the box2dWorld parameter is not null then this joint has its own box2dWorld + this.world = worldParameter; + } else { //otherwise it uses the global box2dWorld + this.world = world.box2dWorld; + } + this.addToWorld();//add the joint to the world, this sets this.joint + + + this.type = "revolute";//not really used but i have it here just incase i want to add distance joints, which kinda are like muscles + + //motor variables + this.motorSpeed = 0; + this.motorTorque = 0; + + this.controlJoint = false;//another kinda useless variable for allowing you to turn off the players ability to control this joint, this functionality was not included in the final thing + + } + + //converts the information into an object (like a JSON), used to pass the creatures joint info to the NEAT algorithm + getJointInfoAsObject() { + let obj = { + type: "", anchorX: 0, anchorY: 0, body1No: 0, body2No: 0, limitRevolution: 0, upperLimit: 0, + lowerLimit: 0 + }; + Object.assignMatching(obj, this); + obj.body1No = creature.getBodyNo(this.body1); + obj.body2No = creature.getBodyNo(this.body2); + let anchor = this.getPixelCoordinatesOfAnchor(); + obj.anchorX = anchor.x; + obj.anchorY = anchor.y; + return obj; + } + + //sets the upper limit of the joint and ensures that its within +0 and +2*PI of the lower joint + setUpperLimit(limit) { + + + if (this.lowerLimit > limit) { + limit += 2 * Math.PI; + } else if (limit - this.lowerLimit > 2 * Math.PI) { + limit -= 2 * Math.PI; + } + this.limitRevolution = true; + this.upperLimit = limit; + this.revJointDef.upperAngle = limit; + this.revJointDef.enableLimit = true; + + } + + //sets the lower limit of the joint and ensures that its within -0 and -2*PI of the upper joint + setLowerLimit(limit) { + if (this.upperLimit < limit) { + limit -= 2 * Math.PI; + } else if (this.upperLimit - limit > 2 * Math.PI) { + limit += 2 * Math.PI; + } + this.limitRevolution = true; + this.lowerLimit = limit; + this.revJointDef.lowerAngle = limit; + this.revJointDef.enableLimit = true; + } + + + //enables limits for this joint and sets the upper and lower limit + enableLimits(enabled) { + this.limitRevolution = enabled; + this.revJointDef.enableLimit = enabled; + if (enabled) { + this.setUpperLimit(this.upperLimit); + this.setLowerLimit(this.lowerLimit); + } + this.reset(); + } + + //switches body1 and body2, this is used for changing the focus body when limiting a joint + switchBodies() { + let temp = this.body1; + this.body1 = this.body2; + this.body2 = temp; + this.reset(); + } + + //resets the position of an anchor + setPositionOfAnchor(anchorNo, newPosition) { + this.anchor = new Vec2((newPosition.x - getPannedOffset().x) / SCALE, (newPosition.y - getPannedOffset().y) / SCALE); + this.reset(); + } + + + //gets the position the joint is on the screen in pixels + getPixelCoordinatesOfAnchor(anchorNo) { + let x = this.anchor.x * SCALE + getPannedOffset().x; + let y = this.anchor.y * SCALE + getPannedOffset().y; + return createVector(x, y); + + } + + //essentially the same as the getPixelCoordinatesOfAnchor + //its a different thing because distance joints have 2 different anchors to the center would be the average of the 2 + //anywhay thats why this exists. + getPixelCenter() { + let anchorPoint = createVector(this.anchor.x * SCALE, this.anchor.y * SCALE); + let pixelCenter = p5.Vector.add(anchorPoint, getPannedOffset()); //windowOffset) + return pixelCenter; + } + + //add the joint to the box2d world, making it a real thing + addToWorld() { + this.joint = this.world.CreateJoint(this.revJointDef); + } + + //enable the joint motor in the box2d world + enableMotor() { + this.joint.EnableMotor(true); + } + + //disables the joint motor in the box2d world + disableMotor() { + this.joint.EnableMotor(false); + } + + //sets the motor enabled in the box2d world + setMotorEnabled(val) { + this.joint.EnableMotor(val); + } + + //again this bad boy is fuckin useless but here she is + setAsControlJoint() { + this.controlJoint = true; + } + + setMotorSpeed(val) { + + this.joint.SetMotorSpeed(val); + this.motorSpeed = val; + } + + setMaxTorque(val) { + this.joint.SetMaxMotorTorque(val); + this.motorTorque = val; + } + + setValues(enabled, speed, torque) { + this.setMotorEnabled(enabled); + this.setMotorSpeed(speed); + this.setMaxTorque(torque); + } + + //shows the joint and if the joint has limits, show them (maybe) + show() { + push(); + + let anchor1 = this.joint.GetAnchorA(); + translate(anchor1.x * SCALE, anchor1.y * SCALE); + + //show the limit arc + if (this.limitRevolution && (showJointLimits || this.selectedLimit !== -1) && !buttonManager.areCosmeticsActive()) { + + //create a vector from the anchor to body 2 + let anchorPos = this.getPixelCoordinatesOfAnchor(0); + let body2Pos = createVector(this.body2.x, this.body2.y); + let anchorToBody2 = p5.Vector.sub(body2Pos, anchorPos); + + //get this angle of this vector + let angleBetweenBodies = anchorToBody2.heading(); + let angleFromVertical = Math.PI / 2.0 + angleBetweenBodies; + + //rotate by body1s angle + rotate(this.body1.body.GetAngle() - this.body1.angle); + //then by the angle between the anchor and the body2 + rotate(angleFromVertical); + translate(-0.6, -0.6); + + let lowLim = createVector(-3, -100).rotate(this.lowerLimit); + let upLim = createVector(3, -100).rotate(this.upperLimit); + + let temp = createVector().set(lowLim).mult(0.3); + stroke(100, 100, 255, 100); + temp.rotate(Math.PI / 100); + while (temp.isBetweenVectors(lowLim, upLim)) { + line(0, 0, temp.x, temp.y); + temp.rotate(Math.PI / 100); + } + + //show the limit lines if they are selected + strokeWeight(1); + stroke(0, 150);//,100,155); + + if (this.selectedLimit !== -1) { + line(0, 0, lowLim.x, lowLim.y); + line(0, 0, upLim.x, upLim.y); + } + strokeWeight(3); + stroke(255, 255, 0); + if (this.selectedLimit === 0) + line(0, 0, lowLim.x, lowLim.y); + else if (this.selectedLimit === 1) + line(0, 0, upLim.x, upLim.y); + + } + + //show the little dot for the joitn + stroke(outlineColor); + fill(0, 0, 0); + if (this.selected || this.lookLikeSelected) { + + stroke(255, 255, 0); + if (buttonManager.isInMode("Delete")) { + stroke(255, 0, 0); + } + + strokeWeight(2); + ellipse(0, 0, 7); + + } else { + ellipse(0, 0, 5); + } + + pop(); + + } + + + //moves the selected limit to the mouse position. essentially rotates that baby to face the mouse position + moveSelectedLimitToMousePosition() { + + let anchorPos = this.getPixelCoordinatesOfAnchor(0); + let anchorToMouse = createVector(mouseX - anchorPos.x, mouseY - anchorPos.y); + + + //create a vector from the anchor to body 2 + + let body2Pos = createVector(this.body2.x, this.body2.y); + let anchorToBody2 = p5.Vector.sub(body2Pos, anchorPos); + + //get this angle of this vector + let angleBetweenBodies = anchorToBody2.heading(); + let angleFromVertical = Math.PI / 2.0 + angleBetweenBodies; + + //rotate by body1s angle + // let mousePosRotatedByBody1Angle = anchorToMouse.rotate(-this.body1.body.GetAngle()); + //then by the angle between the anchor and the body2 + let relativeMousePos = anchorToMouse.rotate(-angleFromVertical); + if (this.selectedLimit === 0) { + + // let dif = this.lowerLimit - relateMousePos.heading + this.setLowerLimit(relativeMousePos.heading() + Math.PI / 2); + + } else { + this.setUpperLimit(relativeMousePos.heading() + Math.PI / 2); + } + + this.reset(); + } + + //selects the limit which is closest to the mouses position + selectLimitClosestToMousePosition() { + + + let anchorPos = this.getPixelCoordinatesOfAnchor(0); + let anchorToMouse = createVector(mouseX - anchorPos.x, mouseY - anchorPos.y); + + + //create a vector from the anchor to body 2 + + let body2Pos = createVector(this.body2.x, this.body2.y); + let anchorToBody2 = p5.Vector.sub(body2Pos, anchorPos); + + //get this angle of this vector + let angleBetweenBodies = anchorToBody2.heading(); + let angleFromVertical = Math.PI / 2.0 + angleBetweenBodies; + + + //then by the angle between the anchor and the body2 + let relativeMousePos = anchorToMouse.rotate(-angleFromVertical); + + let lowLim = createVector(-3, -100).rotate(this.lowerLimit); + let upLim = createVector(3, -100).rotate(this.upperLimit); + + if (dist(lowLim.x, lowLim.y, relativeMousePos.x, relativeMousePos.y) < dist(upLim.x, upLim.y, relativeMousePos.x, relativeMousePos.y)) { + this.selectedLimit = 0; + } else { + this.selectedLimit = 1; + } + + + //if another joint is moused over then also look like its selected. + + + let minIndex = -1; + let min = 100000; + + + for (var i = 0; i < creature.joints.length; i++) { + let pos = creature.joints[i].getPixelCenter(); + let distance = dist(mouseX, mouseY, pos.x, pos.y); + if (distance < min && distance < 8) { + min = distance; + minIndex = i; + } + } + + + if (minIndex !== -1 && minIndex != creature.selectedJoint) { + creature.joints[minIndex].lookLikeSelected = true; + } + + + } + + //remove the joint from the box2d world + remove() { + if (this.joint != null) { + this.world.DestroyJoint(this.joint); + } + } + + //destroy the joint then recreate it with the current variables + reset() { + if (this.joint != null) { + this.world.DestroyJoint(this.joint); + } + this.revJointDef.Initialize(this.body1.body, this.body2.body, this.anchor); + this.addToWorld(); + this.joint.SetMotorSpeed(this.motorSpeed); + this.joint.SetMaxMotorTorque(this.motorTorque); + this.joint.EnableMotor(this.enableMotor); + } + +} + +//used to tell if a vector is between 2 other vectors a and b +p5.Vector.prototype.isBetweenVectors = function (a, b) { + let aAngle = a.heading(); + let bAngle = b.heading(); + if (aAngle > bAngle) { + aAngle -= 2 * Math.PI; + + } + + let thisAngle = this.heading(); + if (thisAngle >= aAngle && thisAngle <= bAngle) { + return true; + } + thisAngle -= 2 * PI; + if (thisAngle >= aAngle && thisAngle <= bAngle) { + return true; + } + return false; + + +}; \ No newline at end of file diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/README.md b/websiteUsingBulma/ProjectSketches/Creature Creator/README.md new file mode 100644 index 0000000..4379964 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/README.md @@ -0,0 +1 @@ +# AI Learns To Walk with editor diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/creatureCreator.html b/websiteUsingBulma/ProjectSketches/Creature Creator/creatureCreator.html new file mode 100644 index 0000000..8d2abc5 --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/creatureCreator.html @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ + + +
+
+ + +
+ + + + + + + + + diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/libraries/Box2d.js b/websiteUsingBulma/ProjectSketches/Creature Creator/libraries/Box2d.js new file mode 100644 index 0000000..a9798fb --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/libraries/Box2d.js @@ -0,0 +1,10688 @@ +/* + * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ +var Box2D = {}; + +(function(a2j, undefined) { + + if(!(Object.prototype.defineProperty instanceof Function) && Object.prototype.__defineGetter__ instanceof Function && Object.prototype.__defineSetter__ instanceof Function) { + Object.defineProperty = function(obj, p, cfg) { + if(cfg.get instanceof Function) + obj.__defineGetter__(p, cfg.get); + if(cfg.set instanceof Function) + obj.__defineSetter__(p, cfg.set); + } + } + + function emptyFn() {}; + a2j.inherit = function(cls, base) { + var tmpCtr = cls; + emptyFn.prototype = base.prototype; + cls.prototype = new emptyFn; + cls.prototype.constructor = tmpCtr; + }; + + a2j.generateCallback = function generateCallback(context, cb) { + return function() { + cb.apply(context, arguments); + }; + }; + + a2j.NVector = function NVector(length) { + if(length === undefined) length = 0; + var tmp = new Array(length || 0); + for(var i = 0; i < length; ++i) + tmp[i] = 0; + return tmp; + }; + + a2j.is = function is(o1, o2) { + if(o1 === null) return false; + if((o2 instanceof Function) && (o1 instanceof o2)) return true; + if((o1.constructor.__implements != undefined) && (o1.constructor.__implements[o2])) return true; + return false; + }; + + a2j.parseUInt = function(v) { + return Math.abs(parseInt(v)); + } + +})(Box2D); + +//#TODO remove assignments from global namespace +var Vector = Array; +var Vector_a2j_Number = Box2D.NVector; +//package structure +if(typeof(Box2D) === "undefined") Box2D = {}; +if(typeof(Box2D.Collision) === "undefined") Box2D.Collision = {}; +if(typeof(Box2D.Collision.Shapes) === "undefined") Box2D.Collision.Shapes = {}; +if(typeof(Box2D.Common) === "undefined") Box2D.Common = {}; +if(typeof(Box2D.Common.Math) === "undefined") Box2D.Common.Math = {}; +if(typeof(Box2D.Dynamics) === "undefined") Box2D.Dynamics = {}; +if(typeof(Box2D.Dynamics.Contacts) === "undefined") Box2D.Dynamics.Contacts = {}; +if(typeof(Box2D.Dynamics.Controllers) === "undefined") Box2D.Dynamics.Controllers = {}; +if(typeof(Box2D.Dynamics.Joints) === "undefined") Box2D.Dynamics.Joints = {}; +//pre-definitions +(function() { + Box2D.Collision.IBroadPhase = 'Box2D.Collision.IBroadPhase'; + + function b2AABB() { + b2AABB.b2AABB.apply(this, arguments); + }; + Box2D.Collision.b2AABB = b2AABB; + + function b2Bound() { + b2Bound.b2Bound.apply(this, arguments); + }; + Box2D.Collision.b2Bound = b2Bound; + + function b2BoundValues() { + b2BoundValues.b2BoundValues.apply(this, arguments); + if(this.constructor === b2BoundValues) this.b2BoundValues.apply(this, arguments); + }; + Box2D.Collision.b2BoundValues = b2BoundValues; + + function b2Collision() { + b2Collision.b2Collision.apply(this, arguments); + }; + Box2D.Collision.b2Collision = b2Collision; + + function b2ContactID() { + b2ContactID.b2ContactID.apply(this, arguments); + if(this.constructor === b2ContactID) this.b2ContactID.apply(this, arguments); + }; + Box2D.Collision.b2ContactID = b2ContactID; + + function b2ContactPoint() { + b2ContactPoint.b2ContactPoint.apply(this, arguments); + }; + Box2D.Collision.b2ContactPoint = b2ContactPoint; + + function b2Distance() { + b2Distance.b2Distance.apply(this, arguments); + }; + Box2D.Collision.b2Distance = b2Distance; + + function b2DistanceInput() { + b2DistanceInput.b2DistanceInput.apply(this, arguments); + }; + Box2D.Collision.b2DistanceInput = b2DistanceInput; + + function b2DistanceOutput() { + b2DistanceOutput.b2DistanceOutput.apply(this, arguments); + }; + Box2D.Collision.b2DistanceOutput = b2DistanceOutput; + + function b2DistanceProxy() { + b2DistanceProxy.b2DistanceProxy.apply(this, arguments); + }; + Box2D.Collision.b2DistanceProxy = b2DistanceProxy; + + function b2DynamicTree() { + b2DynamicTree.b2DynamicTree.apply(this, arguments); + if(this.constructor === b2DynamicTree) this.b2DynamicTree.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTree = b2DynamicTree; + + function b2DynamicTreeBroadPhase() { + b2DynamicTreeBroadPhase.b2DynamicTreeBroadPhase.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTreeBroadPhase = b2DynamicTreeBroadPhase; + + function b2DynamicTreeNode() { + b2DynamicTreeNode.b2DynamicTreeNode.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTreeNode = b2DynamicTreeNode; + + function b2DynamicTreePair() { + b2DynamicTreePair.b2DynamicTreePair.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTreePair = b2DynamicTreePair; + + function b2Manifold() { + b2Manifold.b2Manifold.apply(this, arguments); + if(this.constructor === b2Manifold) this.b2Manifold.apply(this, arguments); + }; + Box2D.Collision.b2Manifold = b2Manifold; + + function b2ManifoldPoint() { + b2ManifoldPoint.b2ManifoldPoint.apply(this, arguments); + if(this.constructor === b2ManifoldPoint) this.b2ManifoldPoint.apply(this, arguments); + }; + Box2D.Collision.b2ManifoldPoint = b2ManifoldPoint; + + function b2Point() { + b2Point.b2Point.apply(this, arguments); + }; + Box2D.Collision.b2Point = b2Point; + + function b2RayCastInput() { + b2RayCastInput.b2RayCastInput.apply(this, arguments); + if(this.constructor === b2RayCastInput) this.b2RayCastInput.apply(this, arguments); + }; + Box2D.Collision.b2RayCastInput = b2RayCastInput; + + function b2RayCastOutput() { + b2RayCastOutput.b2RayCastOutput.apply(this, arguments); + }; + Box2D.Collision.b2RayCastOutput = b2RayCastOutput; + + function b2Segment() { + b2Segment.b2Segment.apply(this, arguments); + }; + Box2D.Collision.b2Segment = b2Segment; + + function b2SeparationFunction() { + b2SeparationFunction.b2SeparationFunction.apply(this, arguments); + }; + Box2D.Collision.b2SeparationFunction = b2SeparationFunction; + + function b2Simplex() { + b2Simplex.b2Simplex.apply(this, arguments); + if(this.constructor === b2Simplex) this.b2Simplex.apply(this, arguments); + }; + Box2D.Collision.b2Simplex = b2Simplex; + + function b2SimplexCache() { + b2SimplexCache.b2SimplexCache.apply(this, arguments); + }; + Box2D.Collision.b2SimplexCache = b2SimplexCache; + + function b2SimplexVertex() { + b2SimplexVertex.b2SimplexVertex.apply(this, arguments); + }; + Box2D.Collision.b2SimplexVertex = b2SimplexVertex; + + function b2TimeOfImpact() { + b2TimeOfImpact.b2TimeOfImpact.apply(this, arguments); + }; + Box2D.Collision.b2TimeOfImpact = b2TimeOfImpact; + + function b2TOIInput() { + b2TOIInput.b2TOIInput.apply(this, arguments); + }; + Box2D.Collision.b2TOIInput = b2TOIInput; + + function b2WorldManifold() { + b2WorldManifold.b2WorldManifold.apply(this, arguments); + if(this.constructor === b2WorldManifold) this.b2WorldManifold.apply(this, arguments); + }; + Box2D.Collision.b2WorldManifold = b2WorldManifold; + + function ClipVertex() { + ClipVertex.ClipVertex.apply(this, arguments); + }; + Box2D.Collision.ClipVertex = ClipVertex; + + function Features() { + Features.Features.apply(this, arguments); + }; + Box2D.Collision.Features = Features; + + function b2CircleShape() { + b2CircleShape.b2CircleShape.apply(this, arguments); + if(this.constructor === b2CircleShape) this.b2CircleShape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2CircleShape = b2CircleShape; + + function b2EdgeChainDef() { + b2EdgeChainDef.b2EdgeChainDef.apply(this, arguments); + if(this.constructor === b2EdgeChainDef) this.b2EdgeChainDef.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2EdgeChainDef = b2EdgeChainDef; + + function b2EdgeShape() { + b2EdgeShape.b2EdgeShape.apply(this, arguments); + if(this.constructor === b2EdgeShape) this.b2EdgeShape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2EdgeShape = b2EdgeShape; + + function b2MassData() { + b2MassData.b2MassData.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2MassData = b2MassData; + + function b2PolygonShape() { + b2PolygonShape.b2PolygonShape.apply(this, arguments); + if(this.constructor === b2PolygonShape) this.b2PolygonShape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2PolygonShape = b2PolygonShape; + + function b2Shape() { + b2Shape.b2Shape.apply(this, arguments); + if(this.constructor === b2Shape) this.b2Shape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2Shape = b2Shape; + Box2D.Common.b2internal = 'Box2D.Common.b2internal'; + + function b2Color() { + b2Color.b2Color.apply(this, arguments); + if(this.constructor === b2Color) this.b2Color.apply(this, arguments); + }; + Box2D.Common.b2Color = b2Color; + + function b2Settings() { + b2Settings.b2Settings.apply(this, arguments); + }; + Box2D.Common.b2Settings = b2Settings; + + function b2Mat22() { + b2Mat22.b2Mat22.apply(this, arguments); + if(this.constructor === b2Mat22) this.b2Mat22.apply(this, arguments); + }; + Box2D.Common.Math.b2Mat22 = b2Mat22; + + function b2Mat33() { + b2Mat33.b2Mat33.apply(this, arguments); + if(this.constructor === b2Mat33) this.b2Mat33.apply(this, arguments); + }; + Box2D.Common.Math.b2Mat33 = b2Mat33; + + function b2Math() { + b2Math.b2Math.apply(this, arguments); + }; + Box2D.Common.Math.b2Math = b2Math; + + function b2Sweep() { + b2Sweep.b2Sweep.apply(this, arguments); + }; + Box2D.Common.Math.b2Sweep = b2Sweep; + + function b2Transform() { + b2Transform.b2Transform.apply(this, arguments); + if(this.constructor === b2Transform) this.b2Transform.apply(this, arguments); + }; + Box2D.Common.Math.b2Transform = b2Transform; + + function b2Vec2() { + b2Vec2.b2Vec2.apply(this, arguments); + if(this.constructor === b2Vec2) this.b2Vec2.apply(this, arguments); + }; + Box2D.Common.Math.b2Vec2 = b2Vec2; + + function b2Vec3() { + b2Vec3.b2Vec3.apply(this, arguments); + if(this.constructor === b2Vec3) this.b2Vec3.apply(this, arguments); + }; + Box2D.Common.Math.b2Vec3 = b2Vec3; + + function b2Body() { + b2Body.b2Body.apply(this, arguments); + if(this.constructor === b2Body) this.b2Body.apply(this, arguments); + }; + Box2D.Dynamics.b2Body = b2Body; + + function b2BodyDef() { + b2BodyDef.b2BodyDef.apply(this, arguments); + if(this.constructor === b2BodyDef) this.b2BodyDef.apply(this, arguments); + }; + Box2D.Dynamics.b2BodyDef = b2BodyDef; + + function b2ContactFilter() { + b2ContactFilter.b2ContactFilter.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactFilter = b2ContactFilter; + + function b2ContactImpulse() { + b2ContactImpulse.b2ContactImpulse.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactImpulse = b2ContactImpulse; + + function b2ContactListener() { + b2ContactListener.b2ContactListener.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactListener = b2ContactListener; + + function b2ContactManager() { + b2ContactManager.b2ContactManager.apply(this, arguments); + if(this.constructor === b2ContactManager) this.b2ContactManager.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactManager = b2ContactManager; + + function b2DebugDraw() { + b2DebugDraw.b2DebugDraw.apply(this, arguments); + if(this.constructor === b2DebugDraw) this.b2DebugDraw.apply(this, arguments); + }; + Box2D.Dynamics.b2DebugDraw = b2DebugDraw; + + function b2DestructionListener() { + b2DestructionListener.b2DestructionListener.apply(this, arguments); + }; + Box2D.Dynamics.b2DestructionListener = b2DestructionListener; + + function b2FilterData() { + b2FilterData.b2FilterData.apply(this, arguments); + }; + Box2D.Dynamics.b2FilterData = b2FilterData; + + function b2Fixture() { + b2Fixture.b2Fixture.apply(this, arguments); + if(this.constructor === b2Fixture) this.b2Fixture.apply(this, arguments); + }; + Box2D.Dynamics.b2Fixture = b2Fixture; + + function b2FixtureDef() { + b2FixtureDef.b2FixtureDef.apply(this, arguments); + if(this.constructor === b2FixtureDef) this.b2FixtureDef.apply(this, arguments); + }; + Box2D.Dynamics.b2FixtureDef = b2FixtureDef; + + function b2Island() { + b2Island.b2Island.apply(this, arguments); + if(this.constructor === b2Island) this.b2Island.apply(this, arguments); + }; + Box2D.Dynamics.b2Island = b2Island; + + function b2TimeStep() { + b2TimeStep.b2TimeStep.apply(this, arguments); + }; + Box2D.Dynamics.b2TimeStep = b2TimeStep; + + function b2World() { + b2World.b2World.apply(this, arguments); + if(this.constructor === b2World) this.b2World.apply(this, arguments); + }; + Box2D.Dynamics.b2World = b2World; + + function b2CircleContact() { + b2CircleContact.b2CircleContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2CircleContact = b2CircleContact; + + function b2Contact() { + b2Contact.b2Contact.apply(this, arguments); + if(this.constructor === b2Contact) this.b2Contact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2Contact = b2Contact; + + function b2ContactConstraint() { + b2ContactConstraint.b2ContactConstraint.apply(this, arguments); + if(this.constructor === b2ContactConstraint) this.b2ContactConstraint.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactConstraint = b2ContactConstraint; + + function b2ContactConstraintPoint() { + b2ContactConstraintPoint.b2ContactConstraintPoint.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactConstraintPoint = b2ContactConstraintPoint; + + function b2ContactEdge() { + b2ContactEdge.b2ContactEdge.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactEdge = b2ContactEdge; + + function b2ContactFactory() { + b2ContactFactory.b2ContactFactory.apply(this, arguments); + if(this.constructor === b2ContactFactory) this.b2ContactFactory.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactFactory = b2ContactFactory; + + function b2ContactRegister() { + b2ContactRegister.b2ContactRegister.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactRegister = b2ContactRegister; + + function b2ContactResult() { + b2ContactResult.b2ContactResult.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactResult = b2ContactResult; + + function b2ContactSolver() { + b2ContactSolver.b2ContactSolver.apply(this, arguments); + if(this.constructor === b2ContactSolver) this.b2ContactSolver.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactSolver = b2ContactSolver; + + function b2EdgeAndCircleContact() { + b2EdgeAndCircleContact.b2EdgeAndCircleContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2EdgeAndCircleContact = b2EdgeAndCircleContact; + + function b2NullContact() { + b2NullContact.b2NullContact.apply(this, arguments); + if(this.constructor === b2NullContact) this.b2NullContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2NullContact = b2NullContact; + + function b2PolyAndCircleContact() { + b2PolyAndCircleContact.b2PolyAndCircleContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PolyAndCircleContact = b2PolyAndCircleContact; + + function b2PolyAndEdgeContact() { + b2PolyAndEdgeContact.b2PolyAndEdgeContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PolyAndEdgeContact = b2PolyAndEdgeContact; + + function b2PolygonContact() { + b2PolygonContact.b2PolygonContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PolygonContact = b2PolygonContact; + + function b2PositionSolverManifold() { + b2PositionSolverManifold.b2PositionSolverManifold.apply(this, arguments); + if(this.constructor === b2PositionSolverManifold) this.b2PositionSolverManifold.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PositionSolverManifold = b2PositionSolverManifold; + + function b2BuoyancyController() { + b2BuoyancyController.b2BuoyancyController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2BuoyancyController = b2BuoyancyController; + + function b2ConstantAccelController() { + b2ConstantAccelController.b2ConstantAccelController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2ConstantAccelController = b2ConstantAccelController; + + function b2ConstantForceController() { + b2ConstantForceController.b2ConstantForceController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2ConstantForceController = b2ConstantForceController; + + function b2Controller() { + b2Controller.b2Controller.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2Controller = b2Controller; + + function b2ControllerEdge() { + b2ControllerEdge.b2ControllerEdge.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2ControllerEdge = b2ControllerEdge; + + function b2GravityController() { + b2GravityController.b2GravityController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2GravityController = b2GravityController; + + function b2TensorDampingController() { + b2TensorDampingController.b2TensorDampingController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2TensorDampingController = b2TensorDampingController; + + function b2DistanceJoint() { + b2DistanceJoint.b2DistanceJoint.apply(this, arguments); + if(this.constructor === b2DistanceJoint) this.b2DistanceJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2DistanceJoint = b2DistanceJoint; + + function b2DistanceJointDef() { + b2DistanceJointDef.b2DistanceJointDef.apply(this, arguments); + if(this.constructor === b2DistanceJointDef) this.b2DistanceJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2DistanceJointDef = b2DistanceJointDef; + + function b2FrictionJoint() { + b2FrictionJoint.b2FrictionJoint.apply(this, arguments); + if(this.constructor === b2FrictionJoint) this.b2FrictionJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2FrictionJoint = b2FrictionJoint; + + function b2FrictionJointDef() { + b2FrictionJointDef.b2FrictionJointDef.apply(this, arguments); + if(this.constructor === b2FrictionJointDef) this.b2FrictionJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2FrictionJointDef = b2FrictionJointDef; + + function b2GearJoint() { + b2GearJoint.b2GearJoint.apply(this, arguments); + if(this.constructor === b2GearJoint) this.b2GearJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2GearJoint = b2GearJoint; + + function b2GearJointDef() { + b2GearJointDef.b2GearJointDef.apply(this, arguments); + if(this.constructor === b2GearJointDef) this.b2GearJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2GearJointDef = b2GearJointDef; + + function b2Jacobian() { + b2Jacobian.b2Jacobian.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2Jacobian = b2Jacobian; + + function b2Joint() { + b2Joint.b2Joint.apply(this, arguments); + if(this.constructor === b2Joint) this.b2Joint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2Joint = b2Joint; + + function b2JointDef() { + b2JointDef.b2JointDef.apply(this, arguments); + if(this.constructor === b2JointDef) this.b2JointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2JointDef = b2JointDef; + + function b2JointEdge() { + b2JointEdge.b2JointEdge.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2JointEdge = b2JointEdge; + + function b2LineJoint() { + b2LineJoint.b2LineJoint.apply(this, arguments); + if(this.constructor === b2LineJoint) this.b2LineJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2LineJoint = b2LineJoint; + + function b2LineJointDef() { + b2LineJointDef.b2LineJointDef.apply(this, arguments); + if(this.constructor === b2LineJointDef) this.b2LineJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2LineJointDef = b2LineJointDef; + + function b2MouseJoint() { + b2MouseJoint.b2MouseJoint.apply(this, arguments); + if(this.constructor === b2MouseJoint) this.b2MouseJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2MouseJoint = b2MouseJoint; + + function b2MouseJointDef() { + b2MouseJointDef.b2MouseJointDef.apply(this, arguments); + if(this.constructor === b2MouseJointDef) this.b2MouseJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2MouseJointDef = b2MouseJointDef; + + function b2PrismaticJoint() { + b2PrismaticJoint.b2PrismaticJoint.apply(this, arguments); + if(this.constructor === b2PrismaticJoint) this.b2PrismaticJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PrismaticJoint = b2PrismaticJoint; + + function b2PrismaticJointDef() { + b2PrismaticJointDef.b2PrismaticJointDef.apply(this, arguments); + if(this.constructor === b2PrismaticJointDef) this.b2PrismaticJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PrismaticJointDef = b2PrismaticJointDef; + + function b2PulleyJoint() { + b2PulleyJoint.b2PulleyJoint.apply(this, arguments); + if(this.constructor === b2PulleyJoint) this.b2PulleyJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PulleyJoint = b2PulleyJoint; + + function b2PulleyJointDef() { + b2PulleyJointDef.b2PulleyJointDef.apply(this, arguments); + if(this.constructor === b2PulleyJointDef) this.b2PulleyJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PulleyJointDef = b2PulleyJointDef; + + function b2RevoluteJoint() { + b2RevoluteJoint.b2RevoluteJoint.apply(this, arguments); + if(this.constructor === b2RevoluteJoint) this.b2RevoluteJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2RevoluteJoint = b2RevoluteJoint; + + function b2RevoluteJointDef() { + b2RevoluteJointDef.b2RevoluteJointDef.apply(this, arguments); + if(this.constructor === b2RevoluteJointDef) this.b2RevoluteJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2RevoluteJointDef = b2RevoluteJointDef; + + function b2WeldJoint() { + b2WeldJoint.b2WeldJoint.apply(this, arguments); + if(this.constructor === b2WeldJoint) this.b2WeldJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2WeldJoint = b2WeldJoint; + + function b2WeldJointDef() { + b2WeldJointDef.b2WeldJointDef.apply(this, arguments); + if(this.constructor === b2WeldJointDef) this.b2WeldJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2WeldJointDef = b2WeldJointDef; +})(); //definitions +Box2D.postDefs = []; +(function() { + var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase; + + b2AABB.b2AABB = function() { + this.lowerBound = new b2Vec2(); + this.upperBound = new b2Vec2(); + }; + b2AABB.prototype.IsValid = function() { + var dX = this.upperBound.x - this.lowerBound.x; + var dY = this.upperBound.y - this.lowerBound.y; + var valid = dX >= 0.0 && dY >= 0.0; + valid = valid && this.lowerBound.IsValid() && this.upperBound.IsValid(); + return valid; + } + b2AABB.prototype.GetCenter = function() { + return new b2Vec2((this.lowerBound.x + this.upperBound.x) / 2, (this.lowerBound.y + this.upperBound.y) / 2); + } + b2AABB.prototype.GetExtents = function() { + return new b2Vec2((this.upperBound.x - this.lowerBound.x) / 2, (this.upperBound.y - this.lowerBound.y) / 2); + } + b2AABB.prototype.Contains = function(aabb) { + var result = true; + result = result && this.lowerBound.x <= aabb.lowerBound.x; + result = result && this.lowerBound.y <= aabb.lowerBound.y; + result = result && aabb.upperBound.x <= this.upperBound.x; + result = result && aabb.upperBound.y <= this.upperBound.y; + return result; + } + b2AABB.prototype.RayCast = function(output, input) { + var tmin = (-Number.MAX_VALUE); + var tmax = Number.MAX_VALUE; + var pX = input.p1.x; + var pY = input.p1.y; + var dX = input.p2.x - input.p1.x; + var dY = input.p2.y - input.p1.y; + var absDX = Math.abs(dX); + var absDY = Math.abs(dY); + var normal = output.normal; + var inv_d = 0; + var t1 = 0; + var t2 = 0; + var t3 = 0; + var s = 0; { + if(absDX < Number.MIN_VALUE) { + if(pX < this.lowerBound.x || this.upperBound.x < pX) return false; + } else { + inv_d = 1.0 / dX; + t1 = (this.lowerBound.x - pX) * inv_d; + t2 = (this.upperBound.x - pX) * inv_d; + s = (-1.0); + if(t1 > t2) { + t3 = t1; + t1 = t2; + t2 = t3; + s = 1.0; + } + if(t1 > tmin) { + normal.x = s; + normal.y = 0; + tmin = t1; + } + tmax = Math.min(tmax, t2); + if(tmin > tmax) return false; + } + } { + if(absDY < Number.MIN_VALUE) { + if(pY < this.lowerBound.y || this.upperBound.y < pY) return false; + } else { + inv_d = 1.0 / dY; + t1 = (this.lowerBound.y - pY) * inv_d; + t2 = (this.upperBound.y - pY) * inv_d; + s = (-1.0); + if(t1 > t2) { + t3 = t1; + t1 = t2; + t2 = t3; + s = 1.0; + } + if(t1 > tmin) { + normal.y = s; + normal.x = 0; + tmin = t1; + } + tmax = Math.min(tmax, t2); + if(tmin > tmax) return false; + } + } + output.fraction = tmin; + return true; + } + b2AABB.prototype.TestOverlap = function(other) { + var d1X = other.lowerBound.x - this.upperBound.x; + var d1Y = other.lowerBound.y - this.upperBound.y; + var d2X = this.lowerBound.x - other.upperBound.x; + var d2Y = this.lowerBound.y - other.upperBound.y; + if(d1X > 0.0 || d1Y > 0.0) return false; + if(d2X > 0.0 || d2Y > 0.0) return false; + return true; + } + b2AABB.Combine = function(aabb1, aabb2) { + var aabb = new b2AABB(); + aabb.Combine(aabb1, aabb2); + return aabb; + } + b2AABB.prototype.Combine = function(aabb1, aabb2) { + this.lowerBound.x = Math.min(aabb1.lowerBound.x, aabb2.lowerBound.x); + this.lowerBound.y = Math.min(aabb1.lowerBound.y, aabb2.lowerBound.y); + this.upperBound.x = Math.max(aabb1.upperBound.x, aabb2.upperBound.x); + this.upperBound.y = Math.max(aabb1.upperBound.y, aabb2.upperBound.y); + } + b2Bound.b2Bound = function() {}; + b2Bound.prototype.IsLower = function() { + return(this.value & 1) == 0; + } + b2Bound.prototype.IsUpper = function() { + return(this.value & 1) == 1; + } + b2Bound.prototype.Swap = function(b) { + var tempValue = this.value; + var tempProxy = this.proxy; + var tempStabbingCount = this.stabbingCount; + this.value = b.value; + this.proxy = b.proxy; + this.stabbingCount = b.stabbingCount; + b.value = tempValue; + b.proxy = tempProxy; + b.stabbingCount = tempStabbingCount; + } + b2BoundValues.b2BoundValues = function() {}; + b2BoundValues.prototype.b2BoundValues = function() { + this.lowerValues = new Vector_a2j_Number(); + this.lowerValues[0] = 0.0; + this.lowerValues[1] = 0.0; + this.upperValues = new Vector_a2j_Number(); + this.upperValues[0] = 0.0; + this.upperValues[1] = 0.0; + } + b2Collision.b2Collision = function() {}; + b2Collision.ClipSegmentToLine = function(vOut, vIn, normal, offset) { + if(offset === undefined) offset = 0; + var cv; + var numOut = 0; + cv = vIn[0]; + var vIn0 = cv.v; + cv = vIn[1]; + var vIn1 = cv.v; + var distance0 = normal.x * vIn0.x + normal.y * vIn0.y - offset; + var distance1 = normal.x * vIn1.x + normal.y * vIn1.y - offset; + if(distance0 <= 0.0) vOut[numOut++].Set(vIn[0]); + if(distance1 <= 0.0) vOut[numOut++].Set(vIn[1]); + if(distance0 * distance1 < 0.0) { + var interp = distance0 / (distance0 - distance1); + cv = vOut[numOut]; + var tVec = cv.v; + tVec.x = vIn0.x + interp * (vIn1.x - vIn0.x); + tVec.y = vIn0.y + interp * (vIn1.y - vIn0.y); + cv = vOut[numOut]; + var cv2; + if(distance0 > 0.0) { + cv2 = vIn[0]; + cv.id = cv2.id; + } else { + cv2 = vIn[1]; + cv.id = cv2.id; + }++numOut; + } + return numOut; + } + b2Collision.EdgeSeparation = function(poly1, xf1, edge1, poly2, xf2) { + if(edge1 === undefined) edge1 = 0; + var count1 = parseInt(poly1.m_vertexCount); + var vertices1 = poly1.m_vertices; + var normals1 = poly1.m_normals; + var count2 = parseInt(poly2.m_vertexCount); + var vertices2 = poly2.m_vertices; + var tMat; + var tVec; + tMat = xf1.R; + tVec = normals1[edge1]; + var normal1WorldX = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var normal1WorldY = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf2.R; + var normal1X = (tMat.col1.x * normal1WorldX + tMat.col1.y * normal1WorldY); + var normal1Y = (tMat.col2.x * normal1WorldX + tMat.col2.y * normal1WorldY); + var index = 0; + var minDot = Number.MAX_VALUE; + for(var i = 0; i < count2; ++i) { + tVec = vertices2[i]; + var dot = tVec.x * normal1X + tVec.y * normal1Y; + if(dot < minDot) { + minDot = dot; + index = i; + } + } + tVec = vertices1[edge1]; + tMat = xf1.R; + var v1X = xf1.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var v1Y = xf1.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = vertices2[index]; + tMat = xf2.R; + var v2X = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var v2Y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + v2X -= v1X; + v2Y -= v1Y; + var separation = v2X * normal1WorldX + v2Y * normal1WorldY; + return separation; + } + b2Collision.FindMaxSeparation = function(edgeIndex, poly1, xf1, poly2, xf2) { + var count1 = parseInt(poly1.m_vertexCount); + var normals1 = poly1.m_normals; + var tVec; + var tMat; + tMat = xf2.R; + tVec = poly2.m_centroid; + var dX = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var dY = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf1.R; + tVec = poly1.m_centroid; + dX -= xf1.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + dY -= xf1.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var dLocal1X = (dX * xf1.R.col1.x + dY * xf1.R.col1.y); + var dLocal1Y = (dX * xf1.R.col2.x + dY * xf1.R.col2.y); + var edge = 0; + var maxDot = (-Number.MAX_VALUE); + for(var i = 0; i < count1; ++i) { + tVec = normals1[i]; + var dot = (tVec.x * dLocal1X + tVec.y * dLocal1Y); + if(dot > maxDot) { + maxDot = dot; + edge = i; + } + } + var s = b2Collision.EdgeSeparation(poly1, xf1, edge, poly2, xf2); + var prevEdge = parseInt(edge - 1 >= 0 ? edge - 1 : count1 - 1); + var sPrev = b2Collision.EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2); + var nextEdge = parseInt(edge + 1 < count1 ? edge + 1 : 0); + var sNext = b2Collision.EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2); + var bestEdge = 0; + var bestSeparation = 0; + var increment = 0; + if(sPrev > s && sPrev > sNext) { + increment = (-1); + bestEdge = prevEdge; + bestSeparation = sPrev; + } else if(sNext > s) { + increment = 1; + bestEdge = nextEdge; + bestSeparation = sNext; + } else { + edgeIndex[0] = edge; + return s; + } + while(true) { + if(increment == (-1)) edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1; + else edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0; + s = b2Collision.EdgeSeparation(poly1, xf1, edge, poly2, xf2); + if(s > bestSeparation) { + bestEdge = edge; + bestSeparation = s; + } else { + break; + } + } + edgeIndex[0] = bestEdge; + return bestSeparation; + } + b2Collision.FindIncidentEdge = function(c, poly1, xf1, edge1, poly2, xf2) { + if(edge1 === undefined) edge1 = 0; + var count1 = parseInt(poly1.m_vertexCount); + var normals1 = poly1.m_normals; + var count2 = parseInt(poly2.m_vertexCount); + var vertices2 = poly2.m_vertices; + var normals2 = poly2.m_normals; + var tMat; + var tVec; + tMat = xf1.R; + tVec = normals1[edge1]; + var normal1X = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var normal1Y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf2.R; + var tX = (tMat.col1.x * normal1X + tMat.col1.y * normal1Y); + normal1Y = (tMat.col2.x * normal1X + tMat.col2.y * normal1Y); + normal1X = tX; + var index = 0; + var minDot = Number.MAX_VALUE; + for(var i = 0; i < count2; ++i) { + tVec = normals2[i]; + var dot = (normal1X * tVec.x + normal1Y * tVec.y); + if(dot < minDot) { + minDot = dot; + index = i; + } + } + var tClip; + var i1 = parseInt(index); + var i2 = parseInt(i1 + 1 < count2 ? i1 + 1 : 0); + tClip = c[0]; + tVec = vertices2[i1]; + tMat = xf2.R; + tClip.v.x = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + tClip.v.y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tClip.id.features.referenceEdge = edge1; + tClip.id.features.incidentEdge = i1; + tClip.id.features.incidentVertex = 0; + tClip = c[1]; + tVec = vertices2[i2]; + tMat = xf2.R; + tClip.v.x = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + tClip.v.y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tClip.id.features.referenceEdge = edge1; + tClip.id.features.incidentEdge = i2; + tClip.id.features.incidentVertex = 1; + } + b2Collision.MakeClipPointVector = function() { + var r = new Vector(2); + r[0] = new ClipVertex(); + r[1] = new ClipVertex(); + return r; + } + b2Collision.CollidePolygons = function(manifold, polyA, xfA, polyB, xfB) { + var cv; + manifold.m_pointCount = 0; + var totalRadius = polyA.m_radius + polyB.m_radius; + var edgeA = 0; + b2Collision.s_edgeAO[0] = edgeA; + var separationA = b2Collision.FindMaxSeparation(b2Collision.s_edgeAO, polyA, xfA, polyB, xfB); + edgeA = b2Collision.s_edgeAO[0]; + if(separationA > totalRadius) return; + var edgeB = 0; + b2Collision.s_edgeBO[0] = edgeB; + var separationB = b2Collision.FindMaxSeparation(b2Collision.s_edgeBO, polyB, xfB, polyA, xfA); + edgeB = b2Collision.s_edgeBO[0]; + if(separationB > totalRadius) return; + var poly1; + var poly2; + var xf1; + var xf2; + var edge1 = 0; + var flip = 0; + var k_relativeTol = 0.98; + var k_absoluteTol = 0.001; + var tMat; + if(separationB > k_relativeTol * separationA + k_absoluteTol) { + poly1 = polyB; + poly2 = polyA; + xf1 = xfB; + xf2 = xfA; + edge1 = edgeB; + manifold.m_type = b2Manifold.e_faceB; + flip = 1; + } else { + poly1 = polyA; + poly2 = polyB; + xf1 = xfA; + xf2 = xfB; + edge1 = edgeA; + manifold.m_type = b2Manifold.e_faceA; + flip = 0; + } + var incidentEdge = b2Collision.s_incidentEdge; + b2Collision.FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2); + var count1 = parseInt(poly1.m_vertexCount); + var vertices1 = poly1.m_vertices; + var local_v11 = vertices1[edge1]; + var local_v12; + if(edge1 + 1 < count1) { + local_v12 = vertices1[parseInt(edge1 + 1)]; + } else { + local_v12 = vertices1[0]; + } + var localTangent = b2Collision.s_localTangent; + localTangent.Set(local_v12.x - local_v11.x, local_v12.y - local_v11.y); + localTangent.Normalize(); + var localNormal = b2Collision.s_localNormal; + localNormal.x = localTangent.y; + localNormal.y = (-localTangent.x); + var planePoint = b2Collision.s_planePoint; + planePoint.Set(0.5 * (local_v11.x + local_v12.x), 0.5 * (local_v11.y + local_v12.y)); + var tangent = b2Collision.s_tangent; + tMat = xf1.R; + tangent.x = (tMat.col1.x * localTangent.x + tMat.col2.x * localTangent.y); + tangent.y = (tMat.col1.y * localTangent.x + tMat.col2.y * localTangent.y); + var tangent2 = b2Collision.s_tangent2; + tangent2.x = (-tangent.x); + tangent2.y = (-tangent.y); + var normal = b2Collision.s_normal; + normal.x = tangent.y; + normal.y = (-tangent.x); + var v11 = b2Collision.s_v11; + var v12 = b2Collision.s_v12; + v11.x = xf1.position.x + (tMat.col1.x * local_v11.x + tMat.col2.x * local_v11.y); + v11.y = xf1.position.y + (tMat.col1.y * local_v11.x + tMat.col2.y * local_v11.y); + v12.x = xf1.position.x + (tMat.col1.x * local_v12.x + tMat.col2.x * local_v12.y); + v12.y = xf1.position.y + (tMat.col1.y * local_v12.x + tMat.col2.y * local_v12.y); + var frontOffset = normal.x * v11.x + normal.y * v11.y; + var sideOffset1 = (-tangent.x * v11.x) - tangent.y * v11.y + totalRadius; + var sideOffset2 = tangent.x * v12.x + tangent.y * v12.y + totalRadius; + var clipPoints1 = b2Collision.s_clipPoints1; + var clipPoints2 = b2Collision.s_clipPoints2; + var np = 0; + np = b2Collision.ClipSegmentToLine(clipPoints1, incidentEdge, tangent2, sideOffset1); + if(np < 2) return; + np = b2Collision.ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2); + if(np < 2) return; + manifold.m_localPlaneNormal.SetV(localNormal); + manifold.m_localPoint.SetV(planePoint); + var pointCount = 0; + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; ++i) { + cv = clipPoints2[i]; + var separation = normal.x * cv.v.x + normal.y * cv.v.y - frontOffset; + if(separation <= totalRadius) { + var cp = manifold.m_points[pointCount]; + tMat = xf2.R; + var tX = cv.v.x - xf2.position.x; + var tY = cv.v.y - xf2.position.y; + cp.m_localPoint.x = (tX * tMat.col1.x + tY * tMat.col1.y); + cp.m_localPoint.y = (tX * tMat.col2.x + tY * tMat.col2.y); + cp.m_id.Set(cv.id); + cp.m_id.features.flip = flip; + ++pointCount; + } + } + manifold.m_pointCount = pointCount; + } + b2Collision.CollideCircles = function(manifold, circle1, xf1, circle2, xf2) { + manifold.m_pointCount = 0; + var tMat; + var tVec; + tMat = xf1.R; + tVec = circle1.m_p; + var p1X = xf1.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var p1Y = xf1.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf2.R; + tVec = circle2.m_p; + var p2X = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var p2Y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var distSqr = dX * dX + dY * dY; + var radius = circle1.m_radius + circle2.m_radius; + if(distSqr > radius * radius) { + return; + } + manifold.m_type = b2Manifold.e_circles; + manifold.m_localPoint.SetV(circle1.m_p); + manifold.m_localPlaneNormal.SetZero(); + manifold.m_pointCount = 1; + manifold.m_points[0].m_localPoint.SetV(circle2.m_p); + manifold.m_points[0].m_id.key = 0; + } + b2Collision.CollidePolygonAndCircle = function(manifold, polygon, xf1, circle, xf2) { + manifold.m_pointCount = 0; + var tPoint; + var dX = 0; + var dY = 0; + var positionX = 0; + var positionY = 0; + var tVec; + var tMat; + tMat = xf2.R; + tVec = circle.m_p; + var cX = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var cY = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + dX = cX - xf1.position.x; + dY = cY - xf1.position.y; + tMat = xf1.R; + var cLocalX = (dX * tMat.col1.x + dY * tMat.col1.y); + var cLocalY = (dX * tMat.col2.x + dY * tMat.col2.y); + var dist = 0; + var normalIndex = 0; + var separation = (-Number.MAX_VALUE); + var radius = polygon.m_radius + circle.m_radius; + var vertexCount = parseInt(polygon.m_vertexCount); + var vertices = polygon.m_vertices; + var normals = polygon.m_normals; + for(var i = 0; i < vertexCount; ++i) { + tVec = vertices[i]; + dX = cLocalX - tVec.x; + dY = cLocalY - tVec.y; + tVec = normals[i]; + var s = tVec.x * dX + tVec.y * dY; + if(s > radius) { + return; + } + if(s > separation) { + separation = s; + normalIndex = i; + } + } + var vertIndex1 = parseInt(normalIndex); + var vertIndex2 = parseInt(vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0); + var v1 = vertices[vertIndex1]; + var v2 = vertices[vertIndex2]; + if(separation < Number.MIN_VALUE) { + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.SetV(normals[normalIndex]); + manifold.m_localPoint.x = 0.5 * (v1.x + v2.x); + manifold.m_localPoint.y = 0.5 * (v1.y + v2.y); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + return; + } + var u1 = (cLocalX - v1.x) * (v2.x - v1.x) + (cLocalY - v1.y) * (v2.y - v1.y); + var u2 = (cLocalX - v2.x) * (v1.x - v2.x) + (cLocalY - v2.y) * (v1.y - v2.y); + if(u1 <= 0.0) { + if((cLocalX - v1.x) * (cLocalX - v1.x) + (cLocalY - v1.y) * (cLocalY - v1.y) > radius * radius) return; + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.x = cLocalX - v1.x; + manifold.m_localPlaneNormal.y = cLocalY - v1.y; + manifold.m_localPlaneNormal.Normalize(); + manifold.m_localPoint.SetV(v1); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + } else if(u2 <= 0) { + if((cLocalX - v2.x) * (cLocalX - v2.x) + (cLocalY - v2.y) * (cLocalY - v2.y) > radius * radius) return; + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.x = cLocalX - v2.x; + manifold.m_localPlaneNormal.y = cLocalY - v2.y; + manifold.m_localPlaneNormal.Normalize(); + manifold.m_localPoint.SetV(v2); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + } else { + var faceCenterX = 0.5 * (v1.x + v2.x); + var faceCenterY = 0.5 * (v1.y + v2.y); + separation = (cLocalX - faceCenterX) * normals[vertIndex1].x + (cLocalY - faceCenterY) * normals[vertIndex1].y; + if(separation > radius) return; + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.x = normals[vertIndex1].x; + manifold.m_localPlaneNormal.y = normals[vertIndex1].y; + manifold.m_localPlaneNormal.Normalize(); + manifold.m_localPoint.Set(faceCenterX, faceCenterY); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + } + } + b2Collision.TestOverlap = function(a, b) { + var t1 = b.lowerBound; + var t2 = a.upperBound; + var d1X = t1.x - t2.x; + var d1Y = t1.y - t2.y; + t1 = a.lowerBound; + t2 = b.upperBound; + var d2X = t1.x - t2.x; + var d2Y = t1.y - t2.y; + if(d1X > 0.0 || d1Y > 0.0) return false; + if(d2X > 0.0 || d2Y > 0.0) return false; + return true; + } + Box2D.postDefs.push(function() { + Box2D.Collision.b2Collision.s_incidentEdge = b2Collision.MakeClipPointVector(); + Box2D.Collision.b2Collision.s_clipPoints1 = b2Collision.MakeClipPointVector(); + Box2D.Collision.b2Collision.s_clipPoints2 = b2Collision.MakeClipPointVector(); + Box2D.Collision.b2Collision.s_edgeAO = new Vector_a2j_Number(1); + Box2D.Collision.b2Collision.s_edgeBO = new Vector_a2j_Number(1); + Box2D.Collision.b2Collision.s_localTangent = new b2Vec2(); + Box2D.Collision.b2Collision.s_localNormal = new b2Vec2(); + Box2D.Collision.b2Collision.s_planePoint = new b2Vec2(); + Box2D.Collision.b2Collision.s_normal = new b2Vec2(); + Box2D.Collision.b2Collision.s_tangent = new b2Vec2(); + Box2D.Collision.b2Collision.s_tangent2 = new b2Vec2(); + Box2D.Collision.b2Collision.s_v11 = new b2Vec2(); + Box2D.Collision.b2Collision.s_v12 = new b2Vec2(); + Box2D.Collision.b2Collision.b2CollidePolyTempVec = new b2Vec2(); + Box2D.Collision.b2Collision.b2_nullFeature = 0x000000ff; + }); + b2ContactID.b2ContactID = function() { + this.features = new Features(); + }; + b2ContactID.prototype.b2ContactID = function() { + this.features._m_id = this; + } + b2ContactID.prototype.Set = function(id) { + this.key = id._key; + } + b2ContactID.prototype.Copy = function() { + var id = new b2ContactID(); + id.key = this.key; + return id; + } + Object.defineProperty(b2ContactID.prototype, 'key', { + enumerable: false, + configurable: true, + get: function() { + return this._key; + } + }); + Object.defineProperty(b2ContactID.prototype, 'key', { + enumerable: false, + configurable: true, + set: function(value) { + if(value === undefined) value = 0; + this._key = value; + this.features._referenceEdge = this._key & 0x000000ff; + this.features._incidentEdge = ((this._key & 0x0000ff00) >> 8) & 0x000000ff; + this.features._incidentVertex = ((this._key & 0x00ff0000) >> 16) & 0x000000ff; + this.features._flip = ((this._key & 0xff000000) >> 24) & 0x000000ff; + } + }); + b2ContactPoint.b2ContactPoint = function() { + this.position = new b2Vec2(); + this.velocity = new b2Vec2(); + this.normal = new b2Vec2(); + this.id = new b2ContactID(); + }; + b2Distance.b2Distance = function() {}; + b2Distance.Distance = function(output, cache, input) { + ++b2Distance.b2_gjkCalls; + var proxyA = input.proxyA; + var proxyB = input.proxyB; + var transformA = input.transformA; + var transformB = input.transformB; + var simplex = b2Distance.s_simplex; + simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB); + var vertices = simplex.m_vertices; + var k_maxIters = 20; + var saveA = b2Distance.s_saveA; + var saveB = b2Distance.s_saveB; + var saveCount = 0; + var closestPoint = simplex.GetClosestPoint(); + var distanceSqr1 = closestPoint.LengthSquared(); + var distanceSqr2 = distanceSqr1; + var i = 0; + var p; + var iter = 0; + while(iter < k_maxIters) { + saveCount = simplex.m_count; + for(i = 0; i < saveCount; i++) { + saveA[i] = vertices[i].indexA; + saveB[i] = vertices[i].indexB; + } + switch(simplex.m_count) { + case 1: + break; + case 2: + simplex.Solve2(); + break; + case 3: + simplex.Solve3(); + break; + default: + b2Settings.b2Assert(false); + } + if(simplex.m_count == 3) { + break; + } + p = simplex.GetClosestPoint(); + distanceSqr2 = p.LengthSquared(); + if(distanceSqr2 > distanceSqr1) {} + distanceSqr1 = distanceSqr2; + var d = simplex.GetSearchDirection(); + if(d.LengthSquared() < Number.MIN_VALUE * Number.MIN_VALUE) { + break; + } + var vertex = vertices[simplex.m_count]; + vertex.indexA = proxyA.GetSupport(b2Math.MulTMV(transformA.R, d.GetNegative())); + vertex.wA = b2Math.MulX(transformA, proxyA.GetVertex(vertex.indexA)); + vertex.indexB = proxyB.GetSupport(b2Math.MulTMV(transformB.R, d)); + vertex.wB = b2Math.MulX(transformB, proxyB.GetVertex(vertex.indexB)); + vertex.w = b2Math.SubtractVV(vertex.wB, vertex.wA); + ++iter; + ++b2Distance.b2_gjkIters; + var duplicate = false; + for(i = 0; i < saveCount; i++) { + if(vertex.indexA == saveA[i] && vertex.indexB == saveB[i]) { + duplicate = true; + break; + } + } + if(duplicate) { + break; + }++simplex.m_count; + } + b2Distance.b2_gjkMaxIters = b2Math.Max(b2Distance.b2_gjkMaxIters, iter); + simplex.GetWitnessPoints(output.pointA, output.pointB); + output.distance = b2Math.SubtractVV(output.pointA, output.pointB).Length(); + output.iterations = iter; + simplex.WriteCache(cache); + if(input.useRadii) { + var rA = proxyA.m_radius; + var rB = proxyB.m_radius; + if(output.distance > rA + rB && output.distance > Number.MIN_VALUE) { + output.distance -= rA + rB; + var normal = b2Math.SubtractVV(output.pointB, output.pointA); + normal.Normalize(); + output.pointA.x += rA * normal.x; + output.pointA.y += rA * normal.y; + output.pointB.x -= rB * normal.x; + output.pointB.y -= rB * normal.y; + } else { + p = new b2Vec2(); + p.x = .5 * (output.pointA.x + output.pointB.x); + p.y = .5 * (output.pointA.y + output.pointB.y); + output.pointA.x = output.pointB.x = p.x; + output.pointA.y = output.pointB.y = p.y; + output.distance = 0.0; + } + } + } + Box2D.postDefs.push(function() { + Box2D.Collision.b2Distance.s_simplex = new b2Simplex(); + Box2D.Collision.b2Distance.s_saveA = new Vector_a2j_Number(3); + Box2D.Collision.b2Distance.s_saveB = new Vector_a2j_Number(3); + }); + b2DistanceInput.b2DistanceInput = function() {}; + b2DistanceOutput.b2DistanceOutput = function() { + this.pointA = new b2Vec2(); + this.pointB = new b2Vec2(); + }; + b2DistanceProxy.b2DistanceProxy = function() {}; + b2DistanceProxy.prototype.Set = function(shape) { + switch(shape.GetType()) { + case b2Shape.e_circleShape: + { + var circle = (shape instanceof b2CircleShape ? shape : null); + this.m_vertices = new Vector(1, true); + this.m_vertices[0] = circle.m_p; + this.m_count = 1; + this.m_radius = circle.m_radius; + } + break; + case b2Shape.e_polygonShape: + { + var polygon = (shape instanceof b2PolygonShape ? shape : null); + this.m_vertices = polygon.m_vertices; + this.m_count = polygon.m_vertexCount; + this.m_radius = polygon.m_radius; + } + break; + default: + b2Settings.b2Assert(false); + } + } + b2DistanceProxy.prototype.GetSupport = function(d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for(var i = 1; i < this.m_count; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if(value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return bestIndex; + } + b2DistanceProxy.prototype.GetSupportVertex = function(d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for(var i = 1; i < this.m_count; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if(value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return this.m_vertices[bestIndex]; + } + b2DistanceProxy.prototype.GetVertexCount = function() { + return this.m_count; + } + b2DistanceProxy.prototype.GetVertex = function(index) { + if(index === undefined) index = 0; + b2Settings.b2Assert(0 <= index && index < this.m_count); + return this.m_vertices[index]; + } + b2DynamicTree.b2DynamicTree = function() {}; + b2DynamicTree.prototype.b2DynamicTree = function() { + this.m_root = null; + this.m_freeList = null; + this.m_path = 0; + this.m_insertionCount = 0; + } + b2DynamicTree.prototype.CreateProxy = function(aabb, userData) { + var node = this.AllocateNode(); + var extendX = b2Settings.b2_aabbExtension; + var extendY = b2Settings.b2_aabbExtension; + node.aabb.lowerBound.x = aabb.lowerBound.x - extendX; + node.aabb.lowerBound.y = aabb.lowerBound.y - extendY; + node.aabb.upperBound.x = aabb.upperBound.x + extendX; + node.aabb.upperBound.y = aabb.upperBound.y + extendY; + node.userData = userData; + this.InsertLeaf(node); + return node; + } + b2DynamicTree.prototype.DestroyProxy = function(proxy) { + this.RemoveLeaf(proxy); + this.FreeNode(proxy); + } + b2DynamicTree.prototype.MoveProxy = function(proxy, aabb, displacement) { + b2Settings.b2Assert(proxy.IsLeaf()); + if(proxy.aabb.Contains(aabb)) { + return false; + } + this.RemoveLeaf(proxy); + var extendX = b2Settings.b2_aabbExtension + b2Settings.b2_aabbMultiplier * (displacement.x > 0 ? displacement.x : (-displacement.x)); + var extendY = b2Settings.b2_aabbExtension + b2Settings.b2_aabbMultiplier * (displacement.y > 0 ? displacement.y : (-displacement.y)); + proxy.aabb.lowerBound.x = aabb.lowerBound.x - extendX; + proxy.aabb.lowerBound.y = aabb.lowerBound.y - extendY; + proxy.aabb.upperBound.x = aabb.upperBound.x + extendX; + proxy.aabb.upperBound.y = aabb.upperBound.y + extendY; + this.InsertLeaf(proxy); + return true; + } + b2DynamicTree.prototype.Rebalance = function(iterations) { + if(iterations === undefined) iterations = 0; + if(this.m_root == null) return; + for(var i = 0; i < iterations; i++) { + var node = this.m_root; + var bit = 0; + while(node.IsLeaf() == false) { + node = (this.m_path >> bit) & 1 ? node.child2 : node.child1; + bit = (bit + 1) & 31; + }++this.m_path; + this.RemoveLeaf(node); + this.InsertLeaf(node); + } + } + b2DynamicTree.prototype.GetFatAABB = function(proxy) { + return proxy.aabb; + } + b2DynamicTree.prototype.GetUserData = function(proxy) { + return proxy.userData; + } + b2DynamicTree.prototype.Query = function(callback, aabb) { + if(this.m_root == null) return; + var stack = new Vector(); + var count = 0; + stack[count++] = this.m_root; + while(count > 0) { + var node = stack[--count]; + if(node.aabb.TestOverlap(aabb)) { + if(node.IsLeaf()) { + var proceed = callback(node); + if(!proceed) return; + } else { + stack[count++] = node.child1; + stack[count++] = node.child2; + } + } + } + } + b2DynamicTree.prototype.RayCast = function(callback, input) { + if(this.m_root == null) return; + var p1 = input.p1; + var p2 = input.p2; + var r = b2Math.SubtractVV(p1, p2); + r.Normalize(); + var v = b2Math.CrossFV(1.0, r); + var abs_v = b2Math.AbsV(v); + var maxFraction = input.maxFraction; + var segmentAABB = new b2AABB(); + var tX = 0; + var tY = 0; { + tX = p1.x + maxFraction * (p2.x - p1.x); + tY = p1.y + maxFraction * (p2.y - p1.y); + segmentAABB.lowerBound.x = Math.min(p1.x, tX); + segmentAABB.lowerBound.y = Math.min(p1.y, tY); + segmentAABB.upperBound.x = Math.max(p1.x, tX); + segmentAABB.upperBound.y = Math.max(p1.y, tY); + } + var stack = new Vector(); + var count = 0; + stack[count++] = this.m_root; + while(count > 0) { + var node = stack[--count]; + if(node.aabb.TestOverlap(segmentAABB) == false) { + continue; + } + var c = node.aabb.GetCenter(); + var h = node.aabb.GetExtents(); + var separation = Math.abs(v.x * (p1.x - c.x) + v.y * (p1.y - c.y)) - abs_v.x * h.x - abs_v.y * h.y; + if(separation > 0.0) continue; + if(node.IsLeaf()) { + var subInput = new b2RayCastInput(); + subInput.p1 = input.p1; + subInput.p2 = input.p2; + subInput.maxFraction = input.maxFraction; + maxFraction = callback(subInput, node); + if(maxFraction == 0.0) return; + if(maxFraction > 0.0) { + tX = p1.x + maxFraction * (p2.x - p1.x); + tY = p1.y + maxFraction * (p2.y - p1.y); + segmentAABB.lowerBound.x = Math.min(p1.x, tX); + segmentAABB.lowerBound.y = Math.min(p1.y, tY); + segmentAABB.upperBound.x = Math.max(p1.x, tX); + segmentAABB.upperBound.y = Math.max(p1.y, tY); + } + } else { + stack[count++] = node.child1; + stack[count++] = node.child2; + } + } + } + b2DynamicTree.prototype.AllocateNode = function() { + if(this.m_freeList) { + var node = this.m_freeList; + this.m_freeList = node.parent; + node.parent = null; + node.child1 = null; + node.child2 = null; + return node; + } + return new b2DynamicTreeNode(); + } + b2DynamicTree.prototype.FreeNode = function(node) { + node.parent = this.m_freeList; + this.m_freeList = node; + } + b2DynamicTree.prototype.InsertLeaf = function(leaf) { + ++this.m_insertionCount; + if(this.m_root == null) { + this.m_root = leaf; + this.m_root.parent = null; + return; + } + var center = leaf.aabb.GetCenter(); + var sibling = this.m_root; + if(sibling.IsLeaf() == false) { + do { + var child1 = sibling.child1; + var child2 = sibling.child2; + var norm1 = Math.abs((child1.aabb.lowerBound.x + child1.aabb.upperBound.x) / 2 - center.x) + Math.abs((child1.aabb.lowerBound.y + child1.aabb.upperBound.y) / 2 - center.y); + var norm2 = Math.abs((child2.aabb.lowerBound.x + child2.aabb.upperBound.x) / 2 - center.x) + Math.abs((child2.aabb.lowerBound.y + child2.aabb.upperBound.y) / 2 - center.y); + if(norm1 < norm2) { + sibling = child1; + } else { + sibling = child2; + } + } + while (sibling.IsLeaf() == false) + } + var node1 = sibling.parent; + var node2 = this.AllocateNode(); + node2.parent = node1; + node2.userData = null; + node2.aabb.Combine(leaf.aabb, sibling.aabb); + if(node1) { + if(sibling.parent.child1 == sibling) { + node1.child1 = node2; + } else { + node1.child2 = node2; + } + node2.child1 = sibling; + node2.child2 = leaf; + sibling.parent = node2; + leaf.parent = node2; + do { + if(node1.aabb.Contains(node2.aabb)) break; + node1.aabb.Combine(node1.child1.aabb, node1.child2.aabb); + node2 = node1; + node1 = node1.parent; + } + while (node1) + } else { + node2.child1 = sibling; + node2.child2 = leaf; + sibling.parent = node2; + leaf.parent = node2; + this.m_root = node2; + } + } + b2DynamicTree.prototype.RemoveLeaf = function(leaf) { + if(leaf == this.m_root) { + this.m_root = null; + return; + } + var node2 = leaf.parent; + var node1 = node2.parent; + var sibling; + if(node2.child1 == leaf) { + sibling = node2.child2; + } else { + sibling = node2.child1; + } + if(node1) { + if(node1.child1 == node2) { + node1.child1 = sibling; + } else { + node1.child2 = sibling; + } + sibling.parent = node1; + this.FreeNode(node2); + while(node1) { + var oldAABB = node1.aabb; + node1.aabb = b2AABB.Combine(node1.child1.aabb, node1.child2.aabb); + if(oldAABB.Contains(node1.aabb)) break; + node1 = node1.parent; + } + } else { + this.m_root = sibling; + sibling.parent = null; + this.FreeNode(node2); + } + } + b2DynamicTreeBroadPhase.b2DynamicTreeBroadPhase = function() { + this.m_tree = new b2DynamicTree(); + this.m_moveBuffer = new Vector(); + this.m_pairBuffer = new Vector(); + this.m_pairCount = 0; + }; + b2DynamicTreeBroadPhase.prototype.CreateProxy = function(aabb, userData) { + var proxy = this.m_tree.CreateProxy(aabb, userData); + ++this.m_proxyCount; + this.BufferMove(proxy); + return proxy; + } + b2DynamicTreeBroadPhase.prototype.DestroyProxy = function(proxy) { + this.UnBufferMove(proxy); + --this.m_proxyCount; + this.m_tree.DestroyProxy(proxy); + } + b2DynamicTreeBroadPhase.prototype.MoveProxy = function(proxy, aabb, displacement) { + var buffer = this.m_tree.MoveProxy(proxy, aabb, displacement); + if(buffer) { + this.BufferMove(proxy); + } + } + b2DynamicTreeBroadPhase.prototype.TestOverlap = function(proxyA, proxyB) { + var aabbA = this.m_tree.GetFatAABB(proxyA); + var aabbB = this.m_tree.GetFatAABB(proxyB); + return aabbA.TestOverlap(aabbB); + } + b2DynamicTreeBroadPhase.prototype.GetUserData = function(proxy) { + return this.m_tree.GetUserData(proxy); + } + b2DynamicTreeBroadPhase.prototype.GetFatAABB = function(proxy) { + return this.m_tree.GetFatAABB(proxy); + } + b2DynamicTreeBroadPhase.prototype.GetProxyCount = function() { + return this.m_proxyCount; + } + b2DynamicTreeBroadPhase.prototype.UpdatePairs = function(callback) { + var __this = this; + __this.m_pairCount = 0; + var i = 0, + queryProxy; + for(i = 0; i < __this.m_moveBuffer.length; ++i) { + queryProxy = __this.m_moveBuffer[i]; + + function QueryCallback(proxy) { + if(proxy == queryProxy) return true; + if(__this.m_pairCount == __this.m_pairBuffer.length) { + __this.m_pairBuffer[__this.m_pairCount] = new b2DynamicTreePair(); + } + var pair = __this.m_pairBuffer[__this.m_pairCount]; + pair.proxyA = proxy < queryProxy ? proxy : queryProxy; + pair.proxyB = proxy >= queryProxy ? proxy : queryProxy; + ++__this.m_pairCount; + return true; + }; + var fatAABB = __this.m_tree.GetFatAABB(queryProxy); + __this.m_tree.Query(QueryCallback, fatAABB); + } + __this.m_moveBuffer.length = 0; + for(var i = 0; i < __this.m_pairCount;) { + var primaryPair = __this.m_pairBuffer[i]; + var userDataA = __this.m_tree.GetUserData(primaryPair.proxyA); + var userDataB = __this.m_tree.GetUserData(primaryPair.proxyB); + callback(userDataA, userDataB); + ++i; + while(i < __this.m_pairCount) { + var pair = __this.m_pairBuffer[i]; + if(pair.proxyA != primaryPair.proxyA || pair.proxyB != primaryPair.proxyB) { + break; + }++i; + } + } + } + b2DynamicTreeBroadPhase.prototype.Query = function(callback, aabb) { + this.m_tree.Query(callback, aabb); + } + b2DynamicTreeBroadPhase.prototype.RayCast = function(callback, input) { + this.m_tree.RayCast(callback, input); + } + b2DynamicTreeBroadPhase.prototype.Validate = function() {} + b2DynamicTreeBroadPhase.prototype.Rebalance = function(iterations) { + if(iterations === undefined) iterations = 0; + this.m_tree.Rebalance(iterations); + } + b2DynamicTreeBroadPhase.prototype.BufferMove = function(proxy) { + this.m_moveBuffer[this.m_moveBuffer.length] = proxy; + } + b2DynamicTreeBroadPhase.prototype.UnBufferMove = function(proxy) { + var i = parseInt(this.m_moveBuffer.indexOf(proxy)); + this.m_moveBuffer.splice(i, 1); + } + b2DynamicTreeBroadPhase.prototype.ComparePairs = function(pair1, pair2) { + return 0; + } + b2DynamicTreeBroadPhase.__implements = {}; + b2DynamicTreeBroadPhase.__implements[IBroadPhase] = true; + b2DynamicTreeNode.b2DynamicTreeNode = function() { + this.aabb = new b2AABB(); + }; + b2DynamicTreeNode.prototype.IsLeaf = function() { + return this.child1 == null; + } + b2DynamicTreePair.b2DynamicTreePair = function() {}; + b2Manifold.b2Manifold = function() { + this.m_pointCount = 0; + }; + b2Manifold.prototype.b2Manifold = function() { + this.m_points = new Vector(b2Settings.b2_maxManifoldPoints); + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.m_points[i] = new b2ManifoldPoint(); + } + this.m_localPlaneNormal = new b2Vec2(); + this.m_localPoint = new b2Vec2(); + } + b2Manifold.prototype.Reset = function() { + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + ((this.m_points[i] instanceof b2ManifoldPoint ? this.m_points[i] : null)).Reset(); + } + this.m_localPlaneNormal.SetZero(); + this.m_localPoint.SetZero(); + this.m_type = 0; + this.m_pointCount = 0; + } + b2Manifold.prototype.Set = function(m) { + this.m_pointCount = m.m_pointCount; + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + ((this.m_points[i] instanceof b2ManifoldPoint ? this.m_points[i] : null)).Set(m.m_points[i]); + } + this.m_localPlaneNormal.SetV(m.m_localPlaneNormal); + this.m_localPoint.SetV(m.m_localPoint); + this.m_type = m.m_type; + } + b2Manifold.prototype.Copy = function() { + var copy = new b2Manifold(); + copy.Set(this); + return copy; + } + Box2D.postDefs.push(function() { + Box2D.Collision.b2Manifold.e_circles = 0x0001; + Box2D.Collision.b2Manifold.e_faceA = 0x0002; + Box2D.Collision.b2Manifold.e_faceB = 0x0004; + }); + b2ManifoldPoint.b2ManifoldPoint = function() { + this.m_localPoint = new b2Vec2(); + this.m_id = new b2ContactID(); + }; + b2ManifoldPoint.prototype.b2ManifoldPoint = function() { + this.Reset(); + } + b2ManifoldPoint.prototype.Reset = function() { + this.m_localPoint.SetZero(); + this.m_normalImpulse = 0.0; + this.m_tangentImpulse = 0.0; + this.m_id.key = 0; + } + b2ManifoldPoint.prototype.Set = function(m) { + this.m_localPoint.SetV(m.m_localPoint); + this.m_normalImpulse = m.m_normalImpulse; + this.m_tangentImpulse = m.m_tangentImpulse; + this.m_id.Set(m.m_id); + } + b2Point.b2Point = function() { + this.p = new b2Vec2(); + }; + b2Point.prototype.Support = function(xf, vX, vY) { + if(vX === undefined) vX = 0; + if(vY === undefined) vY = 0; + return this.p; + } + b2Point.prototype.GetFirstVertex = function(xf) { + return this.p; + } + b2RayCastInput.b2RayCastInput = function() { + this.p1 = new b2Vec2(); + this.p2 = new b2Vec2(); + }; + b2RayCastInput.prototype.b2RayCastInput = function(p1, p2, maxFraction) { + if(p1 === undefined) p1 = null; + if(p2 === undefined) p2 = null; + if(maxFraction === undefined) maxFraction = 1; + if(p1) this.p1.SetV(p1); + if(p2) this.p2.SetV(p2); + this.maxFraction = maxFraction; + } + b2RayCastOutput.b2RayCastOutput = function() { + this.normal = new b2Vec2(); + }; + b2Segment.b2Segment = function() { + this.p1 = new b2Vec2(); + this.p2 = new b2Vec2(); + }; + b2Segment.prototype.TestSegment = function(lambda, normal, segment, maxLambda) { + if(maxLambda === undefined) maxLambda = 0; + var s = segment.p1; + var rX = segment.p2.x - s.x; + var rY = segment.p2.y - s.y; + var dX = this.p2.x - this.p1.x; + var dY = this.p2.y - this.p1.y; + var nX = dY; + var nY = (-dX); + var k_slop = 100.0 * Number.MIN_VALUE; + var denom = (-(rX * nX + rY * nY)); + if(denom > k_slop) { + var bX = s.x - this.p1.x; + var bY = s.y - this.p1.y; + var a = (bX * nX + bY * nY); + if(0.0 <= a && a <= maxLambda * denom) { + var mu2 = (-rX * bY) + rY * bX; + if((-k_slop * denom) <= mu2 && mu2 <= denom * (1.0 + k_slop)) { + a /= denom; + var nLen = Math.sqrt(nX * nX + nY * nY); + nX /= nLen; + nY /= nLen; + lambda[0] = a; + normal.Set(nX, nY); + return true; + } + } + } + return false; + } + b2Segment.prototype.Extend = function(aabb) { + this.ExtendForward(aabb); + this.ExtendBackward(aabb); + } + b2Segment.prototype.ExtendForward = function(aabb) { + var dX = this.p2.x - this.p1.x; + var dY = this.p2.y - this.p1.y; + var lambda = Math.min(dX > 0 ? (aabb.upperBound.x - this.p1.x) / dX : dX < 0 ? (aabb.lowerBound.x - this.p1.x) / dX : Number.POSITIVE_INFINITY, + dY > 0 ? (aabb.upperBound.y - this.p1.y) / dY : dY < 0 ? (aabb.lowerBound.y - this.p1.y) / dY : Number.POSITIVE_INFINITY); + this.p2.x = this.p1.x + dX * lambda; + this.p2.y = this.p1.y + dY * lambda; + } + b2Segment.prototype.ExtendBackward = function(aabb) { + var dX = (-this.p2.x) + this.p1.x; + var dY = (-this.p2.y) + this.p1.y; + var lambda = Math.min(dX > 0 ? (aabb.upperBound.x - this.p2.x) / dX : dX < 0 ? (aabb.lowerBound.x - this.p2.x) / dX : Number.POSITIVE_INFINITY, + dY > 0 ? (aabb.upperBound.y - this.p2.y) / dY : dY < 0 ? (aabb.lowerBound.y - this.p2.y) / dY : Number.POSITIVE_INFINITY); + this.p1.x = this.p2.x + dX * lambda; + this.p1.y = this.p2.y + dY * lambda; + } + b2SeparationFunction.b2SeparationFunction = function() { + this.m_localPoint = new b2Vec2(); + this.m_axis = new b2Vec2(); + }; + b2SeparationFunction.prototype.Initialize = function(cache, proxyA, transformA, proxyB, transformB) { + this.m_proxyA = proxyA; + this.m_proxyB = proxyB; + var count = parseInt(cache.count); + b2Settings.b2Assert(0 < count && count < 3); + var localPointA; + var localPointA1; + var localPointA2; + var localPointB; + var localPointB1; + var localPointB2; + var pointAX = 0; + var pointAY = 0; + var pointBX = 0; + var pointBY = 0; + var normalX = 0; + var normalY = 0; + var tMat; + var tVec; + var s = 0; + var sgn = 0; + if(count == 1) { + this.m_type = b2SeparationFunction.e_points; + localPointA = this.m_proxyA.GetVertex(cache.indexA[0]); + localPointB = this.m_proxyB.GetVertex(cache.indexB[0]); + tVec = localPointA; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointB; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_axis.x = pointBX - pointAX; + this.m_axis.y = pointBY - pointAY; + this.m_axis.Normalize(); + } else if(cache.indexB[0] == cache.indexB[1]) { + this.m_type = b2SeparationFunction.e_faceA; + localPointA1 = this.m_proxyA.GetVertex(cache.indexA[0]); + localPointA2 = this.m_proxyA.GetVertex(cache.indexA[1]); + localPointB = this.m_proxyB.GetVertex(cache.indexB[0]); + this.m_localPoint.x = 0.5 * (localPointA1.x + localPointA2.x); + this.m_localPoint.y = 0.5 * (localPointA1.y + localPointA2.y); + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointA2, localPointA1), 1.0); + this.m_axis.Normalize(); + tVec = this.m_axis; + tMat = transformA.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointB; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + s = (pointBX - pointAX) * normalX + (pointBY - pointAY) * normalY; + if(s < 0.0) { + this.m_axis.NegativeSelf(); + } + } else if(cache.indexA[0] == cache.indexA[0]) { + this.m_type = b2SeparationFunction.e_faceB; + localPointB1 = this.m_proxyB.GetVertex(cache.indexB[0]); + localPointB2 = this.m_proxyB.GetVertex(cache.indexB[1]); + localPointA = this.m_proxyA.GetVertex(cache.indexA[0]); + this.m_localPoint.x = 0.5 * (localPointB1.x + localPointB2.x); + this.m_localPoint.y = 0.5 * (localPointB1.y + localPointB2.y); + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointB2, localPointB1), 1.0); + this.m_axis.Normalize(); + tVec = this.m_axis; + tMat = transformB.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointA; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + s = (pointAX - pointBX) * normalX + (pointAY - pointBY) * normalY; + if(s < 0.0) { + this.m_axis.NegativeSelf(); + } + } else { + localPointA1 = this.m_proxyA.GetVertex(cache.indexA[0]); + localPointA2 = this.m_proxyA.GetVertex(cache.indexA[1]); + localPointB1 = this.m_proxyB.GetVertex(cache.indexB[0]); + localPointB2 = this.m_proxyB.GetVertex(cache.indexB[1]); + var pA = b2Math.MulX(transformA, localPointA); + var dA = b2Math.MulMV(transformA.R, b2Math.SubtractVV(localPointA2, localPointA1)); + var pB = b2Math.MulX(transformB, localPointB); + var dB = b2Math.MulMV(transformB.R, b2Math.SubtractVV(localPointB2, localPointB1)); + var a = dA.x * dA.x + dA.y * dA.y; + var e = dB.x * dB.x + dB.y * dB.y; + var r = b2Math.SubtractVV(dB, dA); + var c = dA.x * r.x + dA.y * r.y; + var f = dB.x * r.x + dB.y * r.y; + var b = dA.x * dB.x + dA.y * dB.y; + var denom = a * e - b * b; + s = 0.0; + if(denom != 0.0) { + s = b2Math.Clamp((b * f - c * e) / denom, 0.0, 1.0); + } + var t = (b * s + f) / e; + if(t < 0.0) { + t = 0.0; + s = b2Math.Clamp((b - c) / a, 0.0, 1.0); + } + localPointA = new b2Vec2(); + localPointA.x = localPointA1.x + s * (localPointA2.x - localPointA1.x); + localPointA.y = localPointA1.y + s * (localPointA2.y - localPointA1.y); + localPointB = new b2Vec2(); + localPointB.x = localPointB1.x + s * (localPointB2.x - localPointB1.x); + localPointB.y = localPointB1.y + s * (localPointB2.y - localPointB1.y); + if(s == 0.0 || s == 1.0) { + this.m_type = b2SeparationFunction.e_faceB; + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointB2, localPointB1), 1.0); + this.m_axis.Normalize(); + this.m_localPoint = localPointB; + tVec = this.m_axis; + tMat = transformB.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointA; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + sgn = (pointAX - pointBX) * normalX + (pointAY - pointBY) * normalY; + if(s < 0.0) { + this.m_axis.NegativeSelf(); + } + } else { + this.m_type = b2SeparationFunction.e_faceA; + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointA2, localPointA1), 1.0); + this.m_localPoint = localPointA; + tVec = this.m_axis; + tMat = transformA.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointB; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + sgn = (pointBX - pointAX) * normalX + (pointBY - pointAY) * normalY; + if(s < 0.0) { + this.m_axis.NegativeSelf(); + } + } + } + } + b2SeparationFunction.prototype.Evaluate = function(transformA, transformB) { + var axisA; + var axisB; + var localPointA; + var localPointB; + var pointA; + var pointB; + var seperation = 0; + var normal; + switch(this.m_type) { + case b2SeparationFunction.e_points: + { + axisA = b2Math.MulTMV(transformA.R, this.m_axis); + axisB = b2Math.MulTMV(transformB.R, this.m_axis.GetNegative()); + localPointA = this.m_proxyA.GetSupportVertex(axisA); + localPointB = this.m_proxyB.GetSupportVertex(axisB); + pointA = b2Math.MulX(transformA, localPointA); + pointB = b2Math.MulX(transformB, localPointB); + seperation = (pointB.x - pointA.x) * this.m_axis.x + (pointB.y - pointA.y) * this.m_axis.y; + return seperation; + } + case b2SeparationFunction.e_faceA: + { + normal = b2Math.MulMV(transformA.R, this.m_axis); + pointA = b2Math.MulX(transformA, this.m_localPoint); + axisB = b2Math.MulTMV(transformB.R, normal.GetNegative()); + localPointB = this.m_proxyB.GetSupportVertex(axisB); + pointB = b2Math.MulX(transformB, localPointB); + seperation = (pointB.x - pointA.x) * normal.x + (pointB.y - pointA.y) * normal.y; + return seperation; + } + case b2SeparationFunction.e_faceB: + { + normal = b2Math.MulMV(transformB.R, this.m_axis); + pointB = b2Math.MulX(transformB, this.m_localPoint); + axisA = b2Math.MulTMV(transformA.R, normal.GetNegative()); + localPointA = this.m_proxyA.GetSupportVertex(axisA); + pointA = b2Math.MulX(transformA, localPointA); + seperation = (pointA.x - pointB.x) * normal.x + (pointA.y - pointB.y) * normal.y; + return seperation; + } + default: + b2Settings.b2Assert(false); + return 0.0; + } + } + Box2D.postDefs.push(function() { + Box2D.Collision.b2SeparationFunction.e_points = 0x01; + Box2D.Collision.b2SeparationFunction.e_faceA = 0x02; + Box2D.Collision.b2SeparationFunction.e_faceB = 0x04; + }); + b2Simplex.b2Simplex = function() { + this.m_v1 = new b2SimplexVertex(); + this.m_v2 = new b2SimplexVertex(); + this.m_v3 = new b2SimplexVertex(); + this.m_vertices = new Vector(3); + }; + b2Simplex.prototype.b2Simplex = function() { + this.m_vertices[0] = this.m_v1; + this.m_vertices[1] = this.m_v2; + this.m_vertices[2] = this.m_v3; + } + b2Simplex.prototype.ReadCache = function(cache, proxyA, transformA, proxyB, transformB) { + b2Settings.b2Assert(0 <= cache.count && cache.count <= 3); + var wALocal; + var wBLocal; + this.m_count = cache.count; + var vertices = this.m_vertices; + for(var i = 0; i < this.m_count; i++) { + var v = vertices[i]; + v.indexA = cache.indexA[i]; + v.indexB = cache.indexB[i]; + wALocal = proxyA.GetVertex(v.indexA); + wBLocal = proxyB.GetVertex(v.indexB); + v.wA = b2Math.MulX(transformA, wALocal); + v.wB = b2Math.MulX(transformB, wBLocal); + v.w = b2Math.SubtractVV(v.wB, v.wA); + v.a = 0; + } + if(this.m_count > 1) { + var metric1 = cache.metric; + var metric2 = this.GetMetric(); + if(metric2 < .5 * metric1 || 2.0 * metric1 < metric2 || metric2 < Number.MIN_VALUE) { + this.m_count = 0; + } + } + if(this.m_count == 0) { + v = vertices[0]; + v.indexA = 0; + v.indexB = 0; + wALocal = proxyA.GetVertex(0); + wBLocal = proxyB.GetVertex(0); + v.wA = b2Math.MulX(transformA, wALocal); + v.wB = b2Math.MulX(transformB, wBLocal); + v.w = b2Math.SubtractVV(v.wB, v.wA); + this.m_count = 1; + } + } + b2Simplex.prototype.WriteCache = function(cache) { + cache.metric = this.GetMetric(); + cache.count = Box2D.parseUInt(this.m_count); + var vertices = this.m_vertices; + for(var i = 0; i < this.m_count; i++) { + cache.indexA[i] = Box2D.parseUInt(vertices[i].indexA); + cache.indexB[i] = Box2D.parseUInt(vertices[i].indexB); + } + } + b2Simplex.prototype.GetSearchDirection = function() { + switch(this.m_count) { + case 1: + return this.m_v1.w.GetNegative(); + case 2: + { + var e12 = b2Math.SubtractVV(this.m_v2.w, this.m_v1.w); + var sgn = b2Math.CrossVV(e12, this.m_v1.w.GetNegative()); + if(sgn > 0.0) { + return b2Math.CrossFV(1.0, e12); + } else { + return b2Math.CrossVF(e12, 1.0); + } + } + default: + b2Settings.b2Assert(false); + return new b2Vec2(); + } + } + b2Simplex.prototype.GetClosestPoint = function() { + switch(this.m_count) { + case 0: + b2Settings.b2Assert(false); + return new b2Vec2(); + case 1: + return this.m_v1.w; + case 2: + return new b2Vec2(this.m_v1.a * this.m_v1.w.x + this.m_v2.a * this.m_v2.w.x, this.m_v1.a * this.m_v1.w.y + this.m_v2.a * this.m_v2.w.y); + default: + b2Settings.b2Assert(false); + return new b2Vec2(); + } + } + b2Simplex.prototype.GetWitnessPoints = function(pA, pB) { + switch(this.m_count) { + case 0: + b2Settings.b2Assert(false); + break; + case 1: + pA.SetV(this.m_v1.wA); + pB.SetV(this.m_v1.wB); + break; + case 2: + pA.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x; + pA.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y; + pB.x = this.m_v1.a * this.m_v1.wB.x + this.m_v2.a * this.m_v2.wB.x; + pB.y = this.m_v1.a * this.m_v1.wB.y + this.m_v2.a * this.m_v2.wB.y; + break; + case 3: + pB.x = pA.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x + this.m_v3.a * this.m_v3.wA.x; + pB.y = pA.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y + this.m_v3.a * this.m_v3.wA.y; + break; + default: + b2Settings.b2Assert(false); + break; + } + } + b2Simplex.prototype.GetMetric = function() { + switch(this.m_count) { + case 0: + b2Settings.b2Assert(false); + return 0.0; + case 1: + return 0.0; + case 2: + return b2Math.SubtractVV(this.m_v1.w, this.m_v2.w).Length(); + case 3: + return b2Math.CrossVV(b2Math.SubtractVV(this.m_v2.w, this.m_v1.w), b2Math.SubtractVV(this.m_v3.w, this.m_v1.w)); + default: + b2Settings.b2Assert(false); + return 0.0; + } + } + b2Simplex.prototype.Solve2 = function() { + var w1 = this.m_v1.w; + var w2 = this.m_v2.w; + var e12 = b2Math.SubtractVV(w2, w1); + var d12_2 = (-(w1.x * e12.x + w1.y * e12.y)); + if(d12_2 <= 0.0) { + this.m_v1.a = 1.0; + this.m_count = 1; + return; + } + var d12_1 = (w2.x * e12.x + w2.y * e12.y); + if(d12_1 <= 0.0) { + this.m_v2.a = 1.0; + this.m_count = 1; + this.m_v1.Set(this.m_v2); + return; + } + var inv_d12 = 1.0 / (d12_1 + d12_2); + this.m_v1.a = d12_1 * inv_d12; + this.m_v2.a = d12_2 * inv_d12; + this.m_count = 2; + } + b2Simplex.prototype.Solve3 = function() { + var w1 = this.m_v1.w; + var w2 = this.m_v2.w; + var w3 = this.m_v3.w; + var e12 = b2Math.SubtractVV(w2, w1); + var w1e12 = b2Math.Dot(w1, e12); + var w2e12 = b2Math.Dot(w2, e12); + var d12_1 = w2e12; + var d12_2 = (-w1e12); + var e13 = b2Math.SubtractVV(w3, w1); + var w1e13 = b2Math.Dot(w1, e13); + var w3e13 = b2Math.Dot(w3, e13); + var d13_1 = w3e13; + var d13_2 = (-w1e13); + var e23 = b2Math.SubtractVV(w3, w2); + var w2e23 = b2Math.Dot(w2, e23); + var w3e23 = b2Math.Dot(w3, e23); + var d23_1 = w3e23; + var d23_2 = (-w2e23); + var n123 = b2Math.CrossVV(e12, e13); + var d123_1 = n123 * b2Math.CrossVV(w2, w3); + var d123_2 = n123 * b2Math.CrossVV(w3, w1); + var d123_3 = n123 * b2Math.CrossVV(w1, w2); + if(d12_2 <= 0.0 && d13_2 <= 0.0) { + this.m_v1.a = 1.0; + this.m_count = 1; + return; + } + if(d12_1 > 0.0 && d12_2 > 0.0 && d123_3 <= 0.0) { + var inv_d12 = 1.0 / (d12_1 + d12_2); + this.m_v1.a = d12_1 * inv_d12; + this.m_v2.a = d12_2 * inv_d12; + this.m_count = 2; + return; + } + if(d13_1 > 0.0 && d13_2 > 0.0 && d123_2 <= 0.0) { + var inv_d13 = 1.0 / (d13_1 + d13_2); + this.m_v1.a = d13_1 * inv_d13; + this.m_v3.a = d13_2 * inv_d13; + this.m_count = 2; + this.m_v2.Set(this.m_v3); + return; + } + if(d12_1 <= 0.0 && d23_2 <= 0.0) { + this.m_v2.a = 1.0; + this.m_count = 1; + this.m_v1.Set(this.m_v2); + return; + } + if(d13_1 <= 0.0 && d23_1 <= 0.0) { + this.m_v3.a = 1.0; + this.m_count = 1; + this.m_v1.Set(this.m_v3); + return; + } + if(d23_1 > 0.0 && d23_2 > 0.0 && d123_1 <= 0.0) { + var inv_d23 = 1.0 / (d23_1 + d23_2); + this.m_v2.a = d23_1 * inv_d23; + this.m_v3.a = d23_2 * inv_d23; + this.m_count = 2; + this.m_v1.Set(this.m_v3); + return; + } + var inv_d123 = 1.0 / (d123_1 + d123_2 + d123_3); + this.m_v1.a = d123_1 * inv_d123; + this.m_v2.a = d123_2 * inv_d123; + this.m_v3.a = d123_3 * inv_d123; + this.m_count = 3; + } + b2SimplexCache.b2SimplexCache = function() { + this.indexA = new Vector_a2j_Number(3); + this.indexB = new Vector_a2j_Number(3); + }; + b2SimplexVertex.b2SimplexVertex = function() {}; + b2SimplexVertex.prototype.Set = function(other) { + this.wA.SetV(other.wA); + this.wB.SetV(other.wB); + this.w.SetV(other.w); + this.a = other.a; + this.indexA = other.indexA; + this.indexB = other.indexB; + } + b2TimeOfImpact.b2TimeOfImpact = function() {}; + b2TimeOfImpact.TimeOfImpact = function(input) { + ++b2TimeOfImpact.b2_toiCalls; + var proxyA = input.proxyA; + var proxyB = input.proxyB; + var sweepA = input.sweepA; + var sweepB = input.sweepB; + b2Settings.b2Assert(sweepA.t0 == sweepB.t0); + b2Settings.b2Assert(1.0 - sweepA.t0 > Number.MIN_VALUE); + var radius = proxyA.m_radius + proxyB.m_radius; + var tolerance = input.tolerance; + var alpha = 0.0; + var k_maxIterations = 1000; + var iter = 0; + var target = 0.0; + b2TimeOfImpact.s_cache.count = 0; + b2TimeOfImpact.s_distanceInput.useRadii = false; + for(;;) { + sweepA.GetTransform(b2TimeOfImpact.s_xfA, alpha); + sweepB.GetTransform(b2TimeOfImpact.s_xfB, alpha); + b2TimeOfImpact.s_distanceInput.proxyA = proxyA; + b2TimeOfImpact.s_distanceInput.proxyB = proxyB; + b2TimeOfImpact.s_distanceInput.transformA = b2TimeOfImpact.s_xfA; + b2TimeOfImpact.s_distanceInput.transformB = b2TimeOfImpact.s_xfB; + b2Distance.Distance(b2TimeOfImpact.s_distanceOutput, b2TimeOfImpact.s_cache, b2TimeOfImpact.s_distanceInput); + if(b2TimeOfImpact.s_distanceOutput.distance <= 0.0) { + alpha = 1.0; + break; + } + b2TimeOfImpact.s_fcn.Initialize(b2TimeOfImpact.s_cache, proxyA, b2TimeOfImpact.s_xfA, proxyB, b2TimeOfImpact.s_xfB); + var separation = b2TimeOfImpact.s_fcn.Evaluate(b2TimeOfImpact.s_xfA, b2TimeOfImpact.s_xfB); + if(separation <= 0.0) { + alpha = 1.0; + break; + } + if(iter == 0) { + if(separation > radius) { + target = b2Math.Max(radius - tolerance, 0.75 * radius); + } else { + target = b2Math.Max(separation - tolerance, 0.02 * radius); + } + } + if(separation - target < 0.5 * tolerance) { + if(iter == 0) { + alpha = 1.0; + break; + } + break; + } + var newAlpha = alpha; { + var x1 = alpha; + var x2 = 1.0; + var f1 = separation; + sweepA.GetTransform(b2TimeOfImpact.s_xfA, x2); + sweepB.GetTransform(b2TimeOfImpact.s_xfB, x2); + var f2 = b2TimeOfImpact.s_fcn.Evaluate(b2TimeOfImpact.s_xfA, b2TimeOfImpact.s_xfB); + if(f2 >= target) { + alpha = 1.0; + break; + } + var rootIterCount = 0; + for(;;) { + var x = 0; + if(rootIterCount & 1) { + x = x1 + (target - f1) * (x2 - x1) / (f2 - f1); + } else { + x = 0.5 * (x1 + x2); + } + sweepA.GetTransform(b2TimeOfImpact.s_xfA, x); + sweepB.GetTransform(b2TimeOfImpact.s_xfB, x); + var f = b2TimeOfImpact.s_fcn.Evaluate(b2TimeOfImpact.s_xfA, b2TimeOfImpact.s_xfB); + if(b2Math.Abs(f - target) < 0.025 * tolerance) { + newAlpha = x; + break; + } + if(f > target) { + x1 = x; + f1 = f; + } else { + x2 = x; + f2 = f; + }++rootIterCount; + ++b2TimeOfImpact.b2_toiRootIters; + if(rootIterCount == 50) { + break; + } + } + b2TimeOfImpact.b2_toiMaxRootIters = b2Math.Max(b2TimeOfImpact.b2_toiMaxRootIters, rootIterCount); + } + if(newAlpha < (1.0 + 100.0 * Number.MIN_VALUE) * alpha) { + break; + } + alpha = newAlpha; + iter++; + ++b2TimeOfImpact.b2_toiIters; + if(iter == k_maxIterations) { + break; + } + } + b2TimeOfImpact.b2_toiMaxIters = b2Math.Max(b2TimeOfImpact.b2_toiMaxIters, iter); + return alpha; + } + Box2D.postDefs.push(function() { + Box2D.Collision.b2TimeOfImpact.b2_toiCalls = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiIters = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiMaxIters = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiRootIters = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiMaxRootIters = 0; + Box2D.Collision.b2TimeOfImpact.s_cache = new b2SimplexCache(); + Box2D.Collision.b2TimeOfImpact.s_distanceInput = new b2DistanceInput(); + Box2D.Collision.b2TimeOfImpact.s_xfA = new b2Transform(); + Box2D.Collision.b2TimeOfImpact.s_xfB = new b2Transform(); + Box2D.Collision.b2TimeOfImpact.s_fcn = new b2SeparationFunction(); + Box2D.Collision.b2TimeOfImpact.s_distanceOutput = new b2DistanceOutput(); + }); + b2TOIInput.b2TOIInput = function() { + this.proxyA = new b2DistanceProxy(); + this.proxyB = new b2DistanceProxy(); + this.sweepA = new b2Sweep(); + this.sweepB = new b2Sweep(); + }; + b2WorldManifold.b2WorldManifold = function() { + this.m_normal = new b2Vec2(); + }; + b2WorldManifold.prototype.b2WorldManifold = function() { + this.m_points = new Vector(b2Settings.b2_maxManifoldPoints); + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.m_points[i] = new b2Vec2(); + } + } + b2WorldManifold.prototype.Initialize = function(manifold, xfA, radiusA, xfB, radiusB) { + if(radiusA === undefined) radiusA = 0; + if(radiusB === undefined) radiusB = 0; + if(manifold.m_pointCount == 0) { + return; + } + var i = 0; + var tVec; + var tMat; + var normalX = 0; + var normalY = 0; + var planePointX = 0; + var planePointY = 0; + var clipPointX = 0; + var clipPointY = 0; + switch(manifold.m_type) { + case b2Manifold.e_circles: + { + tMat = xfA.R; + tVec = manifold.m_localPoint; + var pointAX = xfA.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + var pointAY = xfA.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = xfB.R; + tVec = manifold.m_points[0].m_localPoint; + var pointBX = xfB.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + var pointBY = xfB.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + var dX = pointBX - pointAX; + var dY = pointBY - pointAY; + var d2 = dX * dX + dY * dY; + if(d2 > Number.MIN_VALUE * Number.MIN_VALUE) { + var d = Math.sqrt(d2); + this.m_normal.x = dX / d; + this.m_normal.y = dY / d; + } else { + this.m_normal.x = 1; + this.m_normal.y = 0; + } + var cAX = pointAX + radiusA * this.m_normal.x; + var cAY = pointAY + radiusA * this.m_normal.y; + var cBX = pointBX - radiusB * this.m_normal.x; + var cBY = pointBY - radiusB * this.m_normal.y; + this.m_points[0].x = 0.5 * (cAX + cBX); + this.m_points[0].y = 0.5 * (cAY + cBY); + } + break; + case b2Manifold.e_faceA: + { + tMat = xfA.R; + tVec = manifold.m_localPlaneNormal; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = xfA.R; + tVec = manifold.m_localPoint; + planePointX = xfA.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + planePointY = xfA.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_normal.x = normalX; + this.m_normal.y = normalY; + for(i = 0; i < manifold.m_pointCount; i++) { + tMat = xfB.R; + tVec = manifold.m_points[i].m_localPoint; + clipPointX = xfB.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + clipPointY = xfB.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_points[i].x = clipPointX + 0.5 * (radiusA - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusB) * normalX; + this.m_points[i].y = clipPointY + 0.5 * (radiusA - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusB) * normalY; + } + } + break; + case b2Manifold.e_faceB: + { + tMat = xfB.R; + tVec = manifold.m_localPlaneNormal; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = xfB.R; + tVec = manifold.m_localPoint; + planePointX = xfB.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + planePointY = xfB.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_normal.x = (-normalX); + this.m_normal.y = (-normalY); + for(i = 0; i < manifold.m_pointCount; i++) { + tMat = xfA.R; + tVec = manifold.m_points[i].m_localPoint; + clipPointX = xfA.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + clipPointY = xfA.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_points[i].x = clipPointX + 0.5 * (radiusB - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusA) * normalX; + this.m_points[i].y = clipPointY + 0.5 * (radiusB - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusA) * normalY; + } + } + break; + } + } + ClipVertex.ClipVertex = function() { + this.v = new b2Vec2(); + this.id = new b2ContactID(); + }; + ClipVertex.prototype.Set = function(other) { + this.v.SetV(other.v); + this.id.Set(other.id); + } + Features.Features = function() {}; + Object.defineProperty(Features.prototype, 'referenceEdge', { + enumerable: false, + configurable: true, + get: function() { + return this._referenceEdge; + } + }); + Object.defineProperty(Features.prototype, 'referenceEdge', { + enumerable: false, + configurable: true, + set: function(value) { + if(value === undefined) value = 0; + this._referenceEdge = value; + this._m_id._key = (this._m_id._key & 0xffffff00) | (this._referenceEdge & 0x000000ff); + } + }); + Object.defineProperty(Features.prototype, 'incidentEdge', { + enumerable: false, + configurable: true, + get: function() { + return this._incidentEdge; + } + }); + Object.defineProperty(Features.prototype, 'incidentEdge', { + enumerable: false, + configurable: true, + set: function(value) { + if(value === undefined) value = 0; + this._incidentEdge = value; + this._m_id._key = (this._m_id._key & 0xffff00ff) | ((this._incidentEdge << 8) & 0x0000ff00); + } + }); + Object.defineProperty(Features.prototype, 'incidentVertex', { + enumerable: false, + configurable: true, + get: function() { + return this._incidentVertex; + } + }); + Object.defineProperty(Features.prototype, 'incidentVertex', { + enumerable: false, + configurable: true, + set: function(value) { + if(value === undefined) value = 0; + this._incidentVertex = value; + this._m_id._key = (this._m_id._key & 0xff00ffff) | ((this._incidentVertex << 16) & 0x00ff0000); + } + }); + Object.defineProperty(Features.prototype, 'flip', { + enumerable: false, + configurable: true, + get: function() { + return this._flip; + } + }); + Object.defineProperty(Features.prototype, 'flip', { + enumerable: false, + configurable: true, + set: function(value) { + if(value === undefined) value = 0; + this._flip = value; + this._m_id._key = (this._m_id._key & 0x00ffffff) | ((this._flip << 24) & 0xff000000); + } + }); +})(); +(function() { + var b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase; + + Box2D.inherit(b2CircleShape, Box2D.Collision.Shapes.b2Shape); + b2CircleShape.prototype.__super = Box2D.Collision.Shapes.b2Shape.prototype; + b2CircleShape.b2CircleShape = function() { + Box2D.Collision.Shapes.b2Shape.b2Shape.apply(this, arguments); + this.m_p = new b2Vec2(); + }; + b2CircleShape.prototype.Copy = function() { + var s = new b2CircleShape(); + s.Set(this); + return s; + } + b2CircleShape.prototype.Set = function(other) { + this.__super.Set.call(this, other); + if(Box2D.is(other, b2CircleShape)) { + var other2 = (other instanceof b2CircleShape ? other : null); + this.m_p.SetV(other2.m_p); + } + } + b2CircleShape.prototype.TestPoint = function(transform, p) { + var tMat = transform.R; + var dX = transform.position.x + (tMat.col1.x * this.m_p.x + tMat.col2.x * this.m_p.y); + var dY = transform.position.y + (tMat.col1.y * this.m_p.x + tMat.col2.y * this.m_p.y); + dX = p.x - dX; + dY = p.y - dY; + return(dX * dX + dY * dY) <= this.m_radius * this.m_radius; + } + b2CircleShape.prototype.RayCast = function(output, input, transform) { + var tMat = transform.R; + var positionX = transform.position.x + (tMat.col1.x * this.m_p.x + tMat.col2.x * this.m_p.y); + var positionY = transform.position.y + (tMat.col1.y * this.m_p.x + tMat.col2.y * this.m_p.y); + var sX = input.p1.x - positionX; + var sY = input.p1.y - positionY; + var b = (sX * sX + sY * sY) - this.m_radius * this.m_radius; + var rX = input.p2.x - input.p1.x; + var rY = input.p2.y - input.p1.y; + var c = (sX * rX + sY * rY); + var rr = (rX * rX + rY * rY); + var sigma = c * c - rr * b; + if(sigma < 0.0 || rr < Number.MIN_VALUE) { + return false; + } + var a = (-(c + Math.sqrt(sigma))); + if(0.0 <= a && a <= input.maxFraction * rr) { + a /= rr; + output.fraction = a; + output.normal.x = sX + a * rX; + output.normal.y = sY + a * rY; + output.normal.Normalize(); + return true; + } + return false; + } + b2CircleShape.prototype.ComputeAABB = function(aabb, transform) { + var tMat = transform.R; + var pX = transform.position.x + (tMat.col1.x * this.m_p.x + tMat.col2.x * this.m_p.y); + var pY = transform.position.y + (tMat.col1.y * this.m_p.x + tMat.col2.y * this.m_p.y); + aabb.lowerBound.Set(pX - this.m_radius, pY - this.m_radius); + aabb.upperBound.Set(pX + this.m_radius, pY + this.m_radius); + } + b2CircleShape.prototype.ComputeMass = function(massData, density) { + if(density === undefined) density = 0; + massData.mass = density * b2Settings.b2_pi * this.m_radius * this.m_radius; + massData.center.SetV(this.m_p); + massData.I = massData.mass * (0.5 * this.m_radius * this.m_radius + (this.m_p.x * this.m_p.x + this.m_p.y * this.m_p.y)); + } + b2CircleShape.prototype.ComputeSubmergedArea = function(normal, offset, xf, c) { + if(offset === undefined) offset = 0; + var p = b2Math.MulX(xf, this.m_p); + var l = (-(b2Math.Dot(normal, p) - offset)); + if(l < (-this.m_radius) + Number.MIN_VALUE) { + return 0; + } + if(l > this.m_radius) { + c.SetV(p); + return Math.PI * this.m_radius * this.m_radius; + } + var r2 = this.m_radius * this.m_radius; + var l2 = l * l; + var area = r2 * (Math.asin(l / this.m_radius) + Math.PI / 2) + l * Math.sqrt(r2 - l2); + var com = (-2 / 3 * Math.pow(r2 - l2, 1.5) / area); + c.x = p.x + normal.x * com; + c.y = p.y + normal.y * com; + return area; + } + b2CircleShape.prototype.GetLocalPosition = function() { + return this.m_p; + } + b2CircleShape.prototype.SetLocalPosition = function(position) { + this.m_p.SetV(position); + } + b2CircleShape.prototype.GetRadius = function() { + return this.m_radius; + } + b2CircleShape.prototype.SetRadius = function(radius) { + if(radius === undefined) radius = 0; + this.m_radius = radius; + } + b2CircleShape.prototype.b2CircleShape = function(radius) { + if(radius === undefined) radius = 0; + this.__super.b2Shape.call(this); + this.m_type = b2Shape.e_circleShape; + this.m_radius = radius; + } + b2EdgeChainDef.b2EdgeChainDef = function() {}; + b2EdgeChainDef.prototype.b2EdgeChainDef = function() { + this.vertexCount = 0; + this.isALoop = true; + this.vertices = []; + } + Box2D.inherit(b2EdgeShape, Box2D.Collision.Shapes.b2Shape); + b2EdgeShape.prototype.__super = Box2D.Collision.Shapes.b2Shape.prototype; + b2EdgeShape.b2EdgeShape = function() { + Box2D.Collision.Shapes.b2Shape.b2Shape.apply(this, arguments); + this.s_supportVec = new b2Vec2(); + this.m_v1 = new b2Vec2(); + this.m_v2 = new b2Vec2(); + this.m_coreV1 = new b2Vec2(); + this.m_coreV2 = new b2Vec2(); + this.m_normal = new b2Vec2(); + this.m_direction = new b2Vec2(); + this.m_cornerDir1 = new b2Vec2(); + this.m_cornerDir2 = new b2Vec2(); + }; + b2EdgeShape.prototype.TestPoint = function(transform, p) { + return false; + } + b2EdgeShape.prototype.RayCast = function(output, input, transform) { + var tMat; + var rX = input.p2.x - input.p1.x; + var rY = input.p2.y - input.p1.y; + tMat = transform.R; + var v1X = transform.position.x + (tMat.col1.x * this.m_v1.x + tMat.col2.x * this.m_v1.y); + var v1Y = transform.position.y + (tMat.col1.y * this.m_v1.x + tMat.col2.y * this.m_v1.y); + var nX = transform.position.y + (tMat.col1.y * this.m_v2.x + tMat.col2.y * this.m_v2.y) - v1Y; + var nY = (-(transform.position.x + (tMat.col1.x * this.m_v2.x + tMat.col2.x * this.m_v2.y) - v1X)); + var k_slop = 100.0 * Number.MIN_VALUE; + var denom = (-(rX * nX + rY * nY)); + if(denom > k_slop) { + var bX = input.p1.x - v1X; + var bY = input.p1.y - v1Y; + var a = (bX * nX + bY * nY); + if(0.0 <= a && a <= input.maxFraction * denom) { + var mu2 = (-rX * bY) + rY * bX; + if((-k_slop * denom) <= mu2 && mu2 <= denom * (1.0 + k_slop)) { + a /= denom; + output.fraction = a; + var nLen = Math.sqrt(nX * nX + nY * nY); + output.normal.x = nX / nLen; + output.normal.y = nY / nLen; + return true; + } + } + } + return false; + } + b2EdgeShape.prototype.ComputeAABB = function(aabb, transform) { + var tMat = transform.R; + var v1X = transform.position.x + (tMat.col1.x * this.m_v1.x + tMat.col2.x * this.m_v1.y); + var v1Y = transform.position.y + (tMat.col1.y * this.m_v1.x + tMat.col2.y * this.m_v1.y); + var v2X = transform.position.x + (tMat.col1.x * this.m_v2.x + tMat.col2.x * this.m_v2.y); + var v2Y = transform.position.y + (tMat.col1.y * this.m_v2.x + tMat.col2.y * this.m_v2.y); + if(v1X < v2X) { + aabb.lowerBound.x = v1X; + aabb.upperBound.x = v2X; + } else { + aabb.lowerBound.x = v2X; + aabb.upperBound.x = v1X; + } + if(v1Y < v2Y) { + aabb.lowerBound.y = v1Y; + aabb.upperBound.y = v2Y; + } else { + aabb.lowerBound.y = v2Y; + aabb.upperBound.y = v1Y; + } + } + b2EdgeShape.prototype.ComputeMass = function(massData, density) { + if(density === undefined) density = 0; + massData.mass = 0; + massData.center.SetV(this.m_v1); + massData.I = 0; + } + b2EdgeShape.prototype.ComputeSubmergedArea = function(normal, offset, xf, c) { + if(offset === undefined) offset = 0; + var v0 = new b2Vec2(normal.x * offset, normal.y * offset); + var v1 = b2Math.MulX(xf, this.m_v1); + var v2 = b2Math.MulX(xf, this.m_v2); + var d1 = b2Math.Dot(normal, v1) - offset; + var d2 = b2Math.Dot(normal, v2) - offset; + if(d1 > 0) { + if(d2 > 0) { + return 0; + } else { + v1.x = (-d2 / (d1 - d2) * v1.x) + d1 / (d1 - d2) * v2.x; + v1.y = (-d2 / (d1 - d2) * v1.y) + d1 / (d1 - d2) * v2.y; + } + } else { + if(d2 > 0) { + v2.x = (-d2 / (d1 - d2) * v1.x) + d1 / (d1 - d2) * v2.x; + v2.y = (-d2 / (d1 - d2) * v1.y) + d1 / (d1 - d2) * v2.y; + } else {} + } + c.x = (v0.x + v1.x + v2.x) / 3; + c.y = (v0.y + v1.y + v2.y) / 3; + return 0.5 * ((v1.x - v0.x) * (v2.y - v0.y) - (v1.y - v0.y) * (v2.x - v0.x)); + } + b2EdgeShape.prototype.GetLength = function() { + return this.m_length; + } + b2EdgeShape.prototype.GetVertex1 = function() { + return this.m_v1; + } + b2EdgeShape.prototype.GetVertex2 = function() { + return this.m_v2; + } + b2EdgeShape.prototype.GetCoreVertex1 = function() { + return this.m_coreV1; + } + b2EdgeShape.prototype.GetCoreVertex2 = function() { + return this.m_coreV2; + } + b2EdgeShape.prototype.GetNormalVector = function() { + return this.m_normal; + } + b2EdgeShape.prototype.GetDirectionVector = function() { + return this.m_direction; + } + b2EdgeShape.prototype.GetCorner1Vector = function() { + return this.m_cornerDir1; + } + b2EdgeShape.prototype.GetCorner2Vector = function() { + return this.m_cornerDir2; + } + b2EdgeShape.prototype.Corner1IsConvex = function() { + return this.m_cornerConvex1; + } + b2EdgeShape.prototype.Corner2IsConvex = function() { + return this.m_cornerConvex2; + } + b2EdgeShape.prototype.GetFirstVertex = function(xf) { + var tMat = xf.R; + return new b2Vec2(xf.position.x + (tMat.col1.x * this.m_coreV1.x + tMat.col2.x * this.m_coreV1.y), xf.position.y + (tMat.col1.y * this.m_coreV1.x + tMat.col2.y * this.m_coreV1.y)); + } + b2EdgeShape.prototype.GetNextEdge = function() { + return this.m_nextEdge; + } + b2EdgeShape.prototype.GetPrevEdge = function() { + return this.m_prevEdge; + } + b2EdgeShape.prototype.Support = function(xf, dX, dY) { + if(dX === undefined) dX = 0; + if(dY === undefined) dY = 0; + var tMat = xf.R; + var v1X = xf.position.x + (tMat.col1.x * this.m_coreV1.x + tMat.col2.x * this.m_coreV1.y); + var v1Y = xf.position.y + (tMat.col1.y * this.m_coreV1.x + tMat.col2.y * this.m_coreV1.y); + var v2X = xf.position.x + (tMat.col1.x * this.m_coreV2.x + tMat.col2.x * this.m_coreV2.y); + var v2Y = xf.position.y + (tMat.col1.y * this.m_coreV2.x + tMat.col2.y * this.m_coreV2.y); + if((v1X * dX + v1Y * dY) > (v2X * dX + v2Y * dY)) { + this.s_supportVec.x = v1X; + this.s_supportVec.y = v1Y; + } else { + this.s_supportVec.x = v2X; + this.s_supportVec.y = v2Y; + } + return this.s_supportVec; + } + b2EdgeShape.prototype.b2EdgeShape = function(v1, v2) { + this.__super.b2Shape.call(this); + this.m_type = b2Shape.e_edgeShape; + this.m_prevEdge = null; + this.m_nextEdge = null; + this.m_v1 = v1; + this.m_v2 = v2; + this.m_direction.Set(this.m_v2.x - this.m_v1.x, this.m_v2.y - this.m_v1.y); + this.m_length = this.m_direction.Normalize(); + this.m_normal.Set(this.m_direction.y, (-this.m_direction.x)); + this.m_coreV1.Set((-b2Settings.b2_toiSlop * (this.m_normal.x - this.m_direction.x)) + this.m_v1.x, (-b2Settings.b2_toiSlop * (this.m_normal.y - this.m_direction.y)) + this.m_v1.y); + this.m_coreV2.Set((-b2Settings.b2_toiSlop * (this.m_normal.x + this.m_direction.x)) + this.m_v2.x, (-b2Settings.b2_toiSlop * (this.m_normal.y + this.m_direction.y)) + this.m_v2.y); + this.m_cornerDir1 = this.m_normal; + this.m_cornerDir2.Set((-this.m_normal.x), (-this.m_normal.y)); + } + b2EdgeShape.prototype.SetPrevEdge = function(edge, core, cornerDir, convex) { + this.m_prevEdge = edge; + this.m_coreV1 = core; + this.m_cornerDir1 = cornerDir; + this.m_cornerConvex1 = convex; + } + b2EdgeShape.prototype.SetNextEdge = function(edge, core, cornerDir, convex) { + this.m_nextEdge = edge; + this.m_coreV2 = core; + this.m_cornerDir2 = cornerDir; + this.m_cornerConvex2 = convex; + } + b2MassData.b2MassData = function() { + this.mass = 0.0; + this.center = new b2Vec2(0, 0); + this.I = 0.0; + }; + Box2D.inherit(b2PolygonShape, Box2D.Collision.Shapes.b2Shape); + b2PolygonShape.prototype.__super = Box2D.Collision.Shapes.b2Shape.prototype; + b2PolygonShape.b2PolygonShape = function() { + Box2D.Collision.Shapes.b2Shape.b2Shape.apply(this, arguments); + }; + b2PolygonShape.prototype.Copy = function() { + var s = new b2PolygonShape(); + s.Set(this); + return s; + } + b2PolygonShape.prototype.Set = function(other) { + this.__super.Set.call(this, other); + if(Box2D.is(other, b2PolygonShape)) { + var other2 = (other instanceof b2PolygonShape ? other : null); + this.m_centroid.SetV(other2.m_centroid); + this.m_vertexCount = other2.m_vertexCount; + this.Reserve(this.m_vertexCount); + for(var i = 0; i < this.m_vertexCount; i++) { + this.m_vertices[i].SetV(other2.m_vertices[i]); + this.m_normals[i].SetV(other2.m_normals[i]); + } + } + } + b2PolygonShape.prototype.SetAsArray = function(vertices, vertexCount) { + if(vertexCount === undefined) vertexCount = 0; + var v = new Vector(); + var i = 0, + tVec; + for(i = 0; i < vertices.length; ++i) { + tVec = vertices[i]; + v.push(tVec); + } + this.SetAsVector(v, vertexCount); + } + b2PolygonShape.AsArray = function(vertices, vertexCount) { + if(vertexCount === undefined) vertexCount = 0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsArray(vertices, vertexCount); + return polygonShape; + } + b2PolygonShape.prototype.SetAsVector = function(vertices, vertexCount) { + if(vertexCount === undefined) vertexCount = 0; + if(vertexCount == 0) vertexCount = vertices.length; + b2Settings.b2Assert(2 <= vertexCount); + this.m_vertexCount = vertexCount; + this.Reserve(vertexCount); + var i = 0; + for(i = 0; i < this.m_vertexCount; i++) { + this.m_vertices[i].SetV(vertices[i]); + } + for(i = 0; i < this.m_vertexCount; ++i) { + var i1 = parseInt(i); + var i2 = parseInt(i + 1 < this.m_vertexCount ? i + 1 : 0); + var edge = b2Math.SubtractVV(this.m_vertices[i2], this.m_vertices[i1]); + b2Settings.b2Assert(edge.LengthSquared() > Number.MIN_VALUE); + this.m_normals[i].SetV(b2Math.CrossVF(edge, 1.0)); + this.m_normals[i].Normalize(); + } + this.m_centroid = b2PolygonShape.ComputeCentroid(this.m_vertices, this.m_vertexCount); + } + b2PolygonShape.AsVector = function(vertices, vertexCount) { + if(vertexCount === undefined) vertexCount = 0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsVector(vertices, vertexCount); + return polygonShape; + } + b2PolygonShape.prototype.SetAsBox = function(hx, hy) { + if(hx === undefined) hx = 0; + if(hy === undefined) hy = 0; + this.m_vertexCount = 4; + this.Reserve(4); + this.m_vertices[0].Set((-hx), (-hy)); + this.m_vertices[1].Set(hx, (-hy)); + this.m_vertices[2].Set(hx, hy); + this.m_vertices[3].Set((-hx), hy); + this.m_normals[0].Set(0.0, (-1.0)); + this.m_normals[1].Set(1.0, 0.0); + this.m_normals[2].Set(0.0, 1.0); + this.m_normals[3].Set((-1.0), 0.0); + this.m_centroid.SetZero(); + } + b2PolygonShape.AsBox = function(hx, hy) { + if(hx === undefined) hx = 0; + if(hy === undefined) hy = 0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsBox(hx, hy); + return polygonShape; + } + b2PolygonShape.prototype.SetAsOrientedBox = function(hx, hy, center, angle) { + if(hx === undefined) hx = 0; + if(hy === undefined) hy = 0; + if(center === undefined) center = null; + if(angle === undefined) angle = 0.0; + this.m_vertexCount = 4; + this.Reserve(4); + this.m_vertices[0].Set((-hx), (-hy)); + this.m_vertices[1].Set(hx, (-hy)); + this.m_vertices[2].Set(hx, hy); + this.m_vertices[3].Set((-hx), hy); + this.m_normals[0].Set(0.0, (-1.0)); + this.m_normals[1].Set(1.0, 0.0); + this.m_normals[2].Set(0.0, 1.0); + this.m_normals[3].Set((-1.0), 0.0); + this.m_centroid = center; + var xf = new b2Transform(); + xf.position = center; + xf.R.Set(angle); + for(var i = 0; i < this.m_vertexCount; ++i) { + this.m_vertices[i] = b2Math.MulX(xf, this.m_vertices[i]); + this.m_normals[i] = b2Math.MulMV(xf.R, this.m_normals[i]); + } + } + b2PolygonShape.AsOrientedBox = function(hx, hy, center, angle) { + if(hx === undefined) hx = 0; + if(hy === undefined) hy = 0; + if(center === undefined) center = null; + if(angle === undefined) angle = 0.0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsOrientedBox(hx, hy, center, angle); + return polygonShape; + } + b2PolygonShape.prototype.SetAsEdge = function(v1, v2) { + this.m_vertexCount = 2; + this.Reserve(2); + this.m_vertices[0].SetV(v1); + this.m_vertices[1].SetV(v2); + this.m_centroid.x = 0.5 * (v1.x + v2.x); + this.m_centroid.y = 0.5 * (v1.y + v2.y); + this.m_normals[0] = b2Math.CrossVF(b2Math.SubtractVV(v2, v1), 1.0); + this.m_normals[0].Normalize(); + this.m_normals[1].x = (-this.m_normals[0].x); + this.m_normals[1].y = (-this.m_normals[0].y); + } + b2PolygonShape.AsEdge = function(v1, v2) { + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsEdge(v1, v2); + return polygonShape; + } + b2PolygonShape.prototype.TestPoint = function(xf, p) { + var tVec; + var tMat = xf.R; + var tX = p.x - xf.position.x; + var tY = p.y - xf.position.y; + var pLocalX = (tX * tMat.col1.x + tY * tMat.col1.y); + var pLocalY = (tX * tMat.col2.x + tY * tMat.col2.y); + for(var i = 0; i < this.m_vertexCount; ++i) { + tVec = this.m_vertices[i]; + tX = pLocalX - tVec.x; + tY = pLocalY - tVec.y; + tVec = this.m_normals[i]; + var dot = (tVec.x * tX + tVec.y * tY); + if(dot > 0.0) { + return false; + } + } + return true; + } + b2PolygonShape.prototype.RayCast = function(output, input, transform) { + var lower = 0.0; + var upper = input.maxFraction; + var tX = 0; + var tY = 0; + var tMat; + var tVec; + tX = input.p1.x - transform.position.x; + tY = input.p1.y - transform.position.y; + tMat = transform.R; + var p1X = (tX * tMat.col1.x + tY * tMat.col1.y); + var p1Y = (tX * tMat.col2.x + tY * tMat.col2.y); + tX = input.p2.x - transform.position.x; + tY = input.p2.y - transform.position.y; + tMat = transform.R; + var p2X = (tX * tMat.col1.x + tY * tMat.col1.y); + var p2Y = (tX * tMat.col2.x + tY * tMat.col2.y); + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var index = parseInt((-1)); + for(var i = 0; i < this.m_vertexCount; ++i) { + tVec = this.m_vertices[i]; + tX = tVec.x - p1X; + tY = tVec.y - p1Y; + tVec = this.m_normals[i]; + var numerator = (tVec.x * tX + tVec.y * tY); + var denominator = (tVec.x * dX + tVec.y * dY); + if(denominator == 0.0) { + if(numerator < 0.0) { + return false; + } + } else { + if(denominator < 0.0 && numerator < lower * denominator) { + lower = numerator / denominator; + index = i; + } else if(denominator > 0.0 && numerator < upper * denominator) { + upper = numerator / denominator; + } + } + if(upper < lower - Number.MIN_VALUE) { + return false; + } + } + if(index >= 0) { + output.fraction = lower; + tMat = transform.R; + tVec = this.m_normals[index]; + output.normal.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + output.normal.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + return true; + } + return false; + } + b2PolygonShape.prototype.ComputeAABB = function(aabb, xf) { + var tMat = xf.R; + var tVec = this.m_vertices[0]; + var lowerX = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var lowerY = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var upperX = lowerX; + var upperY = lowerY; + for(var i = 1; i < this.m_vertexCount; ++i) { + tVec = this.m_vertices[i]; + var vX = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var vY = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + lowerX = lowerX < vX ? lowerX : vX; + lowerY = lowerY < vY ? lowerY : vY; + upperX = upperX > vX ? upperX : vX; + upperY = upperY > vY ? upperY : vY; + } + aabb.lowerBound.x = lowerX - this.m_radius; + aabb.lowerBound.y = lowerY - this.m_radius; + aabb.upperBound.x = upperX + this.m_radius; + aabb.upperBound.y = upperY + this.m_radius; + } + b2PolygonShape.prototype.ComputeMass = function(massData, density) { + if(density === undefined) density = 0; + if(this.m_vertexCount == 2) { + massData.center.x = 0.5 * (this.m_vertices[0].x + this.m_vertices[1].x); + massData.center.y = 0.5 * (this.m_vertices[0].y + this.m_vertices[1].y); + massData.mass = 0.0; + massData.I = 0.0; + return; + } + var centerX = 0.0; + var centerY = 0.0; + var area = 0.0; + var I = 0.0; + var p1X = 0.0; + var p1Y = 0.0; + var k_inv3 = 1.0 / 3.0; + for(var i = 0; i < this.m_vertexCount; ++i) { + var p2 = this.m_vertices[i]; + var p3 = i + 1 < this.m_vertexCount ? this.m_vertices[parseInt(i + 1)] : this.m_vertices[0]; + var e1X = p2.x - p1X; + var e1Y = p2.y - p1Y; + var e2X = p3.x - p1X; + var e2Y = p3.y - p1Y; + var D = e1X * e2Y - e1Y * e2X; + var triangleArea = 0.5 * D; + area += triangleArea; + centerX += triangleArea * k_inv3 * (p1X + p2.x + p3.x); + centerY += triangleArea * k_inv3 * (p1Y + p2.y + p3.y); + var px = p1X; + var py = p1Y; + var ex1 = e1X; + var ey1 = e1Y; + var ex2 = e2X; + var ey2 = e2Y; + var intx2 = k_inv3 * (0.25 * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px * ex2)) + 0.5 * px * px; + var inty2 = k_inv3 * (0.25 * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) + 0.5 * py * py; + I += D * (intx2 + inty2); + } + massData.mass = density * area; + centerX *= 1.0 / area; + centerY *= 1.0 / area; + massData.center.Set(centerX, centerY); + massData.I = density * I; + } + b2PolygonShape.prototype.ComputeSubmergedArea = function(normal, offset, xf, c) { + if(offset === undefined) offset = 0; + var normalL = b2Math.MulTMV(xf.R, normal); + var offsetL = offset - b2Math.Dot(normal, xf.position); + var depths = new Vector_a2j_Number(); + var diveCount = 0; + var intoIndex = parseInt((-1)); + var outoIndex = parseInt((-1)); + var lastSubmerged = false; + var i = 0; + for(i = 0; i < this.m_vertexCount; ++i) { + depths[i] = b2Math.Dot(normalL, this.m_vertices[i]) - offsetL; + var isSubmerged = depths[i] < (-Number.MIN_VALUE); + if(i > 0) { + if(isSubmerged) { + if(!lastSubmerged) { + intoIndex = i - 1; + diveCount++; + } + } else { + if(lastSubmerged) { + outoIndex = i - 1; + diveCount++; + } + } + } + lastSubmerged = isSubmerged; + } + switch(diveCount) { + case 0: + if(lastSubmerged) { + var md = new b2MassData(); + this.ComputeMass(md, 1); + c.SetV(b2Math.MulX(xf, md.center)); + return md.mass; + } else { + return 0; + } + break; + case 1: + if(intoIndex == (-1)) { + intoIndex = this.m_vertexCount - 1; + } else { + outoIndex = this.m_vertexCount - 1; + } + break; + } + var intoIndex2 = parseInt((intoIndex + 1) % this.m_vertexCount); + var outoIndex2 = parseInt((outoIndex + 1) % this.m_vertexCount); + var intoLamdda = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]); + var outoLamdda = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]); + var intoVec = new b2Vec2(this.m_vertices[intoIndex].x * (1 - intoLamdda) + this.m_vertices[intoIndex2].x * intoLamdda, this.m_vertices[intoIndex].y * (1 - intoLamdda) + this.m_vertices[ + intoIndex2].y * intoLamdda); + var outoVec = new b2Vec2(this.m_vertices[outoIndex].x * (1 - outoLamdda) + this.m_vertices[outoIndex2].x * outoLamdda, this.m_vertices[outoIndex].y * (1 - outoLamdda) + this.m_vertices[ + outoIndex2].y * outoLamdda); + var area = 0; + var center = new b2Vec2(); + var p2 = this.m_vertices[intoIndex2]; + var p3; + i = intoIndex2; + while(i != outoIndex2) { + i = (i + 1) % this.m_vertexCount; + if(i == outoIndex2) p3 = outoVec; + else p3 = this.m_vertices[i]; + var triangleArea = 0.5 * ((p2.x - intoVec.x) * (p3.y - intoVec.y) - (p2.y - intoVec.y) * (p3.x - intoVec.x)); + area += triangleArea; + center.x += triangleArea * (intoVec.x + p2.x + p3.x) / 3; + center.y += triangleArea * (intoVec.y + p2.y + p3.y) / 3; + p2 = p3; + } + center.Multiply(1 / area); + c.SetV(b2Math.MulX(xf, center)); + return area; + } + b2PolygonShape.prototype.GetVertexCount = function() { + return this.m_vertexCount; + } + b2PolygonShape.prototype.GetVertices = function() { + return this.m_vertices; + } + b2PolygonShape.prototype.GetNormals = function() { + return this.m_normals; + } + b2PolygonShape.prototype.GetSupport = function(d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for(var i = 1; i < this.m_vertexCount; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if(value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return bestIndex; + } + b2PolygonShape.prototype.GetSupportVertex = function(d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for(var i = 1; i < this.m_vertexCount; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if(value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return this.m_vertices[bestIndex]; + } + b2PolygonShape.prototype.Validate = function() { + return false; + } + b2PolygonShape.prototype.b2PolygonShape = function() { + this.__super.b2Shape.call(this); + this.m_type = b2Shape.e_polygonShape; + this.m_centroid = new b2Vec2(); + this.m_vertices = new Vector(); + this.m_normals = new Vector(); + } + b2PolygonShape.prototype.Reserve = function(count) { + if(count === undefined) count = 0; + for(var i = parseInt(this.m_vertices.length); i < count; i++) { + this.m_vertices[i] = new b2Vec2(); + this.m_normals[i] = new b2Vec2(); + } + } + b2PolygonShape.ComputeCentroid = function(vs, count) { + if(count === undefined) count = 0; + var c = new b2Vec2(); + var area = 0.0; + var p1X = 0.0; + var p1Y = 0.0; + var inv3 = 1.0 / 3.0; + for(var i = 0; i < count; ++i) { + var p2 = vs[i]; + var p3 = i + 1 < count ? vs[parseInt(i + 1)] : vs[0]; + var e1X = p2.x - p1X; + var e1Y = p2.y - p1Y; + var e2X = p3.x - p1X; + var e2Y = p3.y - p1Y; + var D = (e1X * e2Y - e1Y * e2X); + var triangleArea = 0.5 * D; + area += triangleArea; + c.x += triangleArea * inv3 * (p1X + p2.x + p3.x); + c.y += triangleArea * inv3 * (p1Y + p2.y + p3.y); + } + c.x *= 1.0 / area; + c.y *= 1.0 / area; + return c; + } + b2PolygonShape.ComputeOBB = function(obb, vs, count) { + if(count === undefined) count = 0; + var i = 0; + var p = new Vector(count + 1); + for(i = 0; i < count; ++i) { + p[i] = vs[i]; + } + p[count] = p[0]; + var minArea = Number.MAX_VALUE; + for(i = 1; i <= count; ++i) { + var root = p[parseInt(i - 1)]; + var uxX = p[i].x - root.x; + var uxY = p[i].y - root.y; + var length = Math.sqrt(uxX * uxX + uxY * uxY); + uxX /= length; + uxY /= length; + var uyX = (-uxY); + var uyY = uxX; + var lowerX = Number.MAX_VALUE; + var lowerY = Number.MAX_VALUE; + var upperX = (-Number.MAX_VALUE); + var upperY = (-Number.MAX_VALUE); + for(var j = 0; j < count; ++j) { + var dX = p[j].x - root.x; + var dY = p[j].y - root.y; + var rX = (uxX * dX + uxY * dY); + var rY = (uyX * dX + uyY * dY); + if(rX < lowerX) lowerX = rX; + if(rY < lowerY) lowerY = rY; + if(rX > upperX) upperX = rX; + if(rY > upperY) upperY = rY; + } + var area = (upperX - lowerX) * (upperY - lowerY); + if(area < 0.95 * minArea) { + minArea = area; + obb.R.col1.x = uxX; + obb.R.col1.y = uxY; + obb.R.col2.x = uyX; + obb.R.col2.y = uyY; + var centerX = 0.5 * (lowerX + upperX); + var centerY = 0.5 * (lowerY + upperY); + var tMat = obb.R; + obb.center.x = root.x + (tMat.col1.x * centerX + tMat.col2.x * centerY); + obb.center.y = root.y + (tMat.col1.y * centerX + tMat.col2.y * centerY); + obb.extents.x = 0.5 * (upperX - lowerX); + obb.extents.y = 0.5 * (upperY - lowerY); + } + } + } + Box2D.postDefs.push(function() { + Box2D.Collision.Shapes.b2PolygonShape.s_mat = new b2Mat22(); + }); + b2Shape.b2Shape = function() {}; + b2Shape.prototype.Copy = function() { + return null; + } + b2Shape.prototype.Set = function(other) { + this.m_radius = other.m_radius; + } + b2Shape.prototype.GetType = function() { + return this.m_type; + } + b2Shape.prototype.TestPoint = function(xf, p) { + return false; + } + b2Shape.prototype.RayCast = function(output, input, transform) { + return false; + } + b2Shape.prototype.ComputeAABB = function(aabb, xf) {} + b2Shape.prototype.ComputeMass = function(massData, density) { + if(density === undefined) density = 0; + } + b2Shape.prototype.ComputeSubmergedArea = function(normal, offset, xf, c) { + if(offset === undefined) offset = 0; + return 0; + } + b2Shape.TestOverlap = function(shape1, transform1, shape2, transform2) { + var input = new b2DistanceInput(); + input.proxyA = new b2DistanceProxy(); + input.proxyA.Set(shape1); + input.proxyB = new b2DistanceProxy(); + input.proxyB.Set(shape2); + input.transformA = transform1; + input.transformB = transform2; + input.useRadii = true; + var simplexCache = new b2SimplexCache(); + simplexCache.count = 0; + var output = new b2DistanceOutput(); + b2Distance.Distance(output, simplexCache, input); + return output.distance < 10.0 * Number.MIN_VALUE; + } + b2Shape.prototype.b2Shape = function() { + this.m_type = b2Shape.e_unknownShape; + this.m_radius = b2Settings.b2_linearSlop; + } + Box2D.postDefs.push(function() { + Box2D.Collision.Shapes.b2Shape.e_unknownShape = parseInt((-1)); + Box2D.Collision.Shapes.b2Shape.e_circleShape = 0; + Box2D.Collision.Shapes.b2Shape.e_polygonShape = 1; + Box2D.Collision.Shapes.b2Shape.e_edgeShape = 2; + Box2D.Collision.Shapes.b2Shape.e_shapeTypeCount = 3; + Box2D.Collision.Shapes.b2Shape.e_hitCollide = 1; + Box2D.Collision.Shapes.b2Shape.e_missCollide = 0; + Box2D.Collision.Shapes.b2Shape.e_startsInsideCollide = parseInt((-1)); + }); +})(); +(function() { + var b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3; + + b2Color.b2Color = function() { + this._r = 0; + this._g = 0; + this._b = 0; + }; + b2Color.prototype.b2Color = function(rr, gg, bb) { + if(rr === undefined) rr = 0; + if(gg === undefined) gg = 0; + if(bb === undefined) bb = 0; + this._r = Box2D.parseUInt(255 * b2Math.Clamp(rr, 0.0, 1.0)); + this._g = Box2D.parseUInt(255 * b2Math.Clamp(gg, 0.0, 1.0)); + this._b = Box2D.parseUInt(255 * b2Math.Clamp(bb, 0.0, 1.0)); + } + b2Color.prototype.Set = function(rr, gg, bb) { + if(rr === undefined) rr = 0; + if(gg === undefined) gg = 0; + if(bb === undefined) bb = 0; + this._r = Box2D.parseUInt(255 * b2Math.Clamp(rr, 0.0, 1.0)); + this._g = Box2D.parseUInt(255 * b2Math.Clamp(gg, 0.0, 1.0)); + this._b = Box2D.parseUInt(255 * b2Math.Clamp(bb, 0.0, 1.0)); + } + Object.defineProperty(b2Color.prototype, 'r', { + enumerable: false, + configurable: true, + set: function(rr) { + if(rr === undefined) rr = 0; + this._r = Box2D.parseUInt(255 * b2Math.Clamp(rr, 0.0, 1.0)); + } + }); + Object.defineProperty(b2Color.prototype, 'g', { + enumerable: false, + configurable: true, + set: function(gg) { + if(gg === undefined) gg = 0; + this._g = Box2D.parseUInt(255 * b2Math.Clamp(gg, 0.0, 1.0)); + } + }); + Object.defineProperty(b2Color.prototype, 'b', { + enumerable: false, + configurable: true, + set: function(bb) { + if(bb === undefined) bb = 0; + this._b = Box2D.parseUInt(255 * b2Math.Clamp(bb, 0.0, 1.0)); + } + }); + Object.defineProperty(b2Color.prototype, 'color', { + enumerable: false, + configurable: true, + get: function() { + return(this._r << 16) | (this._g << 8) | (this._b); + } + }); + b2Settings.b2Settings = function() {}; + b2Settings.b2MixFriction = function(friction1, friction2) { + if(friction1 === undefined) friction1 = 0; + if(friction2 === undefined) friction2 = 0; + return Math.sqrt(friction1 * friction2); + } + b2Settings.b2MixRestitution = function(restitution1, restitution2) { + if(restitution1 === undefined) restitution1 = 0; + if(restitution2 === undefined) restitution2 = 0; + return restitution1 > restitution2 ? restitution1 : restitution2; + } + b2Settings.b2Assert = function(a) { + if(!a) { + throw "Assertion Failed"; + } + } + Box2D.postDefs.push(function() { + Box2D.Common.b2Settings.VERSION = "2.1alpha"; + Box2D.Common.b2Settings.USHRT_MAX = 0x0000ffff; + Box2D.Common.b2Settings.b2_pi = Math.PI; + Box2D.Common.b2Settings.b2_maxManifoldPoints = 2; + Box2D.Common.b2Settings.b2_aabbExtension = 0.1; + Box2D.Common.b2Settings.b2_aabbMultiplier = 2.0; + Box2D.Common.b2Settings.b2_polygonRadius = 2.0 * b2Settings.b2_linearSlop; + Box2D.Common.b2Settings.b2_linearSlop = 0.005; + Box2D.Common.b2Settings.b2_angularSlop = 2.0 / 180.0 * b2Settings.b2_pi; + Box2D.Common.b2Settings.b2_toiSlop = 8.0 * b2Settings.b2_linearSlop; + Box2D.Common.b2Settings.b2_maxTOIContactsPerIsland = 32; + Box2D.Common.b2Settings.b2_maxTOIJointsPerIsland = 32; + Box2D.Common.b2Settings.b2_velocityThreshold = 1.0; + Box2D.Common.b2Settings.b2_maxLinearCorrection = 0.2; + Box2D.Common.b2Settings.b2_maxAngularCorrection = 8.0 / 180.0 * b2Settings.b2_pi; + Box2D.Common.b2Settings.b2_maxTranslation = 2.0; + Box2D.Common.b2Settings.b2_maxTranslationSquared = b2Settings.b2_maxTranslation * b2Settings.b2_maxTranslation; + Box2D.Common.b2Settings.b2_maxRotation = 0.5 * b2Settings.b2_pi; + Box2D.Common.b2Settings.b2_maxRotationSquared = b2Settings.b2_maxRotation * b2Settings.b2_maxRotation; + Box2D.Common.b2Settings.b2_contactBaumgarte = 0.2; + Box2D.Common.b2Settings.b2_timeToSleep = 0.5; + Box2D.Common.b2Settings.b2_linearSleepTolerance = 0.01; + Box2D.Common.b2Settings.b2_angularSleepTolerance = 2.0 / 180.0 * b2Settings.b2_pi; + }); +})(); +(function() { + var b2AABB = Box2D.Collision.b2AABB, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3; + + b2Mat22.b2Mat22 = function() { + this.col1 = new b2Vec2(); + this.col2 = new b2Vec2(); + }; + b2Mat22.prototype.b2Mat22 = function() { + this.SetIdentity(); + } + b2Mat22.FromAngle = function(angle) { + if(angle === undefined) angle = 0; + var mat = new b2Mat22(); + mat.Set(angle); + return mat; + } + b2Mat22.FromVV = function(c1, c2) { + var mat = new b2Mat22(); + mat.SetVV(c1, c2); + return mat; + } + b2Mat22.prototype.Set = function(angle) { + if(angle === undefined) angle = 0; + var c = Math.cos(angle); + var s = Math.sin(angle); + this.col1.x = c; + this.col2.x = (-s); + this.col1.y = s; + this.col2.y = c; + } + b2Mat22.prototype.SetVV = function(c1, c2) { + this.col1.SetV(c1); + this.col2.SetV(c2); + } + b2Mat22.prototype.Copy = function() { + var mat = new b2Mat22(); + mat.SetM(this); + return mat; + } + b2Mat22.prototype.SetM = function(m) { + this.col1.SetV(m.col1); + this.col2.SetV(m.col2); + } + b2Mat22.prototype.AddM = function(m) { + this.col1.x += m.col1.x; + this.col1.y += m.col1.y; + this.col2.x += m.col2.x; + this.col2.y += m.col2.y; + } + b2Mat22.prototype.SetIdentity = function() { + this.col1.x = 1.0; + this.col2.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 1.0; + } + b2Mat22.prototype.SetZero = function() { + this.col1.x = 0.0; + this.col2.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 0.0; + } + b2Mat22.prototype.GetAngle = function() { + return Math.atan2(this.col1.y, this.col1.x); + } + b2Mat22.prototype.GetInverse = function(out) { + var a = this.col1.x; + var b = this.col2.x; + var c = this.col1.y; + var d = this.col2.y; + var det = a * d - b * c; + if(det != 0.0) { + det = 1.0 / det; + } + out.col1.x = det * d; + out.col2.x = (-det * b); + out.col1.y = (-det * c); + out.col2.y = det * a; + return out; + } + b2Mat22.prototype.Solve = function(out, bX, bY) { + if(bX === undefined) bX = 0; + if(bY === undefined) bY = 0; + var a11 = this.col1.x; + var a12 = this.col2.x; + var a21 = this.col1.y; + var a22 = this.col2.y; + var det = a11 * a22 - a12 * a21; + if(det != 0.0) { + det = 1.0 / det; + } + out.x = det * (a22 * bX - a12 * bY); + out.y = det * (a11 * bY - a21 * bX); + return out; + } + b2Mat22.prototype.Abs = function() { + this.col1.Abs(); + this.col2.Abs(); + } + b2Mat33.b2Mat33 = function() { + this.col1 = new b2Vec3(); + this.col2 = new b2Vec3(); + this.col3 = new b2Vec3(); + }; + b2Mat33.prototype.b2Mat33 = function(c1, c2, c3) { + if(c1 === undefined) c1 = null; + if(c2 === undefined) c2 = null; + if(c3 === undefined) c3 = null; + if(!c1 && !c2 && !c3) { + this.col1.SetZero(); + this.col2.SetZero(); + this.col3.SetZero(); + } else { + this.col1.SetV(c1); + this.col2.SetV(c2); + this.col3.SetV(c3); + } + } + b2Mat33.prototype.SetVVV = function(c1, c2, c3) { + this.col1.SetV(c1); + this.col2.SetV(c2); + this.col3.SetV(c3); + } + b2Mat33.prototype.Copy = function() { + return new b2Mat33(this.col1, this.col2, this.col3); + } + b2Mat33.prototype.SetM = function(m) { + this.col1.SetV(m.col1); + this.col2.SetV(m.col2); + this.col3.SetV(m.col3); + } + b2Mat33.prototype.AddM = function(m) { + this.col1.x += m.col1.x; + this.col1.y += m.col1.y; + this.col1.z += m.col1.z; + this.col2.x += m.col2.x; + this.col2.y += m.col2.y; + this.col2.z += m.col2.z; + this.col3.x += m.col3.x; + this.col3.y += m.col3.y; + this.col3.z += m.col3.z; + } + b2Mat33.prototype.SetIdentity = function() { + this.col1.x = 1.0; + this.col2.x = 0.0; + this.col3.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 1.0; + this.col3.y = 0.0; + this.col1.z = 0.0; + this.col2.z = 0.0; + this.col3.z = 1.0; + } + b2Mat33.prototype.SetZero = function() { + this.col1.x = 0.0; + this.col2.x = 0.0; + this.col3.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 0.0; + this.col3.y = 0.0; + this.col1.z = 0.0; + this.col2.z = 0.0; + this.col3.z = 0.0; + } + b2Mat33.prototype.Solve22 = function(out, bX, bY) { + if(bX === undefined) bX = 0; + if(bY === undefined) bY = 0; + var a11 = this.col1.x; + var a12 = this.col2.x; + var a21 = this.col1.y; + var a22 = this.col2.y; + var det = a11 * a22 - a12 * a21; + if(det != 0.0) { + det = 1.0 / det; + } + out.x = det * (a22 * bX - a12 * bY); + out.y = det * (a11 * bY - a21 * bX); + return out; + } + b2Mat33.prototype.Solve33 = function(out, bX, bY, bZ) { + if(bX === undefined) bX = 0; + if(bY === undefined) bY = 0; + if(bZ === undefined) bZ = 0; + var a11 = this.col1.x; + var a21 = this.col1.y; + var a31 = this.col1.z; + var a12 = this.col2.x; + var a22 = this.col2.y; + var a32 = this.col2.z; + var a13 = this.col3.x; + var a23 = this.col3.y; + var a33 = this.col3.z; + var det = a11 * (a22 * a33 - a32 * a23) + a21 * (a32 * a13 - a12 * a33) + a31 * (a12 * a23 - a22 * a13); + if(det != 0.0) { + det = 1.0 / det; + } + out.x = det * (bX * (a22 * a33 - a32 * a23) + bY * (a32 * a13 - a12 * a33) + bZ * (a12 * a23 - a22 * a13)); + out.y = det * (a11 * (bY * a33 - bZ * a23) + a21 * (bZ * a13 - bX * a33) + a31 * (bX * a23 - bY * a13)); + out.z = det * (a11 * (a22 * bZ - a32 * bY) + a21 * (a32 * bX - a12 * bZ) + a31 * (a12 * bY - a22 * bX)); + return out; + } + b2Math.b2Math = function() {}; + b2Math.IsValid = function(x) { + if(x === undefined) x = 0; + return isFinite(x); + } + b2Math.Dot = function(a, b) { + return a.x * b.x + a.y * b.y; + } + b2Math.CrossVV = function(a, b) { + return a.x * b.y - a.y * b.x; + } + b2Math.CrossVF = function(a, s) { + if(s === undefined) s = 0; + var v = new b2Vec2(s * a.y, (-s * a.x)); + return v; + } + b2Math.CrossFV = function(s, a) { + if(s === undefined) s = 0; + var v = new b2Vec2((-s * a.y), s * a.x); + return v; + } + b2Math.MulMV = function(A, v) { + var u = new b2Vec2(A.col1.x * v.x + A.col2.x * v.y, A.col1.y * v.x + A.col2.y * v.y); + return u; + } + b2Math.MulTMV = function(A, v) { + var u = new b2Vec2(b2Math.Dot(v, A.col1), b2Math.Dot(v, A.col2)); + return u; + } + b2Math.MulX = function(T, v) { + var a = b2Math.MulMV(T.R, v); + a.x += T.position.x; + a.y += T.position.y; + return a; + } + b2Math.MulXT = function(T, v) { + var a = b2Math.SubtractVV(v, T.position); + var tX = (a.x * T.R.col1.x + a.y * T.R.col1.y); + a.y = (a.x * T.R.col2.x + a.y * T.R.col2.y); + a.x = tX; + return a; + } + b2Math.AddVV = function(a, b) { + var v = new b2Vec2(a.x + b.x, a.y + b.y); + return v; + } + b2Math.SubtractVV = function(a, b) { + var v = new b2Vec2(a.x - b.x, a.y - b.y); + return v; + } + b2Math.Distance = function(a, b) { + var cX = a.x - b.x; + var cY = a.y - b.y; + return Math.sqrt(cX * cX + cY * cY); + } + b2Math.DistanceSquared = function(a, b) { + var cX = a.x - b.x; + var cY = a.y - b.y; + return(cX * cX + cY * cY); + } + b2Math.MulFV = function(s, a) { + if(s === undefined) s = 0; + var v = new b2Vec2(s * a.x, s * a.y); + return v; + } + b2Math.AddMM = function(A, B) { + var C = b2Mat22.FromVV(b2Math.AddVV(A.col1, B.col1), b2Math.AddVV(A.col2, B.col2)); + return C; + } + b2Math.MulMM = function(A, B) { + var C = b2Mat22.FromVV(b2Math.MulMV(A, B.col1), b2Math.MulMV(A, B.col2)); + return C; + } + b2Math.MulTMM = function(A, B) { + var c1 = new b2Vec2(b2Math.Dot(A.col1, B.col1), b2Math.Dot(A.col2, B.col1)); + var c2 = new b2Vec2(b2Math.Dot(A.col1, B.col2), b2Math.Dot(A.col2, B.col2)); + var C = b2Mat22.FromVV(c1, c2); + return C; + } + b2Math.Abs = function(a) { + if(a === undefined) a = 0; + return a > 0.0 ? a : (-a); + } + b2Math.AbsV = function(a) { + var b = new b2Vec2(b2Math.Abs(a.x), b2Math.Abs(a.y)); + return b; + } + b2Math.AbsM = function(A) { + var B = b2Mat22.FromVV(b2Math.AbsV(A.col1), b2Math.AbsV(A.col2)); + return B; + } + b2Math.Min = function(a, b) { + if(a === undefined) a = 0; + if(b === undefined) b = 0; + return a < b ? a : b; + } + b2Math.MinV = function(a, b) { + var c = new b2Vec2(b2Math.Min(a.x, b.x), b2Math.Min(a.y, b.y)); + return c; + } + b2Math.Max = function(a, b) { + if(a === undefined) a = 0; + if(b === undefined) b = 0; + return a > b ? a : b; + } + b2Math.MaxV = function(a, b) { + var c = new b2Vec2(b2Math.Max(a.x, b.x), b2Math.Max(a.y, b.y)); + return c; + } + b2Math.Clamp = function(a, low, high) { + if(a === undefined) a = 0; + if(low === undefined) low = 0; + if(high === undefined) high = 0; + return a < low ? low : a > high ? high : a; + } + b2Math.ClampV = function(a, low, high) { + return b2Math.MaxV(low, b2Math.MinV(a, high)); + } + b2Math.Swap = function(a, b) { + var tmp = a[0]; + a[0] = b[0]; + b[0] = tmp; + } + b2Math.Random = function() { + return Math.random() * 2 - 1; + } + b2Math.RandomRange = function(lo, hi) { + if(lo === undefined) lo = 0; + if(hi === undefined) hi = 0; + var r = Math.random(); + r = (hi - lo) * r + lo; + return r; + } + b2Math.NextPowerOfTwo = function(x) { + if(x === undefined) x = 0; + x |= (x >> 1) & 0x7FFFFFFF; + x |= (x >> 2) & 0x3FFFFFFF; + x |= (x >> 4) & 0x0FFFFFFF; + x |= (x >> 8) & 0x00FFFFFF; + x |= (x >> 16) & 0x0000FFFF; + return x + 1; + } + b2Math.IsPowerOfTwo = function(x) { + if(x === undefined) x = 0; + var result = x > 0 && (x & (x - 1)) == 0; + return result; + } + Box2D.postDefs.push(function() { + Box2D.Common.Math.b2Math.b2Vec2_zero = new b2Vec2(0.0, 0.0); + Box2D.Common.Math.b2Math.b2Mat22_identity = b2Mat22.FromVV(new b2Vec2(1.0, 0.0), new b2Vec2(0.0, 1.0)); + Box2D.Common.Math.b2Math.b2Transform_identity = new b2Transform(b2Math.b2Vec2_zero, b2Math.b2Mat22_identity); + }); + b2Sweep.b2Sweep = function() { + this.localCenter = new b2Vec2(); + this.c0 = new b2Vec2; + this.c = new b2Vec2(); + }; + b2Sweep.prototype.Set = function(other) { + this.localCenter.SetV(other.localCenter); + this.c0.SetV(other.c0); + this.c.SetV(other.c); + this.a0 = other.a0; + this.a = other.a; + this.t0 = other.t0; + } + b2Sweep.prototype.Copy = function() { + var copy = new b2Sweep(); + copy.localCenter.SetV(this.localCenter); + copy.c0.SetV(this.c0); + copy.c.SetV(this.c); + copy.a0 = this.a0; + copy.a = this.a; + copy.t0 = this.t0; + return copy; + } + b2Sweep.prototype.GetTransform = function(xf, alpha) { + if(alpha === undefined) alpha = 0; + xf.position.x = (1.0 - alpha) * this.c0.x + alpha * this.c.x; + xf.position.y = (1.0 - alpha) * this.c0.y + alpha * this.c.y; + var angle = (1.0 - alpha) * this.a0 + alpha * this.a; + xf.R.Set(angle); + var tMat = xf.R; + xf.position.x -= (tMat.col1.x * this.localCenter.x + tMat.col2.x * this.localCenter.y); + xf.position.y -= (tMat.col1.y * this.localCenter.x + tMat.col2.y * this.localCenter.y); + } + b2Sweep.prototype.Advance = function(t) { + if(t === undefined) t = 0; + if(this.t0 < t && 1.0 - this.t0 > Number.MIN_VALUE) { + var alpha = (t - this.t0) / (1.0 - this.t0); + this.c0.x = (1.0 - alpha) * this.c0.x + alpha * this.c.x; + this.c0.y = (1.0 - alpha) * this.c0.y + alpha * this.c.y; + this.a0 = (1.0 - alpha) * this.a0 + alpha * this.a; + this.t0 = t; + } + } + b2Transform.b2Transform = function() { + this.position = new b2Vec2; + this.R = new b2Mat22(); + }; + b2Transform.prototype.b2Transform = function(pos, r) { + if(pos === undefined) pos = null; + if(r === undefined) r = null; + if(pos) { + this.position.SetV(pos); + this.R.SetM(r); + } + } + b2Transform.prototype.Initialize = function(pos, r) { + this.position.SetV(pos); + this.R.SetM(r); + } + b2Transform.prototype.SetIdentity = function() { + this.position.SetZero(); + this.R.SetIdentity(); + } + b2Transform.prototype.Set = function(x) { + this.position.SetV(x.position); + this.R.SetM(x.R); + } + b2Transform.prototype.GetAngle = function() { + return Math.atan2(this.R.col1.y, this.R.col1.x); + } + b2Vec2.b2Vec2 = function() {}; + b2Vec2.prototype.b2Vec2 = function(x_, y_) { + if(x_ === undefined) x_ = 0; + if(y_ === undefined) y_ = 0; + this.x = x_; + this.y = y_; + } + b2Vec2.prototype.SetZero = function() { + this.x = 0.0; + this.y = 0.0; + } + b2Vec2.prototype.Set = function(x_, y_) { + if(x_ === undefined) x_ = 0; + if(y_ === undefined) y_ = 0; + this.x = x_; + this.y = y_; + } + b2Vec2.prototype.SetV = function(v) { + this.x = v.x; + this.y = v.y; + } + b2Vec2.prototype.GetNegative = function() { + return new b2Vec2((-this.x), (-this.y)); + } + b2Vec2.prototype.NegativeSelf = function() { + this.x = (-this.x); + this.y = (-this.y); + } + b2Vec2.Make = function(x_, y_) { + if(x_ === undefined) x_ = 0; + if(y_ === undefined) y_ = 0; + return new b2Vec2(x_, y_); + } + b2Vec2.prototype.Copy = function() { + return new b2Vec2(this.x, this.y); + } + b2Vec2.prototype.Add = function(v) { + this.x += v.x; + this.y += v.y; + } + b2Vec2.prototype.Subtract = function(v) { + this.x -= v.x; + this.y -= v.y; + } + b2Vec2.prototype.Multiply = function(a) { + if(a === undefined) a = 0; + this.x *= a; + this.y *= a; + } + b2Vec2.prototype.MulM = function(A) { + var tX = this.x; + this.x = A.col1.x * tX + A.col2.x * this.y; + this.y = A.col1.y * tX + A.col2.y * this.y; + } + b2Vec2.prototype.MulTM = function(A) { + var tX = b2Math.Dot(this, A.col1); + this.y = b2Math.Dot(this, A.col2); + this.x = tX; + } + b2Vec2.prototype.CrossVF = function(s) { + if(s === undefined) s = 0; + var tX = this.x; + this.x = s * this.y; + this.y = (-s * tX); + } + b2Vec2.prototype.CrossFV = function(s) { + if(s === undefined) s = 0; + var tX = this.x; + this.x = (-s * this.y); + this.y = s * tX; + } + b2Vec2.prototype.MinV = function(b) { + this.x = this.x < b.x ? this.x : b.x; + this.y = this.y < b.y ? this.y : b.y; + } + b2Vec2.prototype.MaxV = function(b) { + this.x = this.x > b.x ? this.x : b.x; + this.y = this.y > b.y ? this.y : b.y; + } + b2Vec2.prototype.Abs = function() { + if(this.x < 0) this.x = (-this.x); + if(this.y < 0) this.y = (-this.y); + } + b2Vec2.prototype.Length = function() { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + b2Vec2.prototype.LengthSquared = function() { + return(this.x * this.x + this.y * this.y); + } + b2Vec2.prototype.Normalize = function() { + var length = Math.sqrt(this.x * this.x + this.y * this.y); + if(length < Number.MIN_VALUE) { + return 0.0; + } + var invLength = 1.0 / length; + this.x *= invLength; + this.y *= invLength; + return length; + } + b2Vec2.prototype.IsValid = function() { + return b2Math.IsValid(this.x) && b2Math.IsValid(this.y); + } + b2Vec3.b2Vec3 = function() {}; + b2Vec3.prototype.b2Vec3 = function(x, y, z) { + if(x === undefined) x = 0; + if(y === undefined) y = 0; + if(z === undefined) z = 0; + this.x = x; + this.y = y; + this.z = z; + } + b2Vec3.prototype.SetZero = function() { + this.x = this.y = this.z = 0.0; + } + b2Vec3.prototype.Set = function(x, y, z) { + if(x === undefined) x = 0; + if(y === undefined) y = 0; + if(z === undefined) z = 0; + this.x = x; + this.y = y; + this.z = z; + } + b2Vec3.prototype.SetV = function(v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + b2Vec3.prototype.GetNegative = function() { + return new b2Vec3((-this.x), (-this.y), (-this.z)); + } + b2Vec3.prototype.NegativeSelf = function() { + this.x = (-this.x); + this.y = (-this.y); + this.z = (-this.z); + } + b2Vec3.prototype.Copy = function() { + return new b2Vec3(this.x, this.y, this.z); + } + b2Vec3.prototype.Add = function(v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + } + b2Vec3.prototype.Subtract = function(v) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } + b2Vec3.prototype.Multiply = function(a) { + if(a === undefined) a = 0; + this.x *= a; + this.y *= a; + this.z *= a; + } +})(); +(function() { + var b2ControllerEdge = Box2D.Dynamics.Controllers.b2ControllerEdge, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase, + b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2CircleContact = Box2D.Dynamics.Contacts.b2CircleContact, + b2Contact = Box2D.Dynamics.Contacts.b2Contact, + b2ContactConstraint = Box2D.Dynamics.Contacts.b2ContactConstraint, + b2ContactConstraintPoint = Box2D.Dynamics.Contacts.b2ContactConstraintPoint, + b2ContactEdge = Box2D.Dynamics.Contacts.b2ContactEdge, + b2ContactFactory = Box2D.Dynamics.Contacts.b2ContactFactory, + b2ContactRegister = Box2D.Dynamics.Contacts.b2ContactRegister, + b2ContactResult = Box2D.Dynamics.Contacts.b2ContactResult, + b2ContactSolver = Box2D.Dynamics.Contacts.b2ContactSolver, + b2EdgeAndCircleContact = Box2D.Dynamics.Contacts.b2EdgeAndCircleContact, + b2NullContact = Box2D.Dynamics.Contacts.b2NullContact, + b2PolyAndCircleContact = Box2D.Dynamics.Contacts.b2PolyAndCircleContact, + b2PolyAndEdgeContact = Box2D.Dynamics.Contacts.b2PolyAndEdgeContact, + b2PolygonContact = Box2D.Dynamics.Contacts.b2PolygonContact, + b2PositionSolverManifold = Box2D.Dynamics.Contacts.b2PositionSolverManifold, + b2Controller = Box2D.Dynamics.Controllers.b2Controller, + b2DistanceJoint = Box2D.Dynamics.Joints.b2DistanceJoint, + b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef, + b2FrictionJoint = Box2D.Dynamics.Joints.b2FrictionJoint, + b2FrictionJointDef = Box2D.Dynamics.Joints.b2FrictionJointDef, + b2GearJoint = Box2D.Dynamics.Joints.b2GearJoint, + b2GearJointDef = Box2D.Dynamics.Joints.b2GearJointDef, + b2Jacobian = Box2D.Dynamics.Joints.b2Jacobian, + b2Joint = Box2D.Dynamics.Joints.b2Joint, + b2JointDef = Box2D.Dynamics.Joints.b2JointDef, + b2JointEdge = Box2D.Dynamics.Joints.b2JointEdge, + b2LineJoint = Box2D.Dynamics.Joints.b2LineJoint, + b2LineJointDef = Box2D.Dynamics.Joints.b2LineJointDef, + b2MouseJoint = Box2D.Dynamics.Joints.b2MouseJoint, + b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef, + b2PrismaticJoint = Box2D.Dynamics.Joints.b2PrismaticJoint, + b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef, + b2PulleyJoint = Box2D.Dynamics.Joints.b2PulleyJoint, + b2PulleyJointDef = Box2D.Dynamics.Joints.b2PulleyJointDef, + b2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint, + b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef, + b2WeldJoint = Box2D.Dynamics.Joints.b2WeldJoint, + b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef; + + b2Body.b2Body = function() { + this.m_xf = new b2Transform(); + this.m_sweep = new b2Sweep(); + this.m_linearVelocity = new b2Vec2(); + this.m_force = new b2Vec2(); + }; + b2Body.prototype.connectEdges = function(s1, s2, angle1) { + if(angle1 === undefined) angle1 = 0; + var angle2 = Math.atan2(s2.GetDirectionVector().y, s2.GetDirectionVector().x); + var coreOffset = Math.tan((angle2 - angle1) * 0.5); + var core = b2Math.MulFV(coreOffset, s2.GetDirectionVector()); + core = b2Math.SubtractVV(core, s2.GetNormalVector()); + core = b2Math.MulFV(b2Settings.b2_toiSlop, core); + core = b2Math.AddVV(core, s2.GetVertex1()); + var cornerDir = b2Math.AddVV(s1.GetDirectionVector(), s2.GetDirectionVector()); + cornerDir.Normalize(); + var convex = b2Math.Dot(s1.GetDirectionVector(), s2.GetNormalVector()) > 0.0; + s1.SetNextEdge(s2, core, cornerDir, convex); + s2.SetPrevEdge(s1, core, cornerDir, convex); + return angle2; + } + b2Body.prototype.CreateFixture = function(def) { + if(this.m_world.IsLocked() == true) { + return null; + } + var fixture = new b2Fixture(); + fixture.Create(this, this.m_xf, def); + if(this.m_flags & b2Body.e_activeFlag) { + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + fixture.CreateProxy(broadPhase, this.m_xf); + } + fixture.m_next = this.m_fixtureList; + this.m_fixtureList = fixture; + ++this.m_fixtureCount; + fixture.m_body = this; + if(fixture.m_density > 0.0) { + this.ResetMassData(); + } + this.m_world.m_flags |= b2World.e_newFixture; + return fixture; + } + b2Body.prototype.CreateFixture2 = function(shape, density) { + if(density === undefined) density = 0.0; + var def = new b2FixtureDef(); + def.shape = shape; + def.density = density; + return this.CreateFixture(def); + } + b2Body.prototype.DestroyFixture = function(fixture) { + if(this.m_world.IsLocked() == true) { + return; + } + var node = this.m_fixtureList; + var ppF = null; + var found = false; + while(node != null) { + if(node == fixture) { + if(ppF) ppF.m_next = fixture.m_next; + else this.m_fixtureList = fixture.m_next; + found = true; + break; + } + ppF = node; + node = node.m_next; + } + var edge = this.m_contactList; + while(edge) { + var c = edge.contact; + edge = edge.next; + var fixtureA = c.GetFixtureA(); + var fixtureB = c.GetFixtureB(); + if(fixture == fixtureA || fixture == fixtureB) { + this.m_world.m_contactManager.Destroy(c); + } + } + if(this.m_flags & b2Body.e_activeFlag) { + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + fixture.DestroyProxy(broadPhase); + } else {} + fixture.Destroy(); + fixture.m_body = null; + fixture.m_next = null; + --this.m_fixtureCount; + this.ResetMassData(); + } + b2Body.prototype.SetPositionAndAngle = function(position, angle) { + if(angle === undefined) angle = 0; + var f; + if(this.m_world.IsLocked() == true) { + return; + } + this.m_xf.R.Set(angle); + this.m_xf.position.SetV(position); + var tMat = this.m_xf.R; + var tVec = this.m_sweep.localCenter; + this.m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + this.m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_sweep.c.x += this.m_xf.position.x; + this.m_sweep.c.y += this.m_xf.position.y; + this.m_sweep.c0.SetV(this.m_sweep.c); + this.m_sweep.a0 = this.m_sweep.a = angle; + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + for(f = this.m_fixtureList; f; f = f.m_next) { + f.Synchronize(broadPhase, this.m_xf, this.m_xf); + } + this.m_world.m_contactManager.FindNewContacts(); + } + b2Body.prototype.SetTransform = function(xf) { + this.SetPositionAndAngle(xf.position, xf.GetAngle()); + } + b2Body.prototype.GetTransform = function() { + return this.m_xf; + } + b2Body.prototype.GetPosition = function() { + return this.m_xf.position; + } + b2Body.prototype.SetPosition = function(position) { + this.SetPositionAndAngle(position, this.GetAngle()); + } + b2Body.prototype.GetAngle = function() { + return this.m_sweep.a; + } + b2Body.prototype.SetAngle = function(angle) { + if(angle === undefined) angle = 0; + this.SetPositionAndAngle(this.GetPosition(), angle); + } + b2Body.prototype.GetWorldCenter = function() { + return this.m_sweep.c; + } + b2Body.prototype.GetLocalCenter = function() { + return this.m_sweep.localCenter; + } + b2Body.prototype.SetLinearVelocity = function(v) { + if(this.m_type == b2Body.b2_staticBody) { + return; + } + this.m_linearVelocity.SetV(v); + } + b2Body.prototype.GetLinearVelocity = function() { + return this.m_linearVelocity; + } + b2Body.prototype.SetAngularVelocity = function(omega) { + if(omega === undefined) omega = 0; + if(this.m_type == b2Body.b2_staticBody) { + return; + } + this.m_angularVelocity = omega; + } + b2Body.prototype.GetAngularVelocity = function() { + return this.m_angularVelocity; + } + b2Body.prototype.GetDefinition = function() { + var bd = new b2BodyDef(); + bd.type = this.GetType(); + bd.allowSleep = (this.m_flags & b2Body.e_allowSleepFlag) == b2Body.e_allowSleepFlag; + bd.angle = this.GetAngle(); + bd.angularDamping = this.m_angularDamping; + bd.angularVelocity = this.m_angularVelocity; + bd.fixedRotation = (this.m_flags & b2Body.e_fixedRotationFlag) == b2Body.e_fixedRotationFlag; + bd.bullet = (this.m_flags & b2Body.e_bulletFlag) == b2Body.e_bulletFlag; + bd.awake = (this.m_flags & b2Body.e_awakeFlag) == b2Body.e_awakeFlag; + bd.linearDamping = this.m_linearDamping; + bd.linearVelocity.SetV(this.GetLinearVelocity()); + bd.position = this.GetPosition(); + bd.userData = this.GetUserData(); + return bd; + } + b2Body.prototype.ApplyForce = function(force, point) { + if(this.m_type != b2Body.b2_dynamicBody) { + return; + } + if(this.IsAwake() == false) { + this.SetAwake(true); + } + this.m_force.x += force.x; + this.m_force.y += force.y; + this.m_torque += ((point.x - this.m_sweep.c.x) * force.y - (point.y - this.m_sweep.c.y) * force.x); + } + b2Body.prototype.ApplyTorque = function(torque) { + if(torque === undefined) torque = 0; + if(this.m_type != b2Body.b2_dynamicBody) { + return; + } + if(this.IsAwake() == false) { + this.SetAwake(true); + } + this.m_torque += torque; + } + b2Body.prototype.ApplyImpulse = function(impulse, point) { + if(this.m_type != b2Body.b2_dynamicBody) { + return; + } + if(this.IsAwake() == false) { + this.SetAwake(true); + } + this.m_linearVelocity.x += this.m_invMass * impulse.x; + this.m_linearVelocity.y += this.m_invMass * impulse.y; + this.m_angularVelocity += this.m_invI * ((point.x - this.m_sweep.c.x) * impulse.y - (point.y - this.m_sweep.c.y) * impulse.x); + } + b2Body.prototype.Split = function(callback) { + var linearVelocity = this.GetLinearVelocity().Copy(); + var angularVelocity = this.GetAngularVelocity(); + var center = this.GetWorldCenter(); + var body1 = this; + var body2 = this.m_world.CreateBody(this.GetDefinition()); + var prev; + for(var f = body1.m_fixtureList; f;) { + if(callback(f)) { + var next = f.m_next; + if(prev) { + prev.m_next = next; + } else { + body1.m_fixtureList = next; + } + body1.m_fixtureCount--; + f.m_next = body2.m_fixtureList; + body2.m_fixtureList = f; + body2.m_fixtureCount++; + f.m_body = body2; + f = next; + } else { + prev = f; + f = f.m_next; + } + } + body1.ResetMassData(); + body2.ResetMassData(); + var center1 = body1.GetWorldCenter(); + var center2 = body2.GetWorldCenter(); + var velocity1 = b2Math.AddVV(linearVelocity, b2Math.CrossFV(angularVelocity, b2Math.SubtractVV(center1, center))); + var velocity2 = b2Math.AddVV(linearVelocity, b2Math.CrossFV(angularVelocity, b2Math.SubtractVV(center2, center))); + body1.SetLinearVelocity(velocity1); + body2.SetLinearVelocity(velocity2); + body1.SetAngularVelocity(angularVelocity); + body2.SetAngularVelocity(angularVelocity); + body1.SynchronizeFixtures(); + body2.SynchronizeFixtures(); + return body2; + } + b2Body.prototype.Merge = function(other) { + var f; + for(f = other.m_fixtureList; f;) { + var next = f.m_next; + other.m_fixtureCount--; + f.m_next = this.m_fixtureList; + this.m_fixtureList = f; + this.m_fixtureCount++; + f.m_body = body2; + f = next; + } + body1.m_fixtureCount = 0; + var body1 = this; + var body2 = other; + var center1 = body1.GetWorldCenter(); + var center2 = body2.GetWorldCenter(); + var velocity1 = body1.GetLinearVelocity().Copy(); + var velocity2 = body2.GetLinearVelocity().Copy(); + var angular1 = body1.GetAngularVelocity(); + var angular = body2.GetAngularVelocity(); + body1.ResetMassData(); + this.SynchronizeFixtures(); + } + b2Body.prototype.GetMass = function() { + return this.m_mass; + } + b2Body.prototype.GetInertia = function() { + return this.m_I; + } + b2Body.prototype.GetMassData = function(data) { + data.mass = this.m_mass; + data.I = this.m_I; + data.center.SetV(this.m_sweep.localCenter); + } + b2Body.prototype.SetMassData = function(massData) { + b2Settings.b2Assert(this.m_world.IsLocked() == false); + if(this.m_world.IsLocked() == true) { + return; + } + if(this.m_type != b2Body.b2_dynamicBody) { + return; + } + this.m_invMass = 0.0; + this.m_I = 0.0; + this.m_invI = 0.0; + this.m_mass = massData.mass; + if(this.m_mass <= 0.0) { + this.m_mass = 1.0; + } + this.m_invMass = 1.0 / this.m_mass; + if(massData.I > 0.0 && (this.m_flags & b2Body.e_fixedRotationFlag) == 0) { + this.m_I = massData.I - this.m_mass * (massData.center.x * massData.center.x + massData.center.y * massData.center.y); + this.m_invI = 1.0 / this.m_I; + } + var oldCenter = this.m_sweep.c.Copy(); + this.m_sweep.localCenter.SetV(massData.center); + this.m_sweep.c0.SetV(b2Math.MulX(this.m_xf, this.m_sweep.localCenter)); + this.m_sweep.c.SetV(this.m_sweep.c0); + this.m_linearVelocity.x += this.m_angularVelocity * (-(this.m_sweep.c.y - oldCenter.y)); + this.m_linearVelocity.y += this.m_angularVelocity * (+(this.m_sweep.c.x - oldCenter.x)); + } + b2Body.prototype.ResetMassData = function() { + this.m_mass = 0.0; + this.m_invMass = 0.0; + this.m_I = 0.0; + this.m_invI = 0.0; + this.m_sweep.localCenter.SetZero(); + if(this.m_type == b2Body.b2_staticBody || this.m_type == b2Body.b2_kinematicBody) { + return; + } + var center = b2Vec2.Make(0, 0); + for(var f = this.m_fixtureList; f; f = f.m_next) { + if(f.m_density == 0.0) { + continue; + } + var massData = f.GetMassData(); + this.m_mass += massData.mass; + center.x += massData.center.x * massData.mass; + center.y += massData.center.y * massData.mass; + this.m_I += massData.I; + } + if(this.m_mass > 0.0) { + this.m_invMass = 1.0 / this.m_mass; + center.x *= this.m_invMass; + center.y *= this.m_invMass; + } else { + this.m_mass = 1.0; + this.m_invMass = 1.0; + } + if(this.m_I > 0.0 && (this.m_flags & b2Body.e_fixedRotationFlag) == 0) { + this.m_I -= this.m_mass * (center.x * center.x + center.y * center.y); + this.m_I *= this.m_inertiaScale; + b2Settings.b2Assert(this.m_I > 0); + this.m_invI = 1.0 / this.m_I; + } else { + this.m_I = 0.0; + this.m_invI = 0.0; + } + var oldCenter = this.m_sweep.c.Copy(); + this.m_sweep.localCenter.SetV(center); + this.m_sweep.c0.SetV(b2Math.MulX(this.m_xf, this.m_sweep.localCenter)); + this.m_sweep.c.SetV(this.m_sweep.c0); + this.m_linearVelocity.x += this.m_angularVelocity * (-(this.m_sweep.c.y - oldCenter.y)); + this.m_linearVelocity.y += this.m_angularVelocity * (+(this.m_sweep.c.x - oldCenter.x)); + } + b2Body.prototype.GetWorldPoint = function(localPoint) { + var A = this.m_xf.R; + var u = new b2Vec2(A.col1.x * localPoint.x + A.col2.x * localPoint.y, A.col1.y * localPoint.x + A.col2.y * localPoint.y); + u.x += this.m_xf.position.x; + u.y += this.m_xf.position.y; + return u; + } + b2Body.prototype.GetWorldVector = function(localVector) { + return b2Math.MulMV(this.m_xf.R, localVector); + } + b2Body.prototype.GetLocalPoint = function(worldPoint) { + return b2Math.MulXT(this.m_xf, worldPoint); + } + b2Body.prototype.GetLocalVector = function(worldVector) { + return b2Math.MulTMV(this.m_xf.R, worldVector); + } + b2Body.prototype.GetLinearVelocityFromWorldPoint = function(worldPoint) { + return new b2Vec2(this.m_linearVelocity.x - this.m_angularVelocity * (worldPoint.y - this.m_sweep.c.y), this.m_linearVelocity.y + this.m_angularVelocity * (worldPoint.x - this.m_sweep.c.x)); + } + b2Body.prototype.GetLinearVelocityFromLocalPoint = function(localPoint) { + var A = this.m_xf.R; + var worldPoint = new b2Vec2(A.col1.x * localPoint.x + A.col2.x * localPoint.y, A.col1.y * localPoint.x + A.col2.y * localPoint.y); + worldPoint.x += this.m_xf.position.x; + worldPoint.y += this.m_xf.position.y; + return new b2Vec2(this.m_linearVelocity.x - this.m_angularVelocity * (worldPoint.y - this.m_sweep.c.y), this.m_linearVelocity.y + this.m_angularVelocity * (worldPoint.x - this.m_sweep.c.x)); + } + b2Body.prototype.GetLinearDamping = function() { + return this.m_linearDamping; + } + b2Body.prototype.SetLinearDamping = function(linearDamping) { + if(linearDamping === undefined) linearDamping = 0; + this.m_linearDamping = linearDamping; + } + b2Body.prototype.GetAngularDamping = function() { + return this.m_angularDamping; + } + b2Body.prototype.SetAngularDamping = function(angularDamping) { + if(angularDamping === undefined) angularDamping = 0; + this.m_angularDamping = angularDamping; + } + b2Body.prototype.SetType = function(type) { + if(type === undefined) type = 0; + if(this.m_type == type) { + return; + } + this.m_type = type; + this.ResetMassData(); + if(this.m_type == b2Body.b2_staticBody) { + this.m_linearVelocity.SetZero(); + this.m_angularVelocity = 0.0; + } + this.SetAwake(true); + this.m_force.SetZero(); + this.m_torque = 0.0; + for(var ce = this.m_contactList; ce; ce = ce.next) { + ce.contact.FlagForFiltering(); + } + } + b2Body.prototype.GetType = function() { + return this.m_type; + } + b2Body.prototype.SetBullet = function(flag) { + if(flag) { + this.m_flags |= b2Body.e_bulletFlag; + } else { + this.m_flags &= ~b2Body.e_bulletFlag; + } + } + b2Body.prototype.IsBullet = function() { + return(this.m_flags & b2Body.e_bulletFlag) == b2Body.e_bulletFlag; + } + b2Body.prototype.SetSleepingAllowed = function(flag) { + if(flag) { + this.m_flags |= b2Body.e_allowSleepFlag; + } else { + this.m_flags &= ~b2Body.e_allowSleepFlag; + this.SetAwake(true); + } + } + b2Body.prototype.SetAwake = function(flag) { + if(flag) { + this.m_flags |= b2Body.e_awakeFlag; + this.m_sleepTime = 0.0; + } else { + this.m_flags &= ~b2Body.e_awakeFlag; + this.m_sleepTime = 0.0; + this.m_linearVelocity.SetZero(); + this.m_angularVelocity = 0.0; + this.m_force.SetZero(); + this.m_torque = 0.0; + } + } + b2Body.prototype.IsAwake = function() { + return(this.m_flags & b2Body.e_awakeFlag) == b2Body.e_awakeFlag; + } + b2Body.prototype.SetFixedRotation = function(fixed) { + if(fixed) { + this.m_flags |= b2Body.e_fixedRotationFlag; + } else { + this.m_flags &= ~b2Body.e_fixedRotationFlag; + } + this.ResetMassData(); + } + b2Body.prototype.IsFixedRotation = function() { + return(this.m_flags & b2Body.e_fixedRotationFlag) == b2Body.e_fixedRotationFlag; + } + b2Body.prototype.SetActive = function(flag) { + if(flag == this.IsActive()) { + return; + } + var broadPhase; + var f; + if(flag) { + this.m_flags |= b2Body.e_activeFlag; + broadPhase = this.m_world.m_contactManager.m_broadPhase; + for(f = this.m_fixtureList; f; f = f.m_next) { + f.CreateProxy(broadPhase, this.m_xf); + } + } else { + this.m_flags &= ~b2Body.e_activeFlag; + broadPhase = this.m_world.m_contactManager.m_broadPhase; + for(f = this.m_fixtureList; f; f = f.m_next) { + f.DestroyProxy(broadPhase); + } + var ce = this.m_contactList; + while(ce) { + var ce0 = ce; + ce = ce.next; + this.m_world.m_contactManager.Destroy(ce0.contact); + } + this.m_contactList = null; + } + } + b2Body.prototype.IsActive = function() { + return(this.m_flags & b2Body.e_activeFlag) == b2Body.e_activeFlag; + } + b2Body.prototype.IsSleepingAllowed = function() { + return(this.m_flags & b2Body.e_allowSleepFlag) == b2Body.e_allowSleepFlag; + } + b2Body.prototype.GetFixtureList = function() { + return this.m_fixtureList; + } + b2Body.prototype.GetJointList = function() { + return this.m_jointList; + } + b2Body.prototype.GetControllerList = function() { + return this.m_controllerList; + } + b2Body.prototype.GetContactList = function() { + return this.m_contactList; + } + b2Body.prototype.GetNext = function() { + return this.m_next; + } + b2Body.prototype.GetUserData = function() { + return this.m_userData; + } + b2Body.prototype.SetUserData = function(data) { + this.m_userData = data; + } + b2Body.prototype.GetWorld = function() { + return this.m_world; + } + b2Body.prototype.b2Body = function(bd, world) { + this.m_flags = 0; + if(bd.bullet) { + this.m_flags |= b2Body.e_bulletFlag; + } + if(bd.fixedRotation) { + this.m_flags |= b2Body.e_fixedRotationFlag; + } + if(bd.allowSleep) { + this.m_flags |= b2Body.e_allowSleepFlag; + } + if(bd.awake) { + this.m_flags |= b2Body.e_awakeFlag; + } + if(bd.active) { + this.m_flags |= b2Body.e_activeFlag; + } + this.m_world = world; + this.m_xf.position.SetV(bd.position); + this.m_xf.R.Set(bd.angle); + this.m_sweep.localCenter.SetZero(); + this.m_sweep.t0 = 1.0; + this.m_sweep.a0 = this.m_sweep.a = bd.angle; + var tMat = this.m_xf.R; + var tVec = this.m_sweep.localCenter; + this.m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + this.m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_sweep.c.x += this.m_xf.position.x; + this.m_sweep.c.y += this.m_xf.position.y; + this.m_sweep.c0.SetV(this.m_sweep.c); + this.m_jointList = null; + this.m_controllerList = null; + this.m_contactList = null; + this.m_controllerCount = 0; + this.m_prev = null; + this.m_next = null; + this.m_linearVelocity.SetV(bd.linearVelocity); + this.m_angularVelocity = bd.angularVelocity; + this.m_linearDamping = bd.linearDamping; + this.m_angularDamping = bd.angularDamping; + this.m_force.Set(0.0, 0.0); + this.m_torque = 0.0; + this.m_sleepTime = 0.0; + this.m_type = bd.type; + if(this.m_type == b2Body.b2_dynamicBody) { + this.m_mass = 1.0; + this.m_invMass = 1.0; + } else { + this.m_mass = 0.0; + this.m_invMass = 0.0; + } + this.m_I = 0.0; + this.m_invI = 0.0; + this.m_inertiaScale = bd.inertiaScale; + this.m_userData = bd.userData; + this.m_fixtureList = null; + this.m_fixtureCount = 0; + } + b2Body.prototype.SynchronizeFixtures = function() { + var xf1 = b2Body.s_xf1; + xf1.R.Set(this.m_sweep.a0); + var tMat = xf1.R; + var tVec = this.m_sweep.localCenter; + xf1.position.x = this.m_sweep.c0.x - (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + xf1.position.y = this.m_sweep.c0.y - (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var f; + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + for(f = this.m_fixtureList; f; f = f.m_next) { + f.Synchronize(broadPhase, xf1, this.m_xf); + } + } + b2Body.prototype.SynchronizeTransform = function() { + this.m_xf.R.Set(this.m_sweep.a); + var tMat = this.m_xf.R; + var tVec = this.m_sweep.localCenter; + this.m_xf.position.x = this.m_sweep.c.x - (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + this.m_xf.position.y = this.m_sweep.c.y - (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + } + b2Body.prototype.ShouldCollide = function(other) { + if(this.m_type != b2Body.b2_dynamicBody && other.m_type != b2Body.b2_dynamicBody) { + return false; + } + for(var jn = this.m_jointList; jn; jn = jn.next) { + if(jn.other == other) + if(jn.joint.m_collideConnected == false) { + return false; + } + } + return true; + } + b2Body.prototype.Advance = function(t) { + if(t === undefined) t = 0; + this.m_sweep.Advance(t); + this.m_sweep.c.SetV(this.m_sweep.c0); + this.m_sweep.a = this.m_sweep.a0; + this.SynchronizeTransform(); + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2Body.s_xf1 = new b2Transform(); + Box2D.Dynamics.b2Body.e_islandFlag = 0x0001; + Box2D.Dynamics.b2Body.e_awakeFlag = 0x0002; + Box2D.Dynamics.b2Body.e_allowSleepFlag = 0x0004; + Box2D.Dynamics.b2Body.e_bulletFlag = 0x0008; + Box2D.Dynamics.b2Body.e_fixedRotationFlag = 0x0010; + Box2D.Dynamics.b2Body.e_activeFlag = 0x0020; + Box2D.Dynamics.b2Body.b2_staticBody = 0; + Box2D.Dynamics.b2Body.b2_kinematicBody = 1; + Box2D.Dynamics.b2Body.b2_dynamicBody = 2; + }); + b2BodyDef.b2BodyDef = function() { + this.position = new b2Vec2(); + this.linearVelocity = new b2Vec2(); + }; + b2BodyDef.prototype.b2BodyDef = function() { + this.userData = null; + this.position.Set(0.0, 0.0); + this.angle = 0.0; + this.linearVelocity.Set(0, 0); + this.angularVelocity = 0.0; + this.linearDamping = 0.0; + this.angularDamping = 0.0; + this.allowSleep = true; + this.awake = true; + this.fixedRotation = false; + this.bullet = false; + this.type = b2Body.b2_staticBody; + this.active = true; + this.inertiaScale = 1.0; + } + b2ContactFilter.b2ContactFilter = function() {}; + b2ContactFilter.prototype.ShouldCollide = function(fixtureA, fixtureB) { + var filter1 = fixtureA.GetFilterData(); + var filter2 = fixtureB.GetFilterData(); + if(filter1.groupIndex == filter2.groupIndex && filter1.groupIndex != 0) { + return filter1.groupIndex > 0; + } + var collide = (filter1.maskBits & filter2.categoryBits) != 0 && (filter1.categoryBits & filter2.maskBits) != 0; + return collide; + } + b2ContactFilter.prototype.RayCollide = function(userData, fixture) { + if(!userData) return true; + return this.ShouldCollide((userData instanceof b2Fixture ? userData : null), fixture); + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2ContactFilter.b2_defaultFilter = new b2ContactFilter(); + }); + b2ContactImpulse.b2ContactImpulse = function() { + this.normalImpulses = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints); + this.tangentImpulses = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints); + }; + b2ContactListener.b2ContactListener = function() {}; + b2ContactListener.prototype.BeginContact = function(contact) {} + b2ContactListener.prototype.EndContact = function(contact) {} + b2ContactListener.prototype.PreSolve = function(contact, oldManifold) {} + b2ContactListener.prototype.PostSolve = function(contact, impulse) {} + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2ContactListener.b2_defaultListener = new b2ContactListener(); + }); + b2ContactManager.b2ContactManager = function() {}; + b2ContactManager.prototype.b2ContactManager = function() { + this.m_world = null; + this.m_contactCount = 0; + this.m_contactFilter = b2ContactFilter.b2_defaultFilter; + this.m_contactListener = b2ContactListener.b2_defaultListener; + this.m_contactFactory = new b2ContactFactory(this.m_allocator); + this.m_broadPhase = new b2DynamicTreeBroadPhase(); + } + b2ContactManager.prototype.AddPair = function(proxyUserDataA, proxyUserDataB) { + var fixtureA = (proxyUserDataA instanceof b2Fixture ? proxyUserDataA : null); + var fixtureB = (proxyUserDataB instanceof b2Fixture ? proxyUserDataB : null); + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if(bodyA == bodyB) return; + var edge = bodyB.GetContactList(); + while(edge) { + if(edge.other == bodyA) { + var fA = edge.contact.GetFixtureA(); + var fB = edge.contact.GetFixtureB(); + if(fA == fixtureA && fB == fixtureB) return; + if(fA == fixtureB && fB == fixtureA) return; + } + edge = edge.next; + } + if(bodyB.ShouldCollide(bodyA) == false) { + return; + } + if(this.m_contactFilter.ShouldCollide(fixtureA, fixtureB) == false) { + return; + } + var c = this.m_contactFactory.Create(fixtureA, fixtureB); + fixtureA = c.GetFixtureA(); + fixtureB = c.GetFixtureB(); + bodyA = fixtureA.m_body; + bodyB = fixtureB.m_body; + c.m_prev = null; + c.m_next = this.m_world.m_contactList; + if(this.m_world.m_contactList != null) { + this.m_world.m_contactList.m_prev = c; + } + this.m_world.m_contactList = c; + c.m_nodeA.contact = c; + c.m_nodeA.other = bodyB; + c.m_nodeA.prev = null; + c.m_nodeA.next = bodyA.m_contactList; + if(bodyA.m_contactList != null) { + bodyA.m_contactList.prev = c.m_nodeA; + } + bodyA.m_contactList = c.m_nodeA; + c.m_nodeB.contact = c; + c.m_nodeB.other = bodyA; + c.m_nodeB.prev = null; + c.m_nodeB.next = bodyB.m_contactList; + if(bodyB.m_contactList != null) { + bodyB.m_contactList.prev = c.m_nodeB; + } + bodyB.m_contactList = c.m_nodeB; + ++this.m_world.m_contactCount; + return; + } + b2ContactManager.prototype.FindNewContacts = function() { + this.m_broadPhase.UpdatePairs(Box2D.generateCallback(this, this.AddPair)); + } + b2ContactManager.prototype.Destroy = function(c) { + var fixtureA = c.GetFixtureA(); + var fixtureB = c.GetFixtureB(); + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if(c.IsTouching()) { + this.m_contactListener.EndContact(c); + } + if(c.m_prev) { + c.m_prev.m_next = c.m_next; + } + if(c.m_next) { + c.m_next.m_prev = c.m_prev; + } + if(c == this.m_world.m_contactList) { + this.m_world.m_contactList = c.m_next; + } + if(c.m_nodeA.prev) { + c.m_nodeA.prev.next = c.m_nodeA.next; + } + if(c.m_nodeA.next) { + c.m_nodeA.next.prev = c.m_nodeA.prev; + } + if(c.m_nodeA == bodyA.m_contactList) { + bodyA.m_contactList = c.m_nodeA.next; + } + if(c.m_nodeB.prev) { + c.m_nodeB.prev.next = c.m_nodeB.next; + } + if(c.m_nodeB.next) { + c.m_nodeB.next.prev = c.m_nodeB.prev; + } + if(c.m_nodeB == bodyB.m_contactList) { + bodyB.m_contactList = c.m_nodeB.next; + } + this.m_contactFactory.Destroy(c); + --this.m_contactCount; + } + b2ContactManager.prototype.Collide = function() { + var c = this.m_world.m_contactList; + while(c) { + var fixtureA = c.GetFixtureA(); + var fixtureB = c.GetFixtureB(); + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if(bodyA.IsAwake() == false && bodyB.IsAwake() == false) { + c = c.GetNext(); + continue; + } + if(c.m_flags & b2Contact.e_filterFlag) { + if(bodyB.ShouldCollide(bodyA) == false) { + var cNuke = c; + c = cNuke.GetNext(); + this.Destroy(cNuke); + continue; + } + if(this.m_contactFilter.ShouldCollide(fixtureA, fixtureB) == false) { + cNuke = c; + c = cNuke.GetNext(); + this.Destroy(cNuke); + continue; + } + c.m_flags &= ~b2Contact.e_filterFlag; + } + var proxyA = fixtureA.m_proxy; + var proxyB = fixtureB.m_proxy; + var overlap = this.m_broadPhase.TestOverlap(proxyA, proxyB); + if(overlap == false) { + cNuke = c; + c = cNuke.GetNext(); + this.Destroy(cNuke); + continue; + } + c.Update(this.m_contactListener); + c = c.GetNext(); + } + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2ContactManager.s_evalCP = new b2ContactPoint(); + }); + b2DebugDraw.b2DebugDraw = function() {}; + b2DebugDraw.prototype.b2DebugDraw = function() {} + b2DebugDraw.prototype.SetFlags = function(flags) { + if(flags === undefined) flags = 0; + } + b2DebugDraw.prototype.GetFlags = function() {} + b2DebugDraw.prototype.AppendFlags = function(flags) { + if(flags === undefined) flags = 0; + } + b2DebugDraw.prototype.ClearFlags = function(flags) { + if(flags === undefined) flags = 0; + } + b2DebugDraw.prototype.SetSprite = function(sprite) {} + b2DebugDraw.prototype.GetSprite = function() {} + b2DebugDraw.prototype.SetDrawScale = function(drawScale) { + if(drawScale === undefined) drawScale = 0; + } + b2DebugDraw.prototype.GetDrawScale = function() {} + b2DebugDraw.prototype.SetLineThickness = function(lineThickness) { + if(lineThickness === undefined) lineThickness = 0; + } + b2DebugDraw.prototype.GetLineThickness = function() {} + b2DebugDraw.prototype.SetAlpha = function(alpha) { + if(alpha === undefined) alpha = 0; + } + b2DebugDraw.prototype.GetAlpha = function() {} + b2DebugDraw.prototype.SetFillAlpha = function(alpha) { + if(alpha === undefined) alpha = 0; + } + b2DebugDraw.prototype.GetFillAlpha = function() {} + b2DebugDraw.prototype.SetXFormScale = function(xformScale) { + if(xformScale === undefined) xformScale = 0; + } + b2DebugDraw.prototype.GetXFormScale = function() {} + b2DebugDraw.prototype.DrawPolygon = function(vertices, vertexCount, color) { + if(vertexCount === undefined) vertexCount = 0; + } + b2DebugDraw.prototype.DrawSolidPolygon = function(vertices, vertexCount, color) { + if(vertexCount === undefined) vertexCount = 0; + } + b2DebugDraw.prototype.DrawCircle = function(center, radius, color) { + if(radius === undefined) radius = 0; + } + b2DebugDraw.prototype.DrawSolidCircle = function(center, radius, axis, color) { + if(radius === undefined) radius = 0; + } + b2DebugDraw.prototype.DrawSegment = function(p1, p2, color) {} + b2DebugDraw.prototype.DrawTransform = function(xf) {} + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2DebugDraw.e_shapeBit = 0x0001; + Box2D.Dynamics.b2DebugDraw.e_jointBit = 0x0002; + Box2D.Dynamics.b2DebugDraw.e_aabbBit = 0x0004; + Box2D.Dynamics.b2DebugDraw.e_pairBit = 0x0008; + Box2D.Dynamics.b2DebugDraw.e_centerOfMassBit = 0x0010; + Box2D.Dynamics.b2DebugDraw.e_controllerBit = 0x0020; + }); + b2DestructionListener.b2DestructionListener = function() {}; + b2DestructionListener.prototype.SayGoodbyeJoint = function(joint) {} + b2DestructionListener.prototype.SayGoodbyeFixture = function(fixture) {} + b2FilterData.b2FilterData = function() { + this.categoryBits = 0x0001; + this.maskBits = 0xFFFF; + this.groupIndex = 0; + }; + b2FilterData.prototype.Copy = function() { + var copy = new b2FilterData(); + copy.categoryBits = this.categoryBits; + copy.maskBits = this.maskBits; + copy.groupIndex = this.groupIndex; + return copy; + } + b2Fixture.b2Fixture = function() { + this.m_filter = new b2FilterData(); + }; + b2Fixture.prototype.GetType = function() { + return this.m_shape.GetType(); + } + b2Fixture.prototype.GetShape = function() { + return this.m_shape; + } + b2Fixture.prototype.SetSensor = function(sensor) { + if(this.m_isSensor == sensor) return; + this.m_isSensor = sensor; + if(this.m_body == null) return; + var edge = this.m_body.GetContactList(); + while(edge) { + var contact = edge.contact; + var fixtureA = contact.GetFixtureA(); + var fixtureB = contact.GetFixtureB(); + if(fixtureA == this || fixtureB == this) contact.SetSensor(fixtureA.IsSensor() || fixtureB.IsSensor()); + edge = edge.next; + } + } + b2Fixture.prototype.IsSensor = function() { + return this.m_isSensor; + } + b2Fixture.prototype.SetFilterData = function(filter) { + this.m_filter = filter.Copy(); + if(this.m_body) return; + var edge = this.m_body.GetContactList(); + while(edge) { + var contact = edge.contact; + var fixtureA = contact.GetFixtureA(); + var fixtureB = contact.GetFixtureB(); + if(fixtureA == this || fixtureB == this) contact.FlagForFiltering(); + edge = edge.next; + } + } + b2Fixture.prototype.GetFilterData = function() { + return this.m_filter.Copy(); + } + b2Fixture.prototype.GetBody = function() { + return this.m_body; + } + b2Fixture.prototype.GetNext = function() { + return this.m_next; + } + b2Fixture.prototype.GetUserData = function() { + return this.m_userData; + } + b2Fixture.prototype.SetUserData = function(data) { + this.m_userData = data; + } + b2Fixture.prototype.TestPoint = function(p) { + return this.m_shape.TestPoint(this.m_body.GetTransform(), p); + } + b2Fixture.prototype.RayCast = function(output, input) { + return this.m_shape.RayCast(output, input, this.m_body.GetTransform()); + } + b2Fixture.prototype.GetMassData = function(massData) { + if(massData === undefined) massData = null; + if(massData == null) { + massData = new b2MassData(); + } + this.m_shape.ComputeMass(massData, this.m_density); + return massData; + } + b2Fixture.prototype.SetDensity = function(density) { + if(density === undefined) density = 0; + this.m_density = density; + } + b2Fixture.prototype.GetDensity = function() { + return this.m_density; + } + b2Fixture.prototype.GetFriction = function() { + return this.m_friction; + } + b2Fixture.prototype.SetFriction = function(friction) { + if(friction === undefined) friction = 0; + this.m_friction = friction; + } + b2Fixture.prototype.GetRestitution = function() { + return this.m_restitution; + } + b2Fixture.prototype.SetRestitution = function(restitution) { + if(restitution === undefined) restitution = 0; + this.m_restitution = restitution; + } + b2Fixture.prototype.GetAABB = function() { + return this.m_aabb; + } + b2Fixture.prototype.b2Fixture = function() { + this.m_aabb = new b2AABB(); + this.m_userData = null; + this.m_body = null; + this.m_next = null; + this.m_shape = null; + this.m_density = 0.0; + this.m_friction = 0.0; + this.m_restitution = 0.0; + } + b2Fixture.prototype.Create = function(body, xf, def) { + this.m_userData = def.userData; + this.m_friction = def.friction; + this.m_restitution = def.restitution; + this.m_body = body; + this.m_next = null; + this.m_filter = def.filter.Copy(); + this.m_isSensor = def.isSensor; + this.m_shape = def.shape.Copy(); + this.m_density = def.density; + } + b2Fixture.prototype.Destroy = function() { + this.m_shape = null; + } + b2Fixture.prototype.CreateProxy = function(broadPhase, xf) { + this.m_shape.ComputeAABB(this.m_aabb, xf); + this.m_proxy = broadPhase.CreateProxy(this.m_aabb, this); + } + b2Fixture.prototype.DestroyProxy = function(broadPhase) { + if(this.m_proxy == null) { + return; + } + broadPhase.DestroyProxy(this.m_proxy); + this.m_proxy = null; + } + b2Fixture.prototype.Synchronize = function(broadPhase, transform1, transform2) { + if(!this.m_proxy) return; + var aabb1 = new b2AABB(); + var aabb2 = new b2AABB(); + this.m_shape.ComputeAABB(aabb1, transform1); + this.m_shape.ComputeAABB(aabb2, transform2); + this.m_aabb.Combine(aabb1, aabb2); + var displacement = b2Math.SubtractVV(transform2.position, transform1.position); + broadPhase.MoveProxy(this.m_proxy, this.m_aabb, displacement); + } + b2FixtureDef.b2FixtureDef = function() { + this.filter = new b2FilterData(); + }; + b2FixtureDef.prototype.b2FixtureDef = function() { + this.shape = null; + this.userData = null; + this.friction = 0.2; + this.restitution = 0.0; + this.density = 0.0; + this.filter.categoryBits = 0x0001; + this.filter.maskBits = 0xFFFF; + this.filter.groupIndex = 0; + this.isSensor = false; + } + b2Island.b2Island = function() {}; + b2Island.prototype.b2Island = function() { + this.m_bodies = new Vector(); + this.m_contacts = new Vector(); + this.m_joints = new Vector(); + } + b2Island.prototype.Initialize = function(bodyCapacity, contactCapacity, jointCapacity, allocator, listener, contactSolver) { + if(bodyCapacity === undefined) bodyCapacity = 0; + if(contactCapacity === undefined) contactCapacity = 0; + if(jointCapacity === undefined) jointCapacity = 0; + var i = 0; + this.m_bodyCapacity = bodyCapacity; + this.m_contactCapacity = contactCapacity; + this.m_jointCapacity = jointCapacity; + this.m_bodyCount = 0; + this.m_contactCount = 0; + this.m_jointCount = 0; + this.m_allocator = allocator; + this.m_listener = listener; + this.m_contactSolver = contactSolver; + for(i = this.m_bodies.length; i < bodyCapacity; i++) + this.m_bodies[i] = null; + for(i = this.m_contacts.length; i < contactCapacity; i++) + this.m_contacts[i] = null; + for(i = this.m_joints.length; i < jointCapacity; i++) + this.m_joints[i] = null; + } + b2Island.prototype.Clear = function() { + this.m_bodyCount = 0; + this.m_contactCount = 0; + this.m_jointCount = 0; + } + b2Island.prototype.Solve = function(step, gravity, allowSleep) { + var i = 0; + var j = 0; + var b; + var joint; + for(i = 0; i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + if(b.GetType() != b2Body.b2_dynamicBody) continue; + b.m_linearVelocity.x += step.dt * (gravity.x + b.m_invMass * b.m_force.x); + b.m_linearVelocity.y += step.dt * (gravity.y + b.m_invMass * b.m_force.y); + b.m_angularVelocity += step.dt * b.m_invI * b.m_torque; + b.m_linearVelocity.Multiply(b2Math.Clamp(1.0 - step.dt * b.m_linearDamping, 0.0, 1.0)); + b.m_angularVelocity *= b2Math.Clamp(1.0 - step.dt * b.m_angularDamping, 0.0, 1.0); + } + this.m_contactSolver.Initialize(step, this.m_contacts, this.m_contactCount, this.m_allocator); + var contactSolver = this.m_contactSolver; + contactSolver.InitVelocityConstraints(step); + for(i = 0; i < this.m_jointCount; ++i) { + joint = this.m_joints[i]; + joint.InitVelocityConstraints(step); + } + for(i = 0; i < step.velocityIterations; ++i) { + for(j = 0; j < this.m_jointCount; ++j) { + joint = this.m_joints[j]; + joint.SolveVelocityConstraints(step); + } + contactSolver.SolveVelocityConstraints(); + } + for(i = 0; i < this.m_jointCount; ++i) { + joint = this.m_joints[i]; + joint.FinalizeVelocityConstraints(); + } + contactSolver.FinalizeVelocityConstraints(); + for(i = 0; i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + if(b.GetType() == b2Body.b2_staticBody) continue; + var translationX = step.dt * b.m_linearVelocity.x; + var translationY = step.dt * b.m_linearVelocity.y; + if((translationX * translationX + translationY * translationY) > b2Settings.b2_maxTranslationSquared) { + b.m_linearVelocity.Normalize(); + b.m_linearVelocity.x *= b2Settings.b2_maxTranslation * step.inv_dt; + b.m_linearVelocity.y *= b2Settings.b2_maxTranslation * step.inv_dt; + } + var rotation = step.dt * b.m_angularVelocity; + if(rotation * rotation > b2Settings.b2_maxRotationSquared) { + if(b.m_angularVelocity < 0.0) { + b.m_angularVelocity = (-b2Settings.b2_maxRotation * step.inv_dt); + } else { + b.m_angularVelocity = b2Settings.b2_maxRotation * step.inv_dt; + } + } + b.m_sweep.c0.SetV(b.m_sweep.c); + b.m_sweep.a0 = b.m_sweep.a; + b.m_sweep.c.x += step.dt * b.m_linearVelocity.x; + b.m_sweep.c.y += step.dt * b.m_linearVelocity.y; + b.m_sweep.a += step.dt * b.m_angularVelocity; + b.SynchronizeTransform(); + } + for(i = 0; i < step.positionIterations; ++i) { + var contactsOkay = contactSolver.SolvePositionConstraints(b2Settings.b2_contactBaumgarte); + var jointsOkay = true; + for(j = 0; j < this.m_jointCount; ++j) { + joint = this.m_joints[j]; + var jointOkay = joint.SolvePositionConstraints(b2Settings.b2_contactBaumgarte); + jointsOkay = jointsOkay && jointOkay; + } + if(contactsOkay && jointsOkay) { + break; + } + } + this.Report(contactSolver.m_constraints); + if(allowSleep) { + var minSleepTime = Number.MAX_VALUE; + var linTolSqr = b2Settings.b2_linearSleepTolerance * b2Settings.b2_linearSleepTolerance; + var angTolSqr = b2Settings.b2_angularSleepTolerance * b2Settings.b2_angularSleepTolerance; + for(i = 0; i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + if(b.GetType() == b2Body.b2_staticBody) { + continue; + } + if((b.m_flags & b2Body.e_allowSleepFlag) == 0) { + b.m_sleepTime = 0.0; + minSleepTime = 0.0; + } + if((b.m_flags & b2Body.e_allowSleepFlag) == 0 || b.m_angularVelocity * b.m_angularVelocity > angTolSqr || b2Math.Dot(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr) { + b.m_sleepTime = 0.0; + minSleepTime = 0.0; + } else { + b.m_sleepTime += step.dt; + minSleepTime = b2Math.Min(minSleepTime, b.m_sleepTime); + } + } + if(minSleepTime >= b2Settings.b2_timeToSleep) { + for(i = 0; i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + b.SetAwake(false); + } + } + } + } + b2Island.prototype.SolveTOI = function(subStep) { + var i = 0; + var j = 0; + this.m_contactSolver.Initialize(subStep, this.m_contacts, this.m_contactCount, this.m_allocator); + var contactSolver = this.m_contactSolver; + for(i = 0; i < this.m_jointCount; ++i) { + this.m_joints[i].InitVelocityConstraints(subStep); + } + for(i = 0; i < subStep.velocityIterations; ++i) { + contactSolver.SolveVelocityConstraints(); + for(j = 0; j < this.m_jointCount; ++j) { + this.m_joints[j].SolveVelocityConstraints(subStep); + } + } + for(i = 0; i < this.m_bodyCount; ++i) { + var b = this.m_bodies[i]; + if(b.GetType() == b2Body.b2_staticBody) continue; + var translationX = subStep.dt * b.m_linearVelocity.x; + var translationY = subStep.dt * b.m_linearVelocity.y; + if((translationX * translationX + translationY * translationY) > b2Settings.b2_maxTranslationSquared) { + b.m_linearVelocity.Normalize(); + b.m_linearVelocity.x *= b2Settings.b2_maxTranslation * subStep.inv_dt; + b.m_linearVelocity.y *= b2Settings.b2_maxTranslation * subStep.inv_dt; + } + var rotation = subStep.dt * b.m_angularVelocity; + if(rotation * rotation > b2Settings.b2_maxRotationSquared) { + if(b.m_angularVelocity < 0.0) { + b.m_angularVelocity = (-b2Settings.b2_maxRotation * subStep.inv_dt); + } else { + b.m_angularVelocity = b2Settings.b2_maxRotation * subStep.inv_dt; + } + } + b.m_sweep.c0.SetV(b.m_sweep.c); + b.m_sweep.a0 = b.m_sweep.a; + b.m_sweep.c.x += subStep.dt * b.m_linearVelocity.x; + b.m_sweep.c.y += subStep.dt * b.m_linearVelocity.y; + b.m_sweep.a += subStep.dt * b.m_angularVelocity; + b.SynchronizeTransform(); + } + var k_toiBaumgarte = 0.75; + for(i = 0; i < subStep.positionIterations; ++i) { + var contactsOkay = contactSolver.SolvePositionConstraints(k_toiBaumgarte); + var jointsOkay = true; + for(j = 0; j < this.m_jointCount; ++j) { + var jointOkay = this.m_joints[j].SolvePositionConstraints(b2Settings.b2_contactBaumgarte); + jointsOkay = jointsOkay && jointOkay; + } + if(contactsOkay && jointsOkay) { + break; + } + } + this.Report(contactSolver.m_constraints); + } + b2Island.prototype.Report = function(constraints) { + if(this.m_listener == null) { + return; + } + for(var i = 0; i < this.m_contactCount; ++i) { + var c = this.m_contacts[i]; + var cc = constraints[i]; + for(var j = 0; j < cc.pointCount; ++j) { + b2Island.s_impulse.normalImpulses[j] = cc.points[j].normalImpulse; + b2Island.s_impulse.tangentImpulses[j] = cc.points[j].tangentImpulse; + } + this.m_listener.PostSolve(c, b2Island.s_impulse); + } + } + b2Island.prototype.AddBody = function(body) { + body.m_islandIndex = this.m_bodyCount; + this.m_bodies[this.m_bodyCount++] = body; + } + b2Island.prototype.AddContact = function(contact) { + this.m_contacts[this.m_contactCount++] = contact; + } + b2Island.prototype.AddJoint = function(joint) { + this.m_joints[this.m_jointCount++] = joint; + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2Island.s_impulse = new b2ContactImpulse(); + }); + b2TimeStep.b2TimeStep = function() {}; + b2TimeStep.prototype.Set = function(step) { + this.dt = step.dt; + this.inv_dt = step.inv_dt; + this.positionIterations = step.positionIterations; + this.velocityIterations = step.velocityIterations; + this.warmStarting = step.warmStarting; + } + b2World.b2World = function() { + this.s_stack = new Vector(); + this.m_contactManager = new b2ContactManager(); + this.m_contactSolver = new b2ContactSolver(); + this.m_island = new b2Island(); + }; + b2World.prototype.b2World = function(gravity, doSleep) { + this.m_destructionListener = null; + this.m_debugDraw = null; + this.m_bodyList = null; + this.m_contactList = null; + this.m_jointList = null; + this.m_controllerList = null; + this.m_bodyCount = 0; + this.m_contactCount = 0; + this.m_jointCount = 0; + this.m_controllerCount = 0; + b2World.m_warmStarting = true; + b2World.m_continuousPhysics = true; + this.m_allowSleep = doSleep; + this.m_gravity = gravity; + this.m_inv_dt0 = 0.0; + this.m_contactManager.m_world = this; + var bd = new b2BodyDef(); + this.m_groundBody = this.CreateBody(bd); + } + b2World.prototype.SetDestructionListener = function(listener) { + this.m_destructionListener = listener; + } + b2World.prototype.SetContactFilter = function(filter) { + this.m_contactManager.m_contactFilter = filter; + } + b2World.prototype.SetContactListener = function(listener) { + this.m_contactManager.m_contactListener = listener; + } + b2World.prototype.SetDebugDraw = function(debugDraw) { + this.m_debugDraw = debugDraw; + } + b2World.prototype.SetBroadPhase = function(broadPhase) { + var oldBroadPhase = this.m_contactManager.m_broadPhase; + this.m_contactManager.m_broadPhase = broadPhase; + for(var b = this.m_bodyList; b; b = b.m_next) { + for(var f = b.m_fixtureList; f; f = f.m_next) { + f.m_proxy = broadPhase.CreateProxy(oldBroadPhase.GetFatAABB(f.m_proxy), f); + } + } + } + b2World.prototype.Validate = function() { + this.m_contactManager.m_broadPhase.Validate(); + } + b2World.prototype.GetProxyCount = function() { + return this.m_contactManager.m_broadPhase.GetProxyCount(); + } + b2World.prototype.CreateBody = function(def) { + if(this.IsLocked() == true) { + return null; + } + var b = new b2Body(def, this); + b.m_prev = null; + b.m_next = this.m_bodyList; + if(this.m_bodyList) { + this.m_bodyList.m_prev = b; + } + this.m_bodyList = b; + ++this.m_bodyCount; + return b; + } + b2World.prototype.DestroyBody = function(b) { + if(this.IsLocked() == true) { + return; + } + var jn = b.m_jointList; + while(jn) { + var jn0 = jn; + jn = jn.next; + if(this.m_destructionListener) { + this.m_destructionListener.SayGoodbyeJoint(jn0.joint); + } + this.DestroyJoint(jn0.joint); + } + var coe = b.m_controllerList; + while(coe) { + var coe0 = coe; + coe = coe.nextController; + coe0.controller.RemoveBody(b); + } + var ce = b.m_contactList; + while(ce) { + var ce0 = ce; + ce = ce.next; + this.m_contactManager.Destroy(ce0.contact); + } + b.m_contactList = null; + var f = b.m_fixtureList; + while(f) { + var f0 = f; + f = f.m_next; + if(this.m_destructionListener) { + this.m_destructionListener.SayGoodbyeFixture(f0); + } + f0.DestroyProxy(this.m_contactManager.m_broadPhase); + f0.Destroy(); + } + b.m_fixtureList = null; + b.m_fixtureCount = 0; + if(b.m_prev) { + b.m_prev.m_next = b.m_next; + } + if(b.m_next) { + b.m_next.m_prev = b.m_prev; + } + if(b == this.m_bodyList) { + this.m_bodyList = b.m_next; + }--this.m_bodyCount; + } + b2World.prototype.CreateJoint = function(def) { + var j = b2Joint.Create(def, null); + j.m_prev = null; + j.m_next = this.m_jointList; + if(this.m_jointList) { + this.m_jointList.m_prev = j; + } + this.m_jointList = j; + ++this.m_jointCount; + j.m_edgeA.joint = j; + j.m_edgeA.other = j.m_bodyB; + j.m_edgeA.prev = null; + j.m_edgeA.next = j.m_bodyA.m_jointList; + if(j.m_bodyA.m_jointList) j.m_bodyA.m_jointList.prev = j.m_edgeA; + j.m_bodyA.m_jointList = j.m_edgeA; + j.m_edgeB.joint = j; + j.m_edgeB.other = j.m_bodyA; + j.m_edgeB.prev = null; + j.m_edgeB.next = j.m_bodyB.m_jointList; + if(j.m_bodyB.m_jointList) j.m_bodyB.m_jointList.prev = j.m_edgeB; + j.m_bodyB.m_jointList = j.m_edgeB; + var bodyA = def.bodyA; + var bodyB = def.bodyB; + if(def.collideConnected == false) { + var edge = bodyB.GetContactList(); + while(edge) { + if(edge.other == bodyA) { + edge.contact.FlagForFiltering(); + } + edge = edge.next; + } + } + return j; + } + b2World.prototype.DestroyJoint = function(j) { + var collideConnected = j.m_collideConnected; + if(j.m_prev) { + j.m_prev.m_next = j.m_next; + } + if(j.m_next) { + j.m_next.m_prev = j.m_prev; + } + if(j == this.m_jointList) { + this.m_jointList = j.m_next; + } + var bodyA = j.m_bodyA; + var bodyB = j.m_bodyB; + bodyA.SetAwake(true); + bodyB.SetAwake(true); + if(j.m_edgeA.prev) { + j.m_edgeA.prev.next = j.m_edgeA.next; + } + if(j.m_edgeA.next) { + j.m_edgeA.next.prev = j.m_edgeA.prev; + } + if(j.m_edgeA == bodyA.m_jointList) { + bodyA.m_jointList = j.m_edgeA.next; + } + j.m_edgeA.prev = null; + j.m_edgeA.next = null; + if(j.m_edgeB.prev) { + j.m_edgeB.prev.next = j.m_edgeB.next; + } + if(j.m_edgeB.next) { + j.m_edgeB.next.prev = j.m_edgeB.prev; + } + if(j.m_edgeB == bodyB.m_jointList) { + bodyB.m_jointList = j.m_edgeB.next; + } + j.m_edgeB.prev = null; + j.m_edgeB.next = null; + b2Joint.Destroy(j, null); + --this.m_jointCount; + if(collideConnected == false) { + var edge = bodyB.GetContactList(); + while(edge) { + if(edge.other == bodyA) { + edge.contact.FlagForFiltering(); + } + edge = edge.next; + } + } + } + b2World.prototype.AddController = function(c) { + c.m_next = this.m_controllerList; + c.m_prev = null; + this.m_controllerList = c; + c.m_world = this; + this.m_controllerCount++; + return c; + } + b2World.prototype.RemoveController = function(c) { + if(c.m_prev) c.m_prev.m_next = c.m_next; + if(c.m_next) c.m_next.m_prev = c.m_prev; + if(this.m_controllerList == c) this.m_controllerList = c.m_next; + this.m_controllerCount--; + } + b2World.prototype.CreateController = function(controller) { + if(controller.m_world != this) throw new Error("Controller can only be a member of one box2dWorld"); + controller.m_next = this.m_controllerList; + controller.m_prev = null; + if(this.m_controllerList) this.m_controllerList.m_prev = controller; + this.m_controllerList = controller; + ++this.m_controllerCount; + controller.m_world = this; + return controller; + } + b2World.prototype.DestroyController = function(controller) { + controller.Clear(); + if(controller.m_next) controller.m_next.m_prev = controller.m_prev; + if(controller.m_prev) controller.m_prev.m_next = controller.m_next; + if(controller == this.m_controllerList) this.m_controllerList = controller.m_next; + --this.m_controllerCount; + } + b2World.prototype.SetWarmStarting = function(flag) { + b2World.m_warmStarting = flag; + } + b2World.prototype.SetContinuousPhysics = function(flag) { + b2World.m_continuousPhysics = flag; + } + b2World.prototype.GetBodyCount = function() { + return this.m_bodyCount; + } + b2World.prototype.GetJointCount = function() { + return this.m_jointCount; + } + b2World.prototype.GetContactCount = function() { + return this.m_contactCount; + } + b2World.prototype.SetGravity = function(gravity) { + this.m_gravity = gravity; + } + b2World.prototype.GetGravity = function() { + return this.m_gravity; + } + b2World.prototype.GetGroundBody = function() { + return this.m_groundBody; + } + b2World.prototype.Step = function(dt, velocityIterations, positionIterations) { + if(dt === undefined) dt = 0; + if(velocityIterations === undefined) velocityIterations = 0; + if(positionIterations === undefined) positionIterations = 0; + if(this.m_flags & b2World.e_newFixture) { + this.m_contactManager.FindNewContacts(); + this.m_flags &= ~b2World.e_newFixture; + } + this.m_flags |= b2World.e_locked; + var step = b2World.s_timestep2; + step.dt = dt; + step.velocityIterations = velocityIterations; + step.positionIterations = positionIterations; + if(dt > 0.0) { + step.inv_dt = 1.0 / dt; + } else { + step.inv_dt = 0.0; + } + step.dtRatio = this.m_inv_dt0 * dt; + step.warmStarting = b2World.m_warmStarting; + this.m_contactManager.Collide(); + if(step.dt > 0.0) { + this.Solve(step); + } + if(b2World.m_continuousPhysics && step.dt > 0.0) { + this.SolveTOI(step); + } + if(step.dt > 0.0) { + this.m_inv_dt0 = step.inv_dt; + } + this.m_flags &= ~b2World.e_locked; + } + b2World.prototype.ClearForces = function() { + for(var body = this.m_bodyList; body; body = body.m_next) { + body.m_force.SetZero(); + body.m_torque = 0.0; + } + } + b2World.prototype.DrawDebugData = function() { + if(this.m_debugDraw == null) { + return; + } + this.m_debugDraw.m_sprite.graphics.clear(); + var flags = this.m_debugDraw.GetFlags(); + var i = 0; + var b; + var f; + var s; + var j; + var bp; + var invQ = new b2Vec2; + var x1 = new b2Vec2; + var x2 = new b2Vec2; + var xf; + var b1 = new b2AABB(); + var b2 = new b2AABB(); + var vs = [new b2Vec2(), new b2Vec2(), new b2Vec2(), new b2Vec2()]; + var color = new b2Color(0, 0, 0); + if(flags & b2DebugDraw.e_shapeBit) { + for(b = this.m_bodyList; b; b = b.m_next) { + xf = b.m_xf; + for(f = b.GetFixtureList(); f; f = f.m_next) { + s = f.GetShape(); + if(b.IsActive() == false) { + color.Set(0.5, 0.5, 0.3); + this.DrawShape(s, xf, color); + } else if(b.GetType() == b2Body.b2_staticBody) { + color.Set(0.5, 0.9, 0.5); + this.DrawShape(s, xf, color); + } else if(b.GetType() == b2Body.b2_kinematicBody) { + color.Set(0.5, 0.5, 0.9); + this.DrawShape(s, xf, color); + } else if(b.IsAwake() == false) { + color.Set(0.6, 0.6, 0.6); + this.DrawShape(s, xf, color); + } else { + color.Set(0.9, 0.7, 0.7); + this.DrawShape(s, xf, color); + } + } + } + } + if(flags & b2DebugDraw.e_jointBit) { + for(j = this.m_jointList; j; j = j.m_next) { + this.DrawJoint(j); + } + } + if(flags & b2DebugDraw.e_controllerBit) { + for(var c = this.m_controllerList; c; c = c.m_next) { + c.Draw(this.m_debugDraw); + } + } + if(flags & b2DebugDraw.e_pairBit) { + color.Set(0.3, 0.9, 0.9); + for(var contact = this.m_contactManager.m_contactList; contact; contact = contact.GetNext()) { + var fixtureA = contact.GetFixtureA(); + var fixtureB = contact.GetFixtureB(); + var cA = fixtureA.GetAABB().GetCenter(); + var cB = fixtureB.GetAABB().GetCenter(); + this.m_debugDraw.DrawSegment(cA, cB, color); + } + } + if(flags & b2DebugDraw.e_aabbBit) { + bp = this.m_contactManager.m_broadPhase; + vs = [new b2Vec2(), new b2Vec2(), new b2Vec2(), new b2Vec2()]; + for(b = this.m_bodyList; b; b = b.GetNext()) { + if(b.IsActive() == false) { + continue; + } + for(f = b.GetFixtureList(); f; f = f.GetNext()) { + var aabb = bp.GetFatAABB(f.m_proxy); + vs[0].Set(aabb.lowerBound.x, aabb.lowerBound.y); + vs[1].Set(aabb.upperBound.x, aabb.lowerBound.y); + vs[2].Set(aabb.upperBound.x, aabb.upperBound.y); + vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y); + this.m_debugDraw.DrawPolygon(vs, 4, color); + } + } + } + if(flags & b2DebugDraw.e_centerOfMassBit) { + for(b = this.m_bodyList; b; b = b.m_next) { + xf = b2World.s_xf; + xf.R = b.m_xf.R; + xf.position = b.GetWorldCenter(); + this.m_debugDraw.DrawTransform(xf); + } + } + } + b2World.prototype.QueryAABB = function(callback, aabb) { + var __this = this; + var broadPhase = __this.m_contactManager.m_broadPhase; + + function WorldQueryWrapper(proxy) { + return callback(broadPhase.GetUserData(proxy)); + }; + broadPhase.Query(WorldQueryWrapper, aabb); + } + b2World.prototype.QueryShape = function(callback, shape, transform) { + var __this = this; + if(transform === undefined) transform = null; + if(transform == null) { + transform = new b2Transform(); + transform.SetIdentity(); + } + var broadPhase = __this.m_contactManager.m_broadPhase; + + function WorldQueryWrapper(proxy) { + var fixture = (broadPhase.GetUserData(proxy) instanceof b2Fixture ? broadPhase.GetUserData(proxy) : null); + if(b2Shape.TestOverlap(shape, transform, fixture.GetShape(), fixture.GetBody().GetTransform())) return callback(fixture); + return true; + }; + var aabb = new b2AABB(); + shape.ComputeAABB(aabb, transform); + broadPhase.Query(WorldQueryWrapper, aabb); + } + b2World.prototype.QueryPoint = function(callback, p) { + var __this = this; + var broadPhase = __this.m_contactManager.m_broadPhase; + + function WorldQueryWrapper(proxy) { + var fixture = (broadPhase.GetUserData(proxy) instanceof b2Fixture ? broadPhase.GetUserData(proxy) : null); + if(fixture.TestPoint(p)) return callback(fixture); + return true; + }; + var aabb = new b2AABB(); + aabb.lowerBound.Set(p.x - b2Settings.b2_linearSlop, p.y - b2Settings.b2_linearSlop); + aabb.upperBound.Set(p.x + b2Settings.b2_linearSlop, p.y + b2Settings.b2_linearSlop); + broadPhase.Query(WorldQueryWrapper, aabb); + } + b2World.prototype.RayCast = function(callback, point1, point2) { + var __this = this; + var broadPhase = __this.m_contactManager.m_broadPhase; + var output = new b2RayCastOutput; + + function RayCastWrapper(input, proxy) { + var userData = broadPhase.GetUserData(proxy); + var fixture = (userData instanceof b2Fixture ? userData : null); + var hit = fixture.RayCast(output, input); + if(hit) { + var fraction = output.fraction; + var point = new b2Vec2((1.0 - fraction) * point1.x + fraction * point2.x, (1.0 - fraction) * point1.y + fraction * point2.y); + return callback(fixture, point, output.normal, fraction); + } + return input.maxFraction; + }; + var input = new b2RayCastInput(point1, point2); + broadPhase.RayCast(RayCastWrapper, input); + } + b2World.prototype.RayCastOne = function(point1, point2) { + var __this = this; + var result; + + function RayCastOneWrapper(fixture, point, normal, fraction) { + if(fraction === undefined) fraction = 0; + result = fixture; + return fraction; + }; + __this.RayCast(RayCastOneWrapper, point1, point2); + return result; + } + b2World.prototype.RayCastAll = function(point1, point2) { + var __this = this; + var result = new Vector(); + + function RayCastAllWrapper(fixture, point, normal, fraction) { + if(fraction === undefined) fraction = 0; + result[result.length] = fixture; + return 1; + }; + __this.RayCast(RayCastAllWrapper, point1, point2); + return result; + } + b2World.prototype.GetBodyList = function() { + return this.m_bodyList; + } + b2World.prototype.GetJointList = function() { + return this.m_jointList; + } + b2World.prototype.GetContactList = function() { + return this.m_contactList; + } + b2World.prototype.IsLocked = function() { + return(this.m_flags & b2World.e_locked) > 0; + } + b2World.prototype.Solve = function(step) { + var b; + for(var controller = this.m_controllerList; controller; controller = controller.m_next) { + controller.Step(step); + } + var island = this.m_island; + island.Initialize(this.m_bodyCount, this.m_contactCount, this.m_jointCount, null, this.m_contactManager.m_contactListener, this.m_contactSolver); + for(b = this.m_bodyList; b; b = b.m_next) { + b.m_flags &= ~b2Body.e_islandFlag; + } + for(var c = this.m_contactList; c; c = c.m_next) { + c.m_flags &= ~b2Contact.e_islandFlag; + } + for(var j = this.m_jointList; j; j = j.m_next) { + j.m_islandFlag = false; + } + var stackSize = parseInt(this.m_bodyCount); + var stack = this.s_stack; + for(var seed = this.m_bodyList; seed; seed = seed.m_next) { + if(seed.m_flags & b2Body.e_islandFlag) { + continue; + } + if(seed.IsAwake() == false || seed.IsActive() == false) { + continue; + } + if(seed.GetType() == b2Body.b2_staticBody) { + continue; + } + island.Clear(); + var stackCount = 0; + stack[stackCount++] = seed; + seed.m_flags |= b2Body.e_islandFlag; + while(stackCount > 0) { + b = stack[--stackCount]; + island.AddBody(b); + if(b.IsAwake() == false) { + b.SetAwake(true); + } + if(b.GetType() == b2Body.b2_staticBody) { + continue; + } + var other; + for(var ce = b.m_contactList; ce; ce = ce.next) { + if(ce.contact.m_flags & b2Contact.e_islandFlag) { + continue; + } + if(ce.contact.IsSensor() == true || ce.contact.IsEnabled() == false || ce.contact.IsTouching() == false) { + continue; + } + island.AddContact(ce.contact); + ce.contact.m_flags |= b2Contact.e_islandFlag; + other = ce.other; + if(other.m_flags & b2Body.e_islandFlag) { + continue; + } + stack[stackCount++] = other; + other.m_flags |= b2Body.e_islandFlag; + } + for(var jn = b.m_jointList; jn; jn = jn.next) { + if(jn.joint.m_islandFlag == true) { + continue; + } + other = jn.other; + if(other.IsActive() == false) { + continue; + } + island.AddJoint(jn.joint); + jn.joint.m_islandFlag = true; + if(other.m_flags & b2Body.e_islandFlag) { + continue; + } + stack[stackCount++] = other; + other.m_flags |= b2Body.e_islandFlag; + } + } + island.Solve(step, this.m_gravity, this.m_allowSleep); + for(var i = 0; i < island.m_bodyCount; ++i) { + b = island.m_bodies[i]; + if(b.GetType() == b2Body.b2_staticBody) { + b.m_flags &= ~b2Body.e_islandFlag; + } + } + } + for(i = 0; i < stack.length; ++i) { + if(!stack[i]) break; + stack[i] = null; + } + for(b = this.m_bodyList; b; b = b.m_next) { + if(b.IsAwake() == false || b.IsActive() == false) { + continue; + } + if(b.GetType() == b2Body.b2_staticBody) { + continue; + } + b.SynchronizeFixtures(); + } + this.m_contactManager.FindNewContacts(); + } + b2World.prototype.SolveTOI = function(step) { + var b; + var fA; + var fB; + var bA; + var bB; + var cEdge; + var j; + var island = this.m_island; + island.Initialize(this.m_bodyCount, b2Settings.b2_maxTOIContactsPerIsland, b2Settings.b2_maxTOIJointsPerIsland, null, this.m_contactManager.m_contactListener, this.m_contactSolver); + var queue = b2World.s_queue; + for(b = this.m_bodyList; b; b = b.m_next) { + b.m_flags &= ~b2Body.e_islandFlag; + b.m_sweep.t0 = 0.0; + } + var c; + for(c = this.m_contactList; c; c = c.m_next) { + c.m_flags &= ~(b2Contact.e_toiFlag | b2Contact.e_islandFlag); + } + for(j = this.m_jointList; j; j = j.m_next) { + j.m_islandFlag = false; + } + for(;;) { + var minContact = null; + var minTOI = 1.0; + for(c = this.m_contactList; c; c = c.m_next) { + if(c.IsSensor() == true || c.IsEnabled() == false || c.IsContinuous() == false) { + continue; + } + var toi = 1.0; + if(c.m_flags & b2Contact.e_toiFlag) { + toi = c.m_toi; + } else { + fA = c.m_fixtureA; + fB = c.m_fixtureB; + bA = fA.m_body; + bB = fB.m_body; + if((bA.GetType() != b2Body.b2_dynamicBody || bA.IsAwake() == false) && (bB.GetType() != b2Body.b2_dynamicBody || bB.IsAwake() == false)) { + continue; + } + var t0 = bA.m_sweep.t0; + if(bA.m_sweep.t0 < bB.m_sweep.t0) { + t0 = bB.m_sweep.t0; + bA.m_sweep.Advance(t0); + } else if(bB.m_sweep.t0 < bA.m_sweep.t0) { + t0 = bA.m_sweep.t0; + bB.m_sweep.Advance(t0); + } + toi = c.ComputeTOI(bA.m_sweep, bB.m_sweep); + b2Settings.b2Assert(0.0 <= toi && toi <= 1.0); + if(toi > 0.0 && toi < 1.0) { + toi = (1.0 - toi) * t0 + toi; + if(toi > 1) toi = 1; + } + c.m_toi = toi; + c.m_flags |= b2Contact.e_toiFlag; + } + if(Number.MIN_VALUE < toi && toi < minTOI) { + minContact = c; + minTOI = toi; + } + } + if(minContact == null || 1.0 - 100.0 * Number.MIN_VALUE < minTOI) { + break; + } + fA = minContact.m_fixtureA; + fB = minContact.m_fixtureB; + bA = fA.m_body; + bB = fB.m_body; + b2World.s_backupA.Set(bA.m_sweep); + b2World.s_backupB.Set(bB.m_sweep); + bA.Advance(minTOI); + bB.Advance(minTOI); + minContact.Update(this.m_contactManager.m_contactListener); + minContact.m_flags &= ~b2Contact.e_toiFlag; + if(minContact.IsSensor() == true || minContact.IsEnabled() == false) { + bA.m_sweep.Set(b2World.s_backupA); + bB.m_sweep.Set(b2World.s_backupB); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + continue; + } + if(minContact.IsTouching() == false) { + continue; + } + var seed = bA; + if(seed.GetType() != b2Body.b2_dynamicBody) { + seed = bB; + } + island.Clear(); + var queueStart = 0; + var queueSize = 0; + queue[queueStart + queueSize++] = seed; + seed.m_flags |= b2Body.e_islandFlag; + while(queueSize > 0) { + b = queue[queueStart++]; + --queueSize; + island.AddBody(b); + if(b.IsAwake() == false) { + b.SetAwake(true); + } + if(b.GetType() != b2Body.b2_dynamicBody) { + continue; + } + for(cEdge = b.m_contactList; cEdge; cEdge = cEdge.next) { + if(island.m_contactCount == island.m_contactCapacity) { + break; + } + if(cEdge.contact.m_flags & b2Contact.e_islandFlag) { + continue; + } + if(cEdge.contact.IsSensor() == true || cEdge.contact.IsEnabled() == false || cEdge.contact.IsTouching() == false) { + continue; + } + island.AddContact(cEdge.contact); + cEdge.contact.m_flags |= b2Contact.e_islandFlag; + var other = cEdge.other; + if(other.m_flags & b2Body.e_islandFlag) { + continue; + } + if(other.GetType() != b2Body.b2_staticBody) { + other.Advance(minTOI); + other.SetAwake(true); + } + queue[queueStart + queueSize] = other; + ++queueSize; + other.m_flags |= b2Body.e_islandFlag; + } + for(var jEdge = b.m_jointList; jEdge; jEdge = jEdge.next) { + if(island.m_jointCount == island.m_jointCapacity) continue; + if(jEdge.joint.m_islandFlag == true) continue; + other = jEdge.other; + if(other.IsActive() == false) { + continue; + } + island.AddJoint(jEdge.joint); + jEdge.joint.m_islandFlag = true; + if(other.m_flags & b2Body.e_islandFlag) continue; + if(other.GetType() != b2Body.b2_staticBody) { + other.Advance(minTOI); + other.SetAwake(true); + } + queue[queueStart + queueSize] = other; + ++queueSize; + other.m_flags |= b2Body.e_islandFlag; + } + } + var subStep = b2World.s_timestep; + subStep.warmStarting = false; + subStep.dt = (1.0 - minTOI) * step.dt; + subStep.inv_dt = 1.0 / subStep.dt; + subStep.dtRatio = 0.0; + subStep.velocityIterations = step.velocityIterations; + subStep.positionIterations = step.positionIterations; + island.SolveTOI(subStep); + var i = 0; + for(i = 0; i < island.m_bodyCount; ++i) { + b = island.m_bodies[i]; + b.m_flags &= ~b2Body.e_islandFlag; + if(b.IsAwake() == false) { + continue; + } + if(b.GetType() != b2Body.b2_dynamicBody) { + continue; + } + b.SynchronizeFixtures(); + for(cEdge = b.m_contactList; cEdge; cEdge = cEdge.next) { + cEdge.contact.m_flags &= ~b2Contact.e_toiFlag; + } + } + for(i = 0; i < island.m_contactCount; ++i) { + c = island.m_contacts[i]; + c.m_flags &= ~(b2Contact.e_toiFlag | b2Contact.e_islandFlag); + } + for(i = 0; i < island.m_jointCount; ++i) { + j = island.m_joints[i]; + j.m_islandFlag = false; + } + this.m_contactManager.FindNewContacts(); + } + } + b2World.prototype.DrawJoint = function(joint) { + var b1 = joint.GetBodyA(); + var b2 = joint.GetBodyB(); + var xf1 = b1.m_xf; + var xf2 = b2.m_xf; + var x1 = xf1.position; + var x2 = xf2.position; + var p1 = joint.GetAnchorA(); + var p2 = joint.GetAnchorB(); + var color = b2World.s_jointColor; + switch(joint.m_type) { + case b2Joint.e_distanceJoint: + this.m_debugDraw.DrawSegment(p1, p2, color); + break; + case b2Joint.e_pulleyJoint: + { + var pulley = ((joint instanceof b2PulleyJoint ? joint : null)); + var s1 = pulley.GetGroundAnchorA(); + var s2 = pulley.GetGroundAnchorB(); + this.m_debugDraw.DrawSegment(s1, p1, color); + this.m_debugDraw.DrawSegment(s2, p2, color); + this.m_debugDraw.DrawSegment(s1, s2, color); + } + break; + case b2Joint.e_mouseJoint: + this.m_debugDraw.DrawSegment(p1, p2, color); + break; + default: + if(b1 != this.m_groundBody) this.m_debugDraw.DrawSegment(x1, p1, color); + this.m_debugDraw.DrawSegment(p1, p2, color); + if(b2 != this.m_groundBody) this.m_debugDraw.DrawSegment(x2, p2, color); + } + } + b2World.prototype.DrawShape = function(shape, xf, color) { + switch(shape.m_type) { + case b2Shape.e_circleShape: + { + var circle = ((shape instanceof b2CircleShape ? shape : null)); + var center = b2Math.MulX(xf, circle.m_p); + var radius = circle.m_radius; + var axis = xf.R.col1; + this.m_debugDraw.DrawSolidCircle(center, radius, axis, color); + } + break; + case b2Shape.e_polygonShape: + { + var i = 0; + var poly = ((shape instanceof b2PolygonShape ? shape : null)); + var vertexCount = parseInt(poly.GetVertexCount()); + var localVertices = poly.GetVertices(); + var vertices = new Vector(vertexCount); + for(i = 0; i < vertexCount; ++i) { + vertices[i] = b2Math.MulX(xf, localVertices[i]); + } + this.m_debugDraw.DrawSolidPolygon(vertices, vertexCount, color); + } + break; + case b2Shape.e_edgeShape: + { + var edge = (shape instanceof b2EdgeShape ? shape : null); + this.m_debugDraw.DrawSegment(b2Math.MulX(xf, edge.GetVertex1()), b2Math.MulX(xf, edge.GetVertex2()), color); + } + break; + } + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.b2World.s_timestep2 = new b2TimeStep(); + Box2D.Dynamics.b2World.s_xf = new b2Transform(); + Box2D.Dynamics.b2World.s_backupA = new b2Sweep(); + Box2D.Dynamics.b2World.s_backupB = new b2Sweep(); + Box2D.Dynamics.b2World.s_timestep = new b2TimeStep(); + Box2D.Dynamics.b2World.s_queue = new Vector(); + Box2D.Dynamics.b2World.s_jointColor = new b2Color(0.5, 0.8, 0.8); + Box2D.Dynamics.b2World.e_newFixture = 0x0001; + Box2D.Dynamics.b2World.e_locked = 0x0002; + }); +})(); +(function() { + var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2CircleContact = Box2D.Dynamics.Contacts.b2CircleContact, + b2Contact = Box2D.Dynamics.Contacts.b2Contact, + b2ContactConstraint = Box2D.Dynamics.Contacts.b2ContactConstraint, + b2ContactConstraintPoint = Box2D.Dynamics.Contacts.b2ContactConstraintPoint, + b2ContactEdge = Box2D.Dynamics.Contacts.b2ContactEdge, + b2ContactFactory = Box2D.Dynamics.Contacts.b2ContactFactory, + b2ContactRegister = Box2D.Dynamics.Contacts.b2ContactRegister, + b2ContactResult = Box2D.Dynamics.Contacts.b2ContactResult, + b2ContactSolver = Box2D.Dynamics.Contacts.b2ContactSolver, + b2EdgeAndCircleContact = Box2D.Dynamics.Contacts.b2EdgeAndCircleContact, + b2NullContact = Box2D.Dynamics.Contacts.b2NullContact, + b2PolyAndCircleContact = Box2D.Dynamics.Contacts.b2PolyAndCircleContact, + b2PolyAndEdgeContact = Box2D.Dynamics.Contacts.b2PolyAndEdgeContact, + b2PolygonContact = Box2D.Dynamics.Contacts.b2PolygonContact, + b2PositionSolverManifold = Box2D.Dynamics.Contacts.b2PositionSolverManifold, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase; + + Box2D.inherit(b2CircleContact, Box2D.Dynamics.Contacts.b2Contact); + b2CircleContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2CircleContact.b2CircleContact = function() { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2CircleContact.Create = function(allocator) { + return new b2CircleContact(); + } + b2CircleContact.Destroy = function(contact, allocator) {} + b2CircleContact.prototype.Reset = function(fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + } + b2CircleContact.prototype.Evaluate = function() { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + b2Collision.CollideCircles(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2CircleShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2CircleShape ? + this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2Contact.b2Contact = function() { + this.m_nodeA = new b2ContactEdge(); + this.m_nodeB = new b2ContactEdge(); + this.m_manifold = new b2Manifold(); + this.m_oldManifold = new b2Manifold(); + }; + b2Contact.prototype.GetManifold = function() { + return this.m_manifold; + } + b2Contact.prototype.GetWorldManifold = function(worldManifold) { + var bodyA = this.m_fixtureA.GetBody(); + var bodyB = this.m_fixtureB.GetBody(); + var shapeA = this.m_fixtureA.GetShape(); + var shapeB = this.m_fixtureB.GetShape(); + worldManifold.Initialize(this.m_manifold, bodyA.GetTransform(), shapeA.m_radius, bodyB.GetTransform(), shapeB.m_radius); + } + b2Contact.prototype.IsTouching = function() { + return(this.m_flags & b2Contact.e_touchingFlag) == b2Contact.e_touchingFlag; + } + b2Contact.prototype.IsContinuous = function() { + return(this.m_flags & b2Contact.e_continuousFlag) == b2Contact.e_continuousFlag; + } + b2Contact.prototype.SetSensor = function(sensor) { + if(sensor) { + this.m_flags |= b2Contact.e_sensorFlag; + } else { + this.m_flags &= ~b2Contact.e_sensorFlag; + } + } + b2Contact.prototype.IsSensor = function() { + return(this.m_flags & b2Contact.e_sensorFlag) == b2Contact.e_sensorFlag; + } + b2Contact.prototype.SetEnabled = function(flag) { + if(flag) { + this.m_flags |= b2Contact.e_enabledFlag; + } else { + this.m_flags &= ~b2Contact.e_enabledFlag; + } + } + b2Contact.prototype.IsEnabled = function() { + return(this.m_flags & b2Contact.e_enabledFlag) == b2Contact.e_enabledFlag; + } + b2Contact.prototype.GetNext = function() { + return this.m_next; + } + b2Contact.prototype.GetFixtureA = function() { + return this.m_fixtureA; + } + b2Contact.prototype.GetFixtureB = function() { + return this.m_fixtureB; + } + b2Contact.prototype.FlagForFiltering = function() { + this.m_flags |= b2Contact.e_filterFlag; + } + b2Contact.prototype.b2Contact = function() {} + b2Contact.prototype.Reset = function(fixtureA, fixtureB) { + if(fixtureA === undefined) fixtureA = null; + if(fixtureB === undefined) fixtureB = null; + this.m_flags = b2Contact.e_enabledFlag; + if(!fixtureA || !fixtureB) { + this.m_fixtureA = null; + this.m_fixtureB = null; + return; + } + if(fixtureA.IsSensor() || fixtureB.IsSensor()) { + this.m_flags |= b2Contact.e_sensorFlag; + } + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if(bodyA.GetType() != b2Body.b2_dynamicBody || bodyA.IsBullet() || bodyB.GetType() != b2Body.b2_dynamicBody || bodyB.IsBullet()) { + this.m_flags |= b2Contact.e_continuousFlag; + } + this.m_fixtureA = fixtureA; + this.m_fixtureB = fixtureB; + this.m_manifold.m_pointCount = 0; + this.m_prev = null; + this.m_next = null; + this.m_nodeA.contact = null; + this.m_nodeA.prev = null; + this.m_nodeA.next = null; + this.m_nodeA.other = null; + this.m_nodeB.contact = null; + this.m_nodeB.prev = null; + this.m_nodeB.next = null; + this.m_nodeB.other = null; + } + b2Contact.prototype.Update = function(listener) { + var tManifold = this.m_oldManifold; + this.m_oldManifold = this.m_manifold; + this.m_manifold = tManifold; + this.m_flags |= b2Contact.e_enabledFlag; + var touching = false; + var wasTouching = (this.m_flags & b2Contact.e_touchingFlag) == b2Contact.e_touchingFlag; + var bodyA = this.m_fixtureA.m_body; + var bodyB = this.m_fixtureB.m_body; + var aabbOverlap = this.m_fixtureA.m_aabb.TestOverlap(this.m_fixtureB.m_aabb); + if(this.m_flags & b2Contact.e_sensorFlag) { + if(aabbOverlap) { + var shapeA = this.m_fixtureA.GetShape(); + var shapeB = this.m_fixtureB.GetShape(); + var xfA = bodyA.GetTransform(); + var xfB = bodyB.GetTransform(); + touching = b2Shape.TestOverlap(shapeA, xfA, shapeB, xfB); + } + this.m_manifold.m_pointCount = 0; + } else { + if(bodyA.GetType() != b2Body.b2_dynamicBody || bodyA.IsBullet() || bodyB.GetType() != b2Body.b2_dynamicBody || bodyB.IsBullet()) { + this.m_flags |= b2Contact.e_continuousFlag; + } else { + this.m_flags &= ~b2Contact.e_continuousFlag; + } + if(aabbOverlap) { + this.Evaluate(); + touching = this.m_manifold.m_pointCount > 0; + for(var i = 0; i < this.m_manifold.m_pointCount; ++i) { + var mp2 = this.m_manifold.m_points[i]; + mp2.m_normalImpulse = 0.0; + mp2.m_tangentImpulse = 0.0; + var id2 = mp2.m_id; + for(var j = 0; j < this.m_oldManifold.m_pointCount; ++j) { + var mp1 = this.m_oldManifold.m_points[j]; + if(mp1.m_id.key == id2.key) { + mp2.m_normalImpulse = mp1.m_normalImpulse; + mp2.m_tangentImpulse = mp1.m_tangentImpulse; + break; + } + } + } + } else { + this.m_manifold.m_pointCount = 0; + } + if(touching != wasTouching) { + bodyA.SetAwake(true); + bodyB.SetAwake(true); + } + } + if(touching) { + this.m_flags |= b2Contact.e_touchingFlag; + } else { + this.m_flags &= ~b2Contact.e_touchingFlag; + } + if(wasTouching == false && touching == true) { + listener.BeginContact(this); + } + if(wasTouching == true && touching == false) { + listener.EndContact(this); + } + if((this.m_flags & b2Contact.e_sensorFlag) == 0) { + listener.PreSolve(this, this.m_oldManifold); + } + } + b2Contact.prototype.Evaluate = function() {} + b2Contact.prototype.ComputeTOI = function(sweepA, sweepB) { + b2Contact.s_input.proxyA.Set(this.m_fixtureA.GetShape()); + b2Contact.s_input.proxyB.Set(this.m_fixtureB.GetShape()); + b2Contact.s_input.sweepA = sweepA; + b2Contact.s_input.sweepB = sweepB; + b2Contact.s_input.tolerance = b2Settings.b2_linearSlop; + return b2TimeOfImpact.TimeOfImpact(b2Contact.s_input); + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.Contacts.b2Contact.e_sensorFlag = 0x0001; + Box2D.Dynamics.Contacts.b2Contact.e_continuousFlag = 0x0002; + Box2D.Dynamics.Contacts.b2Contact.e_islandFlag = 0x0004; + Box2D.Dynamics.Contacts.b2Contact.e_toiFlag = 0x0008; + Box2D.Dynamics.Contacts.b2Contact.e_touchingFlag = 0x0010; + Box2D.Dynamics.Contacts.b2Contact.e_enabledFlag = 0x0020; + Box2D.Dynamics.Contacts.b2Contact.e_filterFlag = 0x0040; + Box2D.Dynamics.Contacts.b2Contact.s_input = new b2TOIInput(); + }); + b2ContactConstraint.b2ContactConstraint = function() { + this.localPlaneNormal = new b2Vec2(); + this.localPoint = new b2Vec2(); + this.normal = new b2Vec2(); + this.normalMass = new b2Mat22(); + this.K = new b2Mat22(); + }; + b2ContactConstraint.prototype.b2ContactConstraint = function() { + this.points = new Vector(b2Settings.b2_maxManifoldPoints); + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.points[i] = new b2ContactConstraintPoint(); + } + } + b2ContactConstraintPoint.b2ContactConstraintPoint = function() { + this.localPoint = new b2Vec2(); + this.rA = new b2Vec2(); + this.rB = new b2Vec2(); + }; + b2ContactEdge.b2ContactEdge = function() {}; + b2ContactFactory.b2ContactFactory = function() {}; + b2ContactFactory.prototype.b2ContactFactory = function(allocator) { + this.m_allocator = allocator; + this.InitializeRegisters(); + } + b2ContactFactory.prototype.AddType = function(createFcn, destroyFcn, type1, type2) { + if(type1 === undefined) type1 = 0; + if(type2 === undefined) type2 = 0; + this.m_registers[type1][type2].createFcn = createFcn; + this.m_registers[type1][type2].destroyFcn = destroyFcn; + this.m_registers[type1][type2].primary = true; + if(type1 != type2) { + this.m_registers[type2][type1].createFcn = createFcn; + this.m_registers[type2][type1].destroyFcn = destroyFcn; + this.m_registers[type2][type1].primary = false; + } + } + b2ContactFactory.prototype.InitializeRegisters = function() { + this.m_registers = new Vector(b2Shape.e_shapeTypeCount); + for(var i = 0; i < b2Shape.e_shapeTypeCount; i++) { + this.m_registers[i] = new Vector(b2Shape.e_shapeTypeCount); + for(var j = 0; j < b2Shape.e_shapeTypeCount; j++) { + this.m_registers[i][j] = new b2ContactRegister(); + } + } + this.AddType(b2CircleContact.Create, b2CircleContact.Destroy, b2Shape.e_circleShape, b2Shape.e_circleShape); + this.AddType(b2PolyAndCircleContact.Create, b2PolyAndCircleContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_circleShape); + this.AddType(b2PolygonContact.Create, b2PolygonContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_polygonShape); + this.AddType(b2EdgeAndCircleContact.Create, b2EdgeAndCircleContact.Destroy, b2Shape.e_edgeShape, b2Shape.e_circleShape); + this.AddType(b2PolyAndEdgeContact.Create, b2PolyAndEdgeContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_edgeShape); + } + b2ContactFactory.prototype.Create = function(fixtureA, fixtureB) { + var type1 = parseInt(fixtureA.GetType()); + var type2 = parseInt(fixtureB.GetType()); + var reg = this.m_registers[type1][type2]; + var c; + if(reg.pool) { + c = reg.pool; + reg.pool = c.m_next; + reg.poolCount--; + c.Reset(fixtureA, fixtureB); + return c; + } + var createFcn = reg.createFcn; + if(createFcn != null) { + if(reg.primary) { + c = createFcn(this.m_allocator); + c.Reset(fixtureA, fixtureB); + return c; + } else { + c = createFcn(this.m_allocator); + c.Reset(fixtureB, fixtureA); + return c; + } + } else { + return null; + } + } + b2ContactFactory.prototype.Destroy = function(contact) { + if(contact.m_manifold.m_pointCount > 0) { + contact.m_fixtureA.m_body.SetAwake(true); + contact.m_fixtureB.m_body.SetAwake(true); + } + var type1 = parseInt(contact.m_fixtureA.GetType()); + var type2 = parseInt(contact.m_fixtureB.GetType()); + var reg = this.m_registers[type1][type2]; + if(true) { + reg.poolCount++; + contact.m_next = reg.pool; + reg.pool = contact; + } + var destroyFcn = reg.destroyFcn; + destroyFcn(contact, this.m_allocator); + } + b2ContactRegister.b2ContactRegister = function() {}; + b2ContactResult.b2ContactResult = function() { + this.position = new b2Vec2(); + this.normal = new b2Vec2(); + this.id = new b2ContactID(); + }; + b2ContactSolver.b2ContactSolver = function() { + this.m_step = new b2TimeStep(); + this.m_constraints = new Vector(); + }; + b2ContactSolver.prototype.b2ContactSolver = function() {} + b2ContactSolver.prototype.Initialize = function(step, contacts, contactCount, allocator) { + if(contactCount === undefined) contactCount = 0; + var contact; + this.m_step.Set(step); + this.m_allocator = allocator; + var i = 0; + var tVec; + var tMat; + this.m_constraintCount = contactCount; + while(this.m_constraints.length < this.m_constraintCount) { + this.m_constraints[this.m_constraints.length] = new b2ContactConstraint(); + } + for(i = 0; i < contactCount; ++i) { + contact = contacts[i]; + var fixtureA = contact.m_fixtureA; + var fixtureB = contact.m_fixtureB; + var shapeA = fixtureA.m_shape; + var shapeB = fixtureB.m_shape; + var radiusA = shapeA.m_radius; + var radiusB = shapeB.m_radius; + var bodyA = fixtureA.m_body; + var bodyB = fixtureB.m_body; + var manifold = contact.GetManifold(); + var friction = b2Settings.b2MixFriction(fixtureA.GetFriction(), fixtureB.GetFriction()); + var restitution = b2Settings.b2MixRestitution(fixtureA.GetRestitution(), fixtureB.GetRestitution()); + var vAX = bodyA.m_linearVelocity.x; + var vAY = bodyA.m_linearVelocity.y; + var vBX = bodyB.m_linearVelocity.x; + var vBY = bodyB.m_linearVelocity.y; + var wA = bodyA.m_angularVelocity; + var wB = bodyB.m_angularVelocity; + b2Settings.b2Assert(manifold.m_pointCount > 0); + b2ContactSolver.s_worldManifold.Initialize(manifold, bodyA.m_xf, radiusA, bodyB.m_xf, radiusB); + var normalX = b2ContactSolver.s_worldManifold.m_normal.x; + var normalY = b2ContactSolver.s_worldManifold.m_normal.y; + var cc = this.m_constraints[i]; + cc.bodyA = bodyA; + cc.bodyB = bodyB; + cc.manifold = manifold; + cc.normal.x = normalX; + cc.normal.y = normalY; + cc.pointCount = manifold.m_pointCount; + cc.friction = friction; + cc.restitution = restitution; + cc.localPlaneNormal.x = manifold.m_localPlaneNormal.x; + cc.localPlaneNormal.y = manifold.m_localPlaneNormal.y; + cc.localPoint.x = manifold.m_localPoint.x; + cc.localPoint.y = manifold.m_localPoint.y; + cc.radius = radiusA + radiusB; + cc.type = manifold.m_type; + for(var k = 0; k < cc.pointCount; ++k) { + var cp = manifold.m_points[k]; + var ccp = cc.points[k]; + ccp.normalImpulse = cp.m_normalImpulse; + ccp.tangentImpulse = cp.m_tangentImpulse; + ccp.localPoint.SetV(cp.m_localPoint); + var rAX = ccp.rA.x = b2ContactSolver.s_worldManifold.m_points[k].x - bodyA.m_sweep.c.x; + var rAY = ccp.rA.y = b2ContactSolver.s_worldManifold.m_points[k].y - bodyA.m_sweep.c.y; + var rBX = ccp.rB.x = b2ContactSolver.s_worldManifold.m_points[k].x - bodyB.m_sweep.c.x; + var rBY = ccp.rB.y = b2ContactSolver.s_worldManifold.m_points[k].y - bodyB.m_sweep.c.y; + var rnA = rAX * normalY - rAY * normalX; + var rnB = rBX * normalY - rBY * normalX; + rnA *= rnA; + rnB *= rnB; + var kNormal = bodyA.m_invMass + bodyB.m_invMass + bodyA.m_invI * rnA + bodyB.m_invI * rnB; + ccp.normalMass = 1.0 / kNormal; + var kEqualized = bodyA.m_mass * bodyA.m_invMass + bodyB.m_mass * bodyB.m_invMass; + kEqualized += bodyA.m_mass * bodyA.m_invI * rnA + bodyB.m_mass * bodyB.m_invI * rnB; + ccp.equalizedMass = 1.0 / kEqualized; + var tangentX = normalY; + var tangentY = (-normalX); + var rtA = rAX * tangentY - rAY * tangentX; + var rtB = rBX * tangentY - rBY * tangentX; + rtA *= rtA; + rtB *= rtB; + var kTangent = bodyA.m_invMass + bodyB.m_invMass + bodyA.m_invI * rtA + bodyB.m_invI * rtB; + ccp.tangentMass = 1.0 / kTangent; + ccp.velocityBias = 0.0; + var tX = vBX + ((-wB * rBY)) - vAX - ((-wA * rAY)); + var tY = vBY + (wB * rBX) - vAY - (wA * rAX); + var vRel = cc.normal.x * tX + cc.normal.y * tY; + if(vRel < (-b2Settings.b2_velocityThreshold)) { + ccp.velocityBias += (-cc.restitution * vRel); + } + } + if(cc.pointCount == 2) { + var ccp1 = cc.points[0]; + var ccp2 = cc.points[1]; + var invMassA = bodyA.m_invMass; + var invIA = bodyA.m_invI; + var invMassB = bodyB.m_invMass; + var invIB = bodyB.m_invI; + var rn1A = ccp1.rA.x * normalY - ccp1.rA.y * normalX; + var rn1B = ccp1.rB.x * normalY - ccp1.rB.y * normalX; + var rn2A = ccp2.rA.x * normalY - ccp2.rA.y * normalX; + var rn2B = ccp2.rB.x * normalY - ccp2.rB.y * normalX; + var k11 = invMassA + invMassB + invIA * rn1A * rn1A + invIB * rn1B * rn1B; + var k22 = invMassA + invMassB + invIA * rn2A * rn2A + invIB * rn2B * rn2B; + var k12 = invMassA + invMassB + invIA * rn1A * rn2A + invIB * rn1B * rn2B; + var k_maxConditionNumber = 100.0; + if(k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) { + cc.K.col1.Set(k11, k12); + cc.K.col2.Set(k12, k22); + cc.K.GetInverse(cc.normalMass); + } else { + cc.pointCount = 1; + } + } + } + } + b2ContactSolver.prototype.InitVelocityConstraints = function(step) { + var tVec; + var tVec2; + var tMat; + for(var i = 0; i < this.m_constraintCount; ++i) { + var c = this.m_constraints[i]; + var bodyA = c.bodyA; + var bodyB = c.bodyB; + var invMassA = bodyA.m_invMass; + var invIA = bodyA.m_invI; + var invMassB = bodyB.m_invMass; + var invIB = bodyB.m_invI; + var normalX = c.normal.x; + var normalY = c.normal.y; + var tangentX = normalY; + var tangentY = (-normalX); + var tX = 0; + var j = 0; + var tCount = 0; + if(step.warmStarting) { + tCount = c.pointCount; + for(j = 0; j < tCount; ++j) { + var ccp = c.points[j]; + ccp.normalImpulse *= step.dtRatio; + ccp.tangentImpulse *= step.dtRatio; + var PX = ccp.normalImpulse * normalX + ccp.tangentImpulse * tangentX; + var PY = ccp.normalImpulse * normalY + ccp.tangentImpulse * tangentY; + bodyA.m_angularVelocity -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX); + bodyA.m_linearVelocity.x -= invMassA * PX; + bodyA.m_linearVelocity.y -= invMassA * PY; + bodyB.m_angularVelocity += invIB * (ccp.rB.x * PY - ccp.rB.y * PX); + bodyB.m_linearVelocity.x += invMassB * PX; + bodyB.m_linearVelocity.y += invMassB * PY; + } + } else { + tCount = c.pointCount; + for(j = 0; j < tCount; ++j) { + var ccp2 = c.points[j]; + ccp2.normalImpulse = 0.0; + ccp2.tangentImpulse = 0.0; + } + } + } + } + b2ContactSolver.prototype.SolveVelocityConstraints = function() { + var j = 0; + var ccp; + var rAX = 0; + var rAY = 0; + var rBX = 0; + var rBY = 0; + var dvX = 0; + var dvY = 0; + var vn = 0; + var vt = 0; + var lambda = 0; + var maxFriction = 0; + var newImpulse = 0; + var PX = 0; + var PY = 0; + var dX = 0; + var dY = 0; + var P1X = 0; + var P1Y = 0; + var P2X = 0; + var P2Y = 0; + var tMat; + var tVec; + for(var i = 0; i < this.m_constraintCount; ++i) { + var c = this.m_constraints[i]; + var bodyA = c.bodyA; + var bodyB = c.bodyB; + var wA = bodyA.m_angularVelocity; + var wB = bodyB.m_angularVelocity; + var vA = bodyA.m_linearVelocity; + var vB = bodyB.m_linearVelocity; + var invMassA = bodyA.m_invMass; + var invIA = bodyA.m_invI; + var invMassB = bodyB.m_invMass; + var invIB = bodyB.m_invI; + var normalX = c.normal.x; + var normalY = c.normal.y; + var tangentX = normalY; + var tangentY = (-normalX); + var friction = c.friction; + var tX = 0; + for(j = 0; j < c.pointCount; j++) { + ccp = c.points[j]; + dvX = vB.x - wB * ccp.rB.y - vA.x + wA * ccp.rA.y; + dvY = vB.y + wB * ccp.rB.x - vA.y - wA * ccp.rA.x; + vt = dvX * tangentX + dvY * tangentY; + lambda = ccp.tangentMass * (-vt); + maxFriction = friction * ccp.normalImpulse; + newImpulse = b2Math.Clamp(ccp.tangentImpulse + lambda, (-maxFriction), maxFriction); + lambda = newImpulse - ccp.tangentImpulse; + PX = lambda * tangentX; + PY = lambda * tangentY; + vA.x -= invMassA * PX; + vA.y -= invMassA * PY; + wA -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX); + vB.x += invMassB * PX; + vB.y += invMassB * PY; + wB += invIB * (ccp.rB.x * PY - ccp.rB.y * PX); + ccp.tangentImpulse = newImpulse; + } + var tCount = parseInt(c.pointCount); + if(c.pointCount == 1) { + ccp = c.points[0]; + dvX = vB.x + ((-wB * ccp.rB.y)) - vA.x - ((-wA * ccp.rA.y)); + dvY = vB.y + (wB * ccp.rB.x) - vA.y - (wA * ccp.rA.x); + vn = dvX * normalX + dvY * normalY; + lambda = (-ccp.normalMass * (vn - ccp.velocityBias)); + newImpulse = ccp.normalImpulse + lambda; + newImpulse = newImpulse > 0 ? newImpulse : 0.0; + lambda = newImpulse - ccp.normalImpulse; + PX = lambda * normalX; + PY = lambda * normalY; + vA.x -= invMassA * PX; + vA.y -= invMassA * PY; + wA -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX); + vB.x += invMassB * PX; + vB.y += invMassB * PY; + wB += invIB * (ccp.rB.x * PY - ccp.rB.y * PX); + ccp.normalImpulse = newImpulse; + } else { + var cp1 = c.points[0]; + var cp2 = c.points[1]; + var aX = cp1.normalImpulse; + var aY = cp2.normalImpulse; + var dv1X = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y; + var dv1Y = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x; + var dv2X = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y; + var dv2Y = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x; + var vn1 = dv1X * normalX + dv1Y * normalY; + var vn2 = dv2X * normalX + dv2Y * normalY; + var bX = vn1 - cp1.velocityBias; + var bY = vn2 - cp2.velocityBias; + tMat = c.K; + bX -= tMat.col1.x * aX + tMat.col2.x * aY; + bY -= tMat.col1.y * aX + tMat.col2.y * aY; + var k_errorTol = 0.001; + for(;;) { + tMat = c.normalMass; + var xX = (-(tMat.col1.x * bX + tMat.col2.x * bY)); + var xY = (-(tMat.col1.y * bX + tMat.col2.y * bY)); + if(xX >= 0.0 && xY >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + xX = (-cp1.normalMass * bX); + xY = 0.0; + vn1 = 0.0; + vn2 = c.K.col1.y * xX + bY; + if(xX >= 0.0 && vn2 >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + xX = 0.0; + xY = (-cp2.normalMass * bY); + vn1 = c.K.col2.x * xY + bX; + vn2 = 0.0; + if(xY >= 0.0 && vn1 >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + xX = 0.0; + xY = 0.0; + vn1 = bX; + vn2 = bY; + if(vn1 >= 0.0 && vn2 >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + break; + } + } + bodyA.m_angularVelocity = wA; + bodyB.m_angularVelocity = wB; + } + } + b2ContactSolver.prototype.FinalizeVelocityConstraints = function() { + for(var i = 0; i < this.m_constraintCount; ++i) { + var c = this.m_constraints[i]; + var m = c.manifold; + for(var j = 0; j < c.pointCount; ++j) { + var point1 = m.m_points[j]; + var point2 = c.points[j]; + point1.m_normalImpulse = point2.normalImpulse; + point1.m_tangentImpulse = point2.tangentImpulse; + } + } + } + b2ContactSolver.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var minSeparation = 0.0; + for(var i = 0; i < this.m_constraintCount; i++) { + var c = this.m_constraints[i]; + var bodyA = c.bodyA; + var bodyB = c.bodyB; + var invMassA = bodyA.m_mass * bodyA.m_invMass; + var invIA = bodyA.m_mass * bodyA.m_invI; + var invMassB = bodyB.m_mass * bodyB.m_invMass; + var invIB = bodyB.m_mass * bodyB.m_invI; + b2ContactSolver.s_psm.Initialize(c); + var normal = b2ContactSolver.s_psm.m_normal; + for(var j = 0; j < c.pointCount; j++) { + var ccp = c.points[j]; + var point = b2ContactSolver.s_psm.m_points[j]; + var separation = b2ContactSolver.s_psm.m_separations[j]; + var rAX = point.x - bodyA.m_sweep.c.x; + var rAY = point.y - bodyA.m_sweep.c.y; + var rBX = point.x - bodyB.m_sweep.c.x; + var rBY = point.y - bodyB.m_sweep.c.y; + minSeparation = minSeparation < separation ? minSeparation : separation; + var C = b2Math.Clamp(baumgarte * (separation + b2Settings.b2_linearSlop), (-b2Settings.b2_maxLinearCorrection), 0.0); + var impulse = (-ccp.equalizedMass * C); + var PX = impulse * normal.x; + var PY = impulse * normal.y; + bodyA.m_sweep.c.x -= invMassA * PX; + bodyA.m_sweep.c.y -= invMassA * PY; + bodyA.m_sweep.a -= invIA * (rAX * PY - rAY * PX); + bodyA.SynchronizeTransform(); + bodyB.m_sweep.c.x += invMassB * PX; + bodyB.m_sweep.c.y += invMassB * PY; + bodyB.m_sweep.a += invIB * (rBX * PY - rBY * PX); + bodyB.SynchronizeTransform(); + } + } + return minSeparation > (-1.5 * b2Settings.b2_linearSlop); + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.Contacts.b2ContactSolver.s_worldManifold = new b2WorldManifold(); + Box2D.Dynamics.Contacts.b2ContactSolver.s_psm = new b2PositionSolverManifold(); + }); + Box2D.inherit(b2EdgeAndCircleContact, Box2D.Dynamics.Contacts.b2Contact); + b2EdgeAndCircleContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2EdgeAndCircleContact.b2EdgeAndCircleContact = function() { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2EdgeAndCircleContact.Create = function(allocator) { + return new b2EdgeAndCircleContact(); + } + b2EdgeAndCircleContact.Destroy = function(contact, allocator) {} + b2EdgeAndCircleContact.prototype.Reset = function(fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + } + b2EdgeAndCircleContact.prototype.Evaluate = function() { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + this.b2CollideEdgeAndCircle(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2EdgeShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2CircleShape ? + this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2EdgeAndCircleContact.prototype.b2CollideEdgeAndCircle = function(manifold, edge, xf1, circle, xf2) {} + Box2D.inherit(b2NullContact, Box2D.Dynamics.Contacts.b2Contact); + b2NullContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2NullContact.b2NullContact = function() { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2NullContact.prototype.b2NullContact = function() { + this.__super.b2Contact.call(this); + } + b2NullContact.prototype.Evaluate = function() {} + Box2D.inherit(b2PolyAndCircleContact, Box2D.Dynamics.Contacts.b2Contact); + b2PolyAndCircleContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2PolyAndCircleContact.b2PolyAndCircleContact = function() { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2PolyAndCircleContact.Create = function(allocator) { + return new b2PolyAndCircleContact(); + } + b2PolyAndCircleContact.Destroy = function(contact, allocator) {} + b2PolyAndCircleContact.prototype.Reset = function(fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + b2Settings.b2Assert(fixtureA.GetType() == b2Shape.e_polygonShape); + b2Settings.b2Assert(fixtureB.GetType() == b2Shape.e_circleShape); + } + b2PolyAndCircleContact.prototype.Evaluate = function() { + var bA = this.m_fixtureA.m_body; + var bB = this.m_fixtureB.m_body; + b2Collision.CollidePolygonAndCircle(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2PolygonShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2CircleShape ? + this.m_fixtureB.GetShape() : null), bB.m_xf); + } + Box2D.inherit(b2PolyAndEdgeContact, Box2D.Dynamics.Contacts.b2Contact); + b2PolyAndEdgeContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2PolyAndEdgeContact.b2PolyAndEdgeContact = function() { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2PolyAndEdgeContact.Create = function(allocator) { + return new b2PolyAndEdgeContact(); + } + b2PolyAndEdgeContact.Destroy = function(contact, allocator) {} + b2PolyAndEdgeContact.prototype.Reset = function(fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + b2Settings.b2Assert(fixtureA.GetType() == b2Shape.e_polygonShape); + b2Settings.b2Assert(fixtureB.GetType() == b2Shape.e_edgeShape); + } + b2PolyAndEdgeContact.prototype.Evaluate = function() { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + this.b2CollidePolyAndEdge(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2PolygonShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2EdgeShape ? + this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2PolyAndEdgeContact.prototype.b2CollidePolyAndEdge = function(manifold, polygon, xf1, edge, xf2) {} + Box2D.inherit(b2PolygonContact, Box2D.Dynamics.Contacts.b2Contact); + b2PolygonContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2PolygonContact.b2PolygonContact = function() { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2PolygonContact.Create = function(allocator) { + return new b2PolygonContact(); + } + b2PolygonContact.Destroy = function(contact, allocator) {} + b2PolygonContact.prototype.Reset = function(fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + } + b2PolygonContact.prototype.Evaluate = function() { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + b2Collision.CollidePolygons(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2PolygonShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2PolygonShape ? + this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2PositionSolverManifold.b2PositionSolverManifold = function() {}; + b2PositionSolverManifold.prototype.b2PositionSolverManifold = function() { + this.m_normal = new b2Vec2(); + this.m_separations = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints); + this.m_points = new Vector(b2Settings.b2_maxManifoldPoints); + for(var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.m_points[i] = new b2Vec2(); + } + } + b2PositionSolverManifold.prototype.Initialize = function(cc) { + b2Settings.b2Assert(cc.pointCount > 0); + var i = 0; + var clipPointX = 0; + var clipPointY = 0; + var tMat; + var tVec; + var planePointX = 0; + var planePointY = 0; + switch(cc.type) { + case b2Manifold.e_circles: + { + tMat = cc.bodyA.m_xf.R; + tVec = cc.localPoint; + var pointAX = cc.bodyA.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var pointAY = cc.bodyA.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = cc.bodyB.m_xf.R; + tVec = cc.points[0].localPoint; + var pointBX = cc.bodyB.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var pointBY = cc.bodyB.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var dX = pointBX - pointAX; + var dY = pointBY - pointAY; + var d2 = dX * dX + dY * dY; + if(d2 > Number.MIN_VALUE * Number.MIN_VALUE) { + var d = Math.sqrt(d2); + this.m_normal.x = dX / d; + this.m_normal.y = dY / d; + } else { + this.m_normal.x = 1.0; + this.m_normal.y = 0.0; + } + this.m_points[0].x = 0.5 * (pointAX + pointBX); + this.m_points[0].y = 0.5 * (pointAY + pointBY); + this.m_separations[0] = dX * this.m_normal.x + dY * this.m_normal.y - cc.radius; + } + break; + case b2Manifold.e_faceA: + { + tMat = cc.bodyA.m_xf.R; + tVec = cc.localPlaneNormal; + this.m_normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + this.m_normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = cc.bodyA.m_xf.R; + tVec = cc.localPoint; + planePointX = cc.bodyA.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + planePointY = cc.bodyA.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = cc.bodyB.m_xf.R; + for(i = 0; i < cc.pointCount; ++i) { + tVec = cc.points[i].localPoint; + clipPointX = cc.bodyB.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + clipPointY = cc.bodyB.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_separations[i] = (clipPointX - planePointX) * this.m_normal.x + (clipPointY - planePointY) * this.m_normal.y - cc.radius; + this.m_points[i].x = clipPointX; + this.m_points[i].y = clipPointY; + } + } + break; + case b2Manifold.e_faceB: + { + tMat = cc.bodyB.m_xf.R; + tVec = cc.localPlaneNormal; + this.m_normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + this.m_normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = cc.bodyB.m_xf.R; + tVec = cc.localPoint; + planePointX = cc.bodyB.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + planePointY = cc.bodyB.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = cc.bodyA.m_xf.R; + for(i = 0; i < cc.pointCount; ++i) { + tVec = cc.points[i].localPoint; + clipPointX = cc.bodyA.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + clipPointY = cc.bodyA.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_separations[i] = (clipPointX - planePointX) * this.m_normal.x + (clipPointY - planePointY) * this.m_normal.y - cc.radius; + this.m_points[i].Set(clipPointX, clipPointY); + } + this.m_normal.x *= (-1); + this.m_normal.y *= (-1); + } + break; + } + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.Contacts.b2PositionSolverManifold.circlePointA = new b2Vec2(); + Box2D.Dynamics.Contacts.b2PositionSolverManifold.circlePointB = new b2Vec2(); + }); +})(); +(function() { + var b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2BuoyancyController = Box2D.Dynamics.Controllers.b2BuoyancyController, + b2ConstantAccelController = Box2D.Dynamics.Controllers.b2ConstantAccelController, + b2ConstantForceController = Box2D.Dynamics.Controllers.b2ConstantForceController, + b2Controller = Box2D.Dynamics.Controllers.b2Controller, + b2ControllerEdge = Box2D.Dynamics.Controllers.b2ControllerEdge, + b2GravityController = Box2D.Dynamics.Controllers.b2GravityController, + b2TensorDampingController = Box2D.Dynamics.Controllers.b2TensorDampingController; + + Box2D.inherit(b2BuoyancyController, Box2D.Dynamics.Controllers.b2Controller); + b2BuoyancyController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2BuoyancyController.b2BuoyancyController = function() { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.normal = new b2Vec2(0, (-1)); + this.offset = 0; + this.density = 0; + this.velocity = new b2Vec2(0, 0); + this.linearDrag = 2; + this.angularDrag = 1; + this.useDensity = false; + this.useWorldGravity = true; + this.gravity = null; + }; + b2BuoyancyController.prototype.Step = function(step) { + if(!this.m_bodyList) return; + if(this.useWorldGravity) { + this.gravity = this.GetWorld().GetGravity().Copy(); + } + for(var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if(body.IsAwake() == false) { + continue; + } + var areac = new b2Vec2(); + var massc = new b2Vec2(); + var area = 0.0; + var mass = 0.0; + for(var fixture = body.GetFixtureList(); fixture; fixture = fixture.GetNext()) { + var sc = new b2Vec2(); + var sarea = fixture.GetShape().ComputeSubmergedArea(this.normal, this.offset, body.GetTransform(), sc); + area += sarea; + areac.x += sarea * sc.x; + areac.y += sarea * sc.y; + var shapeDensity = 0; + if(this.useDensity) { + shapeDensity = 1; + } else { + shapeDensity = 1; + } + mass += sarea * shapeDensity; + massc.x += sarea * sc.x * shapeDensity; + massc.y += sarea * sc.y * shapeDensity; + } + areac.x /= area; + areac.y /= area; + massc.x /= mass; + massc.y /= mass; + if(area < Number.MIN_VALUE) continue; + var buoyancyForce = this.gravity.GetNegative(); + buoyancyForce.Multiply(this.density * area); + body.ApplyForce(buoyancyForce, massc); + var dragForce = body.GetLinearVelocityFromWorldPoint(areac); + dragForce.Subtract(this.velocity); + dragForce.Multiply((-this.linearDrag * area)); + body.ApplyForce(dragForce, areac); + body.ApplyTorque((-body.GetInertia() / body.GetMass() * area * body.GetAngularVelocity() * this.angularDrag)); + } + } + b2BuoyancyController.prototype.Draw = function(debugDraw) { + var r = 1000; + var p1 = new b2Vec2(); + var p2 = new b2Vec2(); + p1.x = this.normal.x * this.offset + this.normal.y * r; + p1.y = this.normal.y * this.offset - this.normal.x * r; + p2.x = this.normal.x * this.offset - this.normal.y * r; + p2.y = this.normal.y * this.offset + this.normal.x * r; + var color = new b2Color(0, 0, 1); + debugDraw.DrawSegment(p1, p2, color); + } + Box2D.inherit(b2ConstantAccelController, Box2D.Dynamics.Controllers.b2Controller); + b2ConstantAccelController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2ConstantAccelController.b2ConstantAccelController = function() { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.A = new b2Vec2(0, 0); + }; + b2ConstantAccelController.prototype.Step = function(step) { + var smallA = new b2Vec2(this.A.x * step.dt, this.A.y * step.dt); + for(var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if(!body.IsAwake()) continue; + body.SetLinearVelocity(new b2Vec2(body.GetLinearVelocity().x + smallA.x, body.GetLinearVelocity().y + smallA.y)); + } + } + Box2D.inherit(b2ConstantForceController, Box2D.Dynamics.Controllers.b2Controller); + b2ConstantForceController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2ConstantForceController.b2ConstantForceController = function() { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.F = new b2Vec2(0, 0); + }; + b2ConstantForceController.prototype.Step = function(step) { + for(var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if(!body.IsAwake()) continue; + body.ApplyForce(this.F, body.GetWorldCenter()); + } + } + b2Controller.b2Controller = function() {}; + b2Controller.prototype.Step = function(step) {} + b2Controller.prototype.Draw = function(debugDraw) {} + b2Controller.prototype.AddBody = function(body) { + var edge = new b2ControllerEdge(); + edge.controller = this; + edge.body = body; + edge.nextBody = this.m_bodyList; + edge.prevBody = null; + this.m_bodyList = edge; + if(edge.nextBody) edge.nextBody.prevBody = edge; + this.m_bodyCount++; + edge.nextController = body.m_controllerList; + edge.prevController = null; + body.m_controllerList = edge; + if(edge.nextController) edge.nextController.prevController = edge; + body.m_controllerCount++; + } + b2Controller.prototype.RemoveBody = function(body) { + var edge = body.m_controllerList; + while(edge && edge.controller != this) + edge = edge.nextController; + if(edge.prevBody) edge.prevBody.nextBody = edge.nextBody; + if(edge.nextBody) edge.nextBody.prevBody = edge.prevBody; + if(edge.nextController) edge.nextController.prevController = edge.prevController; + if(edge.prevController) edge.prevController.nextController = edge.nextController; + if(this.m_bodyList == edge) this.m_bodyList = edge.nextBody; + if(body.m_controllerList == edge) body.m_controllerList = edge.nextController; + body.m_controllerCount--; + this.m_bodyCount--; + } + b2Controller.prototype.Clear = function() { + while(this.m_bodyList) + this.RemoveBody(this.m_bodyList.body); + } + b2Controller.prototype.GetNext = function() { + return this.m_next; + } + b2Controller.prototype.GetWorld = function() { + return this.m_world; + } + b2Controller.prototype.GetBodyList = function() { + return this.m_bodyList; + } + b2ControllerEdge.b2ControllerEdge = function() {}; + Box2D.inherit(b2GravityController, Box2D.Dynamics.Controllers.b2Controller); + b2GravityController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2GravityController.b2GravityController = function() { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.G = 1; + this.invSqr = true; + }; + b2GravityController.prototype.Step = function(step) { + var i = null; + var body1 = null; + var p1 = null; + var mass1 = 0; + var j = null; + var body2 = null; + var p2 = null; + var dx = 0; + var dy = 0; + var r2 = 0; + var f = null; + if(this.invSqr) { + for(i = this.m_bodyList; i; i = i.nextBody) { + body1 = i.body; + p1 = body1.GetWorldCenter(); + mass1 = body1.GetMass(); + for(j = this.m_bodyList; j != i; j = j.nextBody) { + body2 = j.body; + p2 = body2.GetWorldCenter(); + dx = p2.x - p1.x; + dy = p2.y - p1.y; + r2 = dx * dx + dy * dy; + if(r2 < Number.MIN_VALUE) continue; + f = new b2Vec2(dx, dy); + f.Multiply(this.G / r2 / Math.sqrt(r2) * mass1 * body2.GetMass()); + if(body1.IsAwake()) body1.ApplyForce(f, p1); + f.Multiply((-1)); + if(body2.IsAwake()) body2.ApplyForce(f, p2); + } + } + } else { + for(i = this.m_bodyList; i; i = i.nextBody) { + body1 = i.body; + p1 = body1.GetWorldCenter(); + mass1 = body1.GetMass(); + for(j = this.m_bodyList; j != i; j = j.nextBody) { + body2 = j.body; + p2 = body2.GetWorldCenter(); + dx = p2.x - p1.x; + dy = p2.y - p1.y; + r2 = dx * dx + dy * dy; + if(r2 < Number.MIN_VALUE) continue; + f = new b2Vec2(dx, dy); + f.Multiply(this.G / r2 * mass1 * body2.GetMass()); + if(body1.IsAwake()) body1.ApplyForce(f, p1); + f.Multiply((-1)); + if(body2.IsAwake()) body2.ApplyForce(f, p2); + } + } + } + } + Box2D.inherit(b2TensorDampingController, Box2D.Dynamics.Controllers.b2Controller); + b2TensorDampingController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2TensorDampingController.b2TensorDampingController = function() { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.T = new b2Mat22(); + this.maxTimestep = 0; + }; + b2TensorDampingController.prototype.SetAxisAligned = function(xDamping, yDamping) { + if(xDamping === undefined) xDamping = 0; + if(yDamping === undefined) yDamping = 0; + this.T.col1.x = (-xDamping); + this.T.col1.y = 0; + this.T.col2.x = 0; + this.T.col2.y = (-yDamping); + if(xDamping > 0 || yDamping > 0) { + this.maxTimestep = 1 / Math.max(xDamping, yDamping); + } else { + this.maxTimestep = 0; + } + } + b2TensorDampingController.prototype.Step = function(step) { + var timestep = step.dt; + if(timestep <= Number.MIN_VALUE) return; + if(timestep > this.maxTimestep && this.maxTimestep > 0) timestep = this.maxTimestep; + for(var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if(!body.IsAwake()) { + continue; + } + var damping = body.GetWorldVector(b2Math.MulMV(this.T, body.GetLocalVector(body.GetLinearVelocity()))); + body.SetLinearVelocity(new b2Vec2(body.GetLinearVelocity().x + damping.x * timestep, body.GetLinearVelocity().y + damping.y * timestep)); + } + } +})(); +(function() { + var b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2DistanceJoint = Box2D.Dynamics.Joints.b2DistanceJoint, + b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef, + b2FrictionJoint = Box2D.Dynamics.Joints.b2FrictionJoint, + b2FrictionJointDef = Box2D.Dynamics.Joints.b2FrictionJointDef, + b2GearJoint = Box2D.Dynamics.Joints.b2GearJoint, + b2GearJointDef = Box2D.Dynamics.Joints.b2GearJointDef, + b2Jacobian = Box2D.Dynamics.Joints.b2Jacobian, + b2Joint = Box2D.Dynamics.Joints.b2Joint, + b2JointDef = Box2D.Dynamics.Joints.b2JointDef, + b2JointEdge = Box2D.Dynamics.Joints.b2JointEdge, + b2LineJoint = Box2D.Dynamics.Joints.b2LineJoint, + b2LineJointDef = Box2D.Dynamics.Joints.b2LineJointDef, + b2MouseJoint = Box2D.Dynamics.Joints.b2MouseJoint, + b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef, + b2PrismaticJoint = Box2D.Dynamics.Joints.b2PrismaticJoint, + b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef, + b2PulleyJoint = Box2D.Dynamics.Joints.b2PulleyJoint, + b2PulleyJointDef = Box2D.Dynamics.Joints.b2PulleyJointDef, + b2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint, + b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef, + b2WeldJoint = Box2D.Dynamics.Joints.b2WeldJoint, + b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World; + + Box2D.inherit(b2DistanceJoint, Box2D.Dynamics.Joints.b2Joint); + b2DistanceJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2DistanceJoint.b2DistanceJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_u = new b2Vec2(); + }; + b2DistanceJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2DistanceJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2DistanceJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse * this.m_u.x, inv_dt * this.m_impulse * this.m_u.y); + } + b2DistanceJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2DistanceJoint.prototype.GetLength = function() { + return this.m_length; + } + b2DistanceJoint.prototype.SetLength = function(length) { + if(length === undefined) length = 0; + this.m_length = length; + } + b2DistanceJoint.prototype.GetFrequency = function() { + return this.m_frequencyHz; + } + b2DistanceJoint.prototype.SetFrequency = function(hz) { + if(hz === undefined) hz = 0; + this.m_frequencyHz = hz; + } + b2DistanceJoint.prototype.GetDampingRatio = function() { + return this.m_dampingRatio; + } + b2DistanceJoint.prototype.SetDampingRatio = function(ratio) { + if(ratio === undefined) ratio = 0; + this.m_dampingRatio = ratio; + } + b2DistanceJoint.prototype.b2DistanceJoint = function(def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_length = def.length; + this.m_frequencyHz = def.frequencyHz; + this.m_dampingRatio = def.dampingRatio; + this.m_impulse = 0.0; + this.m_gamma = 0.0; + this.m_bias = 0.0; + } + b2DistanceJoint.prototype.InitVelocityConstraints = function(step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + this.m_u.x = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + this.m_u.y = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + var length = Math.sqrt(this.m_u.x * this.m_u.x + this.m_u.y * this.m_u.y); + if(length > b2Settings.b2_linearSlop) { + this.m_u.Multiply(1.0 / length); + } else { + this.m_u.SetZero(); + } + var cr1u = (r1X * this.m_u.y - r1Y * this.m_u.x); + var cr2u = (r2X * this.m_u.y - r2Y * this.m_u.x); + var invMass = bA.m_invMass + bA.m_invI * cr1u * cr1u + bB.m_invMass + bB.m_invI * cr2u * cr2u; + this.m_mass = invMass != 0.0 ? 1.0 / invMass : 0.0; + if(this.m_frequencyHz > 0.0) { + var C = length - this.m_length; + var omega = 2.0 * Math.PI * this.m_frequencyHz; + var d = 2.0 * this.m_mass * this.m_dampingRatio * omega; + var k = this.m_mass * omega * omega; + this.m_gamma = step.dt * (d + step.dt * k); + this.m_gamma = this.m_gamma != 0.0 ? 1 / this.m_gamma : 0.0; + this.m_bias = C * step.dt * k * this.m_gamma; + this.m_mass = invMass + this.m_gamma; + this.m_mass = this.m_mass != 0.0 ? 1.0 / this.m_mass : 0.0; + } + if(step.warmStarting) { + this.m_impulse *= step.dtRatio; + var PX = this.m_impulse * this.m_u.x; + var PY = this.m_impulse * this.m_u.y; + bA.m_linearVelocity.x -= bA.m_invMass * PX; + bA.m_linearVelocity.y -= bA.m_invMass * PY; + bA.m_angularVelocity -= bA.m_invI * (r1X * PY - r1Y * PX); + bB.m_linearVelocity.x += bB.m_invMass * PX; + bB.m_linearVelocity.y += bB.m_invMass * PY; + bB.m_angularVelocity += bB.m_invI * (r2X * PY - r2Y * PX); + } else { + this.m_impulse = 0.0; + } + } + b2DistanceJoint.prototype.SolveVelocityConstraints = function(step) { + var tMat; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var v1X = bA.m_linearVelocity.x + ((-bA.m_angularVelocity * r1Y)); + var v1Y = bA.m_linearVelocity.y + (bA.m_angularVelocity * r1X); + var v2X = bB.m_linearVelocity.x + ((-bB.m_angularVelocity * r2Y)); + var v2Y = bB.m_linearVelocity.y + (bB.m_angularVelocity * r2X); + var Cdot = (this.m_u.x * (v2X - v1X) + this.m_u.y * (v2Y - v1Y)); + var impulse = (-this.m_mass * (Cdot + this.m_bias + this.m_gamma * this.m_impulse)); + this.m_impulse += impulse; + var PX = impulse * this.m_u.x; + var PY = impulse * this.m_u.y; + bA.m_linearVelocity.x -= bA.m_invMass * PX; + bA.m_linearVelocity.y -= bA.m_invMass * PY; + bA.m_angularVelocity -= bA.m_invI * (r1X * PY - r1Y * PX); + bB.m_linearVelocity.x += bB.m_invMass * PX; + bB.m_linearVelocity.y += bB.m_invMass * PY; + bB.m_angularVelocity += bB.m_invI * (r2X * PY - r2Y * PX); + } + b2DistanceJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var tMat; + if(this.m_frequencyHz > 0.0) { + return true; + } + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var dY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + var length = Math.sqrt(dX * dX + dY * dY); + dX /= length; + dY /= length; + var C = length - this.m_length; + C = b2Math.Clamp(C, (-b2Settings.b2_maxLinearCorrection), b2Settings.b2_maxLinearCorrection); + var impulse = (-this.m_mass * C); + this.m_u.Set(dX, dY); + var PX = impulse * this.m_u.x; + var PY = impulse * this.m_u.y; + bA.m_sweep.c.x -= bA.m_invMass * PX; + bA.m_sweep.c.y -= bA.m_invMass * PY; + bA.m_sweep.a -= bA.m_invI * (r1X * PY - r1Y * PX); + bB.m_sweep.c.x += bB.m_invMass * PX; + bB.m_sweep.c.y += bB.m_invMass * PY; + bB.m_sweep.a += bB.m_invI * (r2X * PY - r2Y * PX); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return b2Math.Abs(C) < b2Settings.b2_linearSlop; + } + Box2D.inherit(b2DistanceJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2DistanceJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2DistanceJointDef.b2DistanceJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2DistanceJointDef.prototype.b2DistanceJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_distanceJoint; + this.length = 1.0; + this.frequencyHz = 0.0; + this.dampingRatio = 0.0; + } + b2DistanceJointDef.prototype.Initialize = function(bA, bB, anchorA, anchorB) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA.SetV(this.bodyA.GetLocalPoint(anchorA)); + this.localAnchorB.SetV(this.bodyB.GetLocalPoint(anchorB)); + var dX = anchorB.x - anchorA.x; + var dY = anchorB.y - anchorA.y; + this.length = Math.sqrt(dX * dX + dY * dY); + this.frequencyHz = 0.0; + this.dampingRatio = 0.0; + } + Box2D.inherit(b2FrictionJoint, Box2D.Dynamics.Joints.b2Joint); + b2FrictionJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2FrictionJoint.b2FrictionJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchorA = new b2Vec2(); + this.m_localAnchorB = new b2Vec2(); + this.m_linearMass = new b2Mat22(); + this.m_linearImpulse = new b2Vec2(); + }; + b2FrictionJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchorA); + } + b2FrictionJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchorB); + } + b2FrictionJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_linearImpulse.x, inv_dt * this.m_linearImpulse.y); + } + b2FrictionJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_angularImpulse; + } + b2FrictionJoint.prototype.SetMaxForce = function(force) { + if(force === undefined) force = 0; + this.m_maxForce = force; + } + b2FrictionJoint.prototype.GetMaxForce = function() { + return this.m_maxForce; + } + b2FrictionJoint.prototype.SetMaxTorque = function(torque) { + if(torque === undefined) torque = 0; + this.m_maxTorque = torque; + } + b2FrictionJoint.prototype.GetMaxTorque = function() { + return this.m_maxTorque; + } + b2FrictionJoint.prototype.b2FrictionJoint = function(def) { + this.__super.b2Joint.call(this, def); + this.m_localAnchorA.SetV(def.localAnchorA); + this.m_localAnchorB.SetV(def.localAnchorB); + this.m_linearMass.SetZero(); + this.m_angularMass = 0.0; + this.m_linearImpulse.SetZero(); + this.m_angularImpulse = 0.0; + this.m_maxForce = def.maxForce; + this.m_maxTorque = def.maxTorque; + } + b2FrictionJoint.prototype.InitVelocityConstraints = function(step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + var K = new b2Mat22(); + K.col1.x = mA + mB; + K.col2.x = 0.0; + K.col1.y = 0.0; + K.col2.y = mA + mB; + K.col1.x += iA * rAY * rAY; + K.col2.x += (-iA * rAX * rAY); + K.col1.y += (-iA * rAX * rAY); + K.col2.y += iA * rAX * rAX; + K.col1.x += iB * rBY * rBY; + K.col2.x += (-iB * rBX * rBY); + K.col1.y += (-iB * rBX * rBY); + K.col2.y += iB * rBX * rBX; + K.GetInverse(this.m_linearMass); + this.m_angularMass = iA + iB; + if(this.m_angularMass > 0.0) { + this.m_angularMass = 1.0 / this.m_angularMass; + } + if(step.warmStarting) { + this.m_linearImpulse.x *= step.dtRatio; + this.m_linearImpulse.y *= step.dtRatio; + this.m_angularImpulse *= step.dtRatio; + var P = this.m_linearImpulse; + bA.m_linearVelocity.x -= mA * P.x; + bA.m_linearVelocity.y -= mA * P.y; + bA.m_angularVelocity -= iA * (rAX * P.y - rAY * P.x + this.m_angularImpulse); + bB.m_linearVelocity.x += mB * P.x; + bB.m_linearVelocity.y += mB * P.y; + bB.m_angularVelocity += iB * (rBX * P.y - rBY * P.x + this.m_angularImpulse); + } else { + this.m_linearImpulse.SetZero(); + this.m_angularImpulse = 0.0; + } + } + b2FrictionJoint.prototype.SolveVelocityConstraints = function(step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var vA = bA.m_linearVelocity; + var wA = bA.m_angularVelocity; + var vB = bB.m_linearVelocity; + var wB = bB.m_angularVelocity; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var maxImpulse = 0; { + var Cdot = wB - wA; + var impulse = (-this.m_angularMass * Cdot); + var oldImpulse = this.m_angularImpulse; + maxImpulse = step.dt * this.m_maxTorque; + this.m_angularImpulse = b2Math.Clamp(this.m_angularImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_angularImpulse - oldImpulse; + wA -= iA * impulse; + wB += iB * impulse; + } { + var CdotX = vB.x - wB * rBY - vA.x + wA * rAY; + var CdotY = vB.y + wB * rBX - vA.y - wA * rAX; + var impulseV = b2Math.MulMV(this.m_linearMass, new b2Vec2((-CdotX), (-CdotY))); + var oldImpulseV = this.m_linearImpulse.Copy(); + this.m_linearImpulse.Add(impulseV); + maxImpulse = step.dt * this.m_maxForce; + if(this.m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) { + this.m_linearImpulse.Normalize(); + this.m_linearImpulse.Multiply(maxImpulse); + } + impulseV = b2Math.SubtractVV(this.m_linearImpulse, oldImpulseV); + vA.x -= mA * impulseV.x; + vA.y -= mA * impulseV.y; + wA -= iA * (rAX * impulseV.y - rAY * impulseV.x); + vB.x += mB * impulseV.x; + vB.y += mB * impulseV.y; + wB += iB * (rBX * impulseV.y - rBY * impulseV.x); + } + bA.m_angularVelocity = wA; + bB.m_angularVelocity = wB; + } + b2FrictionJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + return true; + } + Box2D.inherit(b2FrictionJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2FrictionJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2FrictionJointDef.b2FrictionJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2FrictionJointDef.prototype.b2FrictionJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_frictionJoint; + this.maxForce = 0.0; + this.maxTorque = 0.0; + } + b2FrictionJointDef.prototype.Initialize = function(bA, bB, anchor) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA.SetV(this.bodyA.GetLocalPoint(anchor)); + this.localAnchorB.SetV(this.bodyB.GetLocalPoint(anchor)); + } + Box2D.inherit(b2GearJoint, Box2D.Dynamics.Joints.b2Joint); + b2GearJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2GearJoint.b2GearJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_groundAnchor1 = new b2Vec2(); + this.m_groundAnchor2 = new b2Vec2(); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_J = new b2Jacobian(); + }; + b2GearJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2GearJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2GearJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse * this.m_J.linearB.x, inv_dt * this.m_impulse * this.m_J.linearB.y); + } + b2GearJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + var tMat = this.m_bodyB.m_xf.R; + var rX = this.m_localAnchor1.x - this.m_bodyB.m_sweep.localCenter.x; + var rY = this.m_localAnchor1.y - this.m_bodyB.m_sweep.localCenter.y; + var tX = tMat.col1.x * rX + tMat.col2.x * rY; + rY = tMat.col1.y * rX + tMat.col2.y * rY; + rX = tX; + var PX = this.m_impulse * this.m_J.linearB.x; + var PY = this.m_impulse * this.m_J.linearB.y; + return inv_dt * (this.m_impulse * this.m_J.angularB - rX * PY + rY * PX); + } + b2GearJoint.prototype.GetRatio = function() { + return this.m_ratio; + } + b2GearJoint.prototype.SetRatio = function(ratio) { + if(ratio === undefined) ratio = 0; + this.m_ratio = ratio; + } + b2GearJoint.prototype.b2GearJoint = function(def) { + this.__super.b2Joint.call(this, def); + var type1 = parseInt(def.joint1.m_type); + var type2 = parseInt(def.joint2.m_type); + this.m_revolute1 = null; + this.m_prismatic1 = null; + this.m_revolute2 = null; + this.m_prismatic2 = null; + var coordinate1 = 0; + var coordinate2 = 0; + this.m_ground1 = def.joint1.GetBodyA(); + this.m_bodyA = def.joint1.GetBodyB(); + if(type1 == b2Joint.e_revoluteJoint) { + this.m_revolute1 = (def.joint1 instanceof b2RevoluteJoint ? def.joint1 : null); + this.m_groundAnchor1.SetV(this.m_revolute1.m_localAnchor1); + this.m_localAnchor1.SetV(this.m_revolute1.m_localAnchor2); + coordinate1 = this.m_revolute1.GetJointAngle(); + } else { + this.m_prismatic1 = (def.joint1 instanceof b2PrismaticJoint ? def.joint1 : null); + this.m_groundAnchor1.SetV(this.m_prismatic1.m_localAnchor1); + this.m_localAnchor1.SetV(this.m_prismatic1.m_localAnchor2); + coordinate1 = this.m_prismatic1.GetJointTranslation(); + } + this.m_ground2 = def.joint2.GetBodyA(); + this.m_bodyB = def.joint2.GetBodyB(); + if(type2 == b2Joint.e_revoluteJoint) { + this.m_revolute2 = (def.joint2 instanceof b2RevoluteJoint ? def.joint2 : null); + this.m_groundAnchor2.SetV(this.m_revolute2.m_localAnchor1); + this.m_localAnchor2.SetV(this.m_revolute2.m_localAnchor2); + coordinate2 = this.m_revolute2.GetJointAngle(); + } else { + this.m_prismatic2 = (def.joint2 instanceof b2PrismaticJoint ? def.joint2 : null); + this.m_groundAnchor2.SetV(this.m_prismatic2.m_localAnchor1); + this.m_localAnchor2.SetV(this.m_prismatic2.m_localAnchor2); + coordinate2 = this.m_prismatic2.GetJointTranslation(); + } + this.m_ratio = def.ratio; + this.m_constant = coordinate1 + this.m_ratio * coordinate2; + this.m_impulse = 0.0; + } + b2GearJoint.prototype.InitVelocityConstraints = function(step) { + var g1 = this.m_ground1; + var g2 = this.m_ground2; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var ugX = 0; + var ugY = 0; + var rX = 0; + var rY = 0; + var tMat; + var tVec; + var crug = 0; + var tX = 0; + var K = 0.0; + this.m_J.SetZero(); + if(this.m_revolute1) { + this.m_J.angularA = (-1.0); + K += bA.m_invI; + } else { + tMat = g1.m_xf.R; + tVec = this.m_prismatic1.m_localXAxis1; + ugX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + ugY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = bA.m_xf.R; + rX = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + rY = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = tMat.col1.x * rX + tMat.col2.x * rY; + rY = tMat.col1.y * rX + tMat.col2.y * rY; + rX = tX; + crug = rX * ugY - rY * ugX; + this.m_J.linearA.Set((-ugX), (-ugY)); + this.m_J.angularA = (-crug); + K += bA.m_invMass + bA.m_invI * crug * crug; + } + if(this.m_revolute2) { + this.m_J.angularB = (-this.m_ratio); + K += this.m_ratio * this.m_ratio * bB.m_invI; + } else { + tMat = g2.m_xf.R; + tVec = this.m_prismatic2.m_localXAxis1; + ugX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + ugY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = bB.m_xf.R; + rX = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + rY = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = tMat.col1.x * rX + tMat.col2.x * rY; + rY = tMat.col1.y * rX + tMat.col2.y * rY; + rX = tX; + crug = rX * ugY - rY * ugX; + this.m_J.linearB.Set((-this.m_ratio * ugX), (-this.m_ratio * ugY)); + this.m_J.angularB = (-this.m_ratio * crug); + K += this.m_ratio * this.m_ratio * (bB.m_invMass + bB.m_invI * crug * crug); + } + this.m_mass = K > 0.0 ? 1.0 / K : 0.0; + if(step.warmStarting) { + bA.m_linearVelocity.x += bA.m_invMass * this.m_impulse * this.m_J.linearA.x; + bA.m_linearVelocity.y += bA.m_invMass * this.m_impulse * this.m_J.linearA.y; + bA.m_angularVelocity += bA.m_invI * this.m_impulse * this.m_J.angularA; + bB.m_linearVelocity.x += bB.m_invMass * this.m_impulse * this.m_J.linearB.x; + bB.m_linearVelocity.y += bB.m_invMass * this.m_impulse * this.m_J.linearB.y; + bB.m_angularVelocity += bB.m_invI * this.m_impulse * this.m_J.angularB; + } else { + this.m_impulse = 0.0; + } + } + b2GearJoint.prototype.SolveVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var Cdot = this.m_J.Compute(bA.m_linearVelocity, bA.m_angularVelocity, bB.m_linearVelocity, bB.m_angularVelocity); + var impulse = (-this.m_mass * Cdot); + this.m_impulse += impulse; + bA.m_linearVelocity.x += bA.m_invMass * impulse * this.m_J.linearA.x; + bA.m_linearVelocity.y += bA.m_invMass * impulse * this.m_J.linearA.y; + bA.m_angularVelocity += bA.m_invI * impulse * this.m_J.angularA; + bB.m_linearVelocity.x += bB.m_invMass * impulse * this.m_J.linearB.x; + bB.m_linearVelocity.y += bB.m_invMass * impulse * this.m_J.linearB.y; + bB.m_angularVelocity += bB.m_invI * impulse * this.m_J.angularB; + } + b2GearJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var linearError = 0.0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var coordinate1 = 0; + var coordinate2 = 0; + if(this.m_revolute1) { + coordinate1 = this.m_revolute1.GetJointAngle(); + } else { + coordinate1 = this.m_prismatic1.GetJointTranslation(); + } + if(this.m_revolute2) { + coordinate2 = this.m_revolute2.GetJointAngle(); + } else { + coordinate2 = this.m_prismatic2.GetJointTranslation(); + } + var C = this.m_constant - (coordinate1 + this.m_ratio * coordinate2); + var impulse = (-this.m_mass * C); + bA.m_sweep.c.x += bA.m_invMass * impulse * this.m_J.linearA.x; + bA.m_sweep.c.y += bA.m_invMass * impulse * this.m_J.linearA.y; + bA.m_sweep.a += bA.m_invI * impulse * this.m_J.angularA; + bB.m_sweep.c.x += bB.m_invMass * impulse * this.m_J.linearB.x; + bB.m_sweep.c.y += bB.m_invMass * impulse * this.m_J.linearB.y; + bB.m_sweep.a += bB.m_invI * impulse * this.m_J.angularB; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return linearError < b2Settings.b2_linearSlop; + } + Box2D.inherit(b2GearJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2GearJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2GearJointDef.b2GearJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + }; + b2GearJointDef.prototype.b2GearJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_gearJoint; + this.joint1 = null; + this.joint2 = null; + this.ratio = 1.0; + } + b2Jacobian.b2Jacobian = function() { + this.linearA = new b2Vec2(); + this.linearB = new b2Vec2(); + }; + b2Jacobian.prototype.SetZero = function() { + this.linearA.SetZero(); + this.angularA = 0.0; + this.linearB.SetZero(); + this.angularB = 0.0; + } + b2Jacobian.prototype.Set = function(x1, a1, x2, a2) { + if(a1 === undefined) a1 = 0; + if(a2 === undefined) a2 = 0; + this.linearA.SetV(x1); + this.angularA = a1; + this.linearB.SetV(x2); + this.angularB = a2; + } + b2Jacobian.prototype.Compute = function(x1, a1, x2, a2) { + if(a1 === undefined) a1 = 0; + if(a2 === undefined) a2 = 0; + return(this.linearA.x * x1.x + this.linearA.y * x1.y) + this.angularA * a1 + (this.linearB.x * x2.x + this.linearB.y * x2.y) + this.angularB * a2; + } + b2Joint.b2Joint = function() { + this.m_edgeA = new b2JointEdge(); + this.m_edgeB = new b2JointEdge(); + this.m_localCenterA = new b2Vec2(); + this.m_localCenterB = new b2Vec2(); + }; + b2Joint.prototype.GetType = function() { + return this.m_type; + } + b2Joint.prototype.GetAnchorA = function() { + return null; + } + b2Joint.prototype.GetAnchorB = function() { + return null; + } + b2Joint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return null; + } + b2Joint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2Joint.prototype.GetBodyA = function() { + return this.m_bodyA; + } + b2Joint.prototype.GetBodyB = function() { + return this.m_bodyB; + } + b2Joint.prototype.GetNext = function() { + return this.m_next; + } + b2Joint.prototype.GetUserData = function() { + return this.m_userData; + } + b2Joint.prototype.SetUserData = function(data) { + this.m_userData = data; + } + b2Joint.prototype.IsActive = function() { + return this.m_bodyA.IsActive() && this.m_bodyB.IsActive(); + } + b2Joint.Create = function(def, allocator) { + var joint = null; + switch(def.type) { + case b2Joint.e_distanceJoint: + { + joint = new b2DistanceJoint((def instanceof b2DistanceJointDef ? def : null)); + } + break; + case b2Joint.e_mouseJoint: + { + joint = new b2MouseJoint((def instanceof b2MouseJointDef ? def : null)); + } + break; + case b2Joint.e_prismaticJoint: + { + joint = new b2PrismaticJoint((def instanceof b2PrismaticJointDef ? def : null)); + } + break; + case b2Joint.e_revoluteJoint: + { + joint = new b2RevoluteJoint((def instanceof b2RevoluteJointDef ? def : null)); + } + break; + case b2Joint.e_pulleyJoint: + { + joint = new b2PulleyJoint((def instanceof b2PulleyJointDef ? def : null)); + } + break; + case b2Joint.e_gearJoint: + { + joint = new b2GearJoint((def instanceof b2GearJointDef ? def : null)); + } + break; + case b2Joint.e_lineJoint: + { + joint = new b2LineJoint((def instanceof b2LineJointDef ? def : null)); + } + break; + case b2Joint.e_weldJoint: + { + joint = new b2WeldJoint((def instanceof b2WeldJointDef ? def : null)); + } + break; + case b2Joint.e_frictionJoint: + { + joint = new b2FrictionJoint((def instanceof b2FrictionJointDef ? def : null)); + } + break; + default: + break; + } + return joint; + } + b2Joint.Destroy = function(joint, allocator) {} + b2Joint.prototype.b2Joint = function(def) { + b2Settings.b2Assert(def.bodyA != def.bodyB); + this.m_type = def.type; + this.m_prev = null; + this.m_next = null; + this.m_bodyA = def.bodyA; + this.m_bodyB = def.bodyB; + this.m_collideConnected = def.collideConnected; + this.m_islandFlag = false; + this.m_userData = def.userData; + } + b2Joint.prototype.InitVelocityConstraints = function(step) {} + b2Joint.prototype.SolveVelocityConstraints = function(step) {} + b2Joint.prototype.FinalizeVelocityConstraints = function() {} + b2Joint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + return false; + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.Joints.b2Joint.e_unknownJoint = 0; + Box2D.Dynamics.Joints.b2Joint.e_revoluteJoint = 1; + Box2D.Dynamics.Joints.b2Joint.e_prismaticJoint = 2; + Box2D.Dynamics.Joints.b2Joint.e_distanceJoint = 3; + Box2D.Dynamics.Joints.b2Joint.e_pulleyJoint = 4; + Box2D.Dynamics.Joints.b2Joint.e_mouseJoint = 5; + Box2D.Dynamics.Joints.b2Joint.e_gearJoint = 6; + Box2D.Dynamics.Joints.b2Joint.e_lineJoint = 7; + Box2D.Dynamics.Joints.b2Joint.e_weldJoint = 8; + Box2D.Dynamics.Joints.b2Joint.e_frictionJoint = 9; + Box2D.Dynamics.Joints.b2Joint.e_inactiveLimit = 0; + Box2D.Dynamics.Joints.b2Joint.e_atLowerLimit = 1; + Box2D.Dynamics.Joints.b2Joint.e_atUpperLimit = 2; + Box2D.Dynamics.Joints.b2Joint.e_equalLimits = 3; + }); + b2JointDef.b2JointDef = function() {}; + b2JointDef.prototype.b2JointDef = function() { + this.type = b2Joint.e_unknownJoint; + this.userData = null; + this.bodyA = null; + this.bodyB = null; + this.collideConnected = false; + } + b2JointEdge.b2JointEdge = function() {}; + Box2D.inherit(b2LineJoint, Box2D.Dynamics.Joints.b2Joint); + b2LineJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2LineJoint.b2LineJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_localXAxis1 = new b2Vec2(); + this.m_localYAxis1 = new b2Vec2(); + this.m_axis = new b2Vec2(); + this.m_perp = new b2Vec2(); + this.m_K = new b2Mat22(); + this.m_impulse = new b2Vec2(); + }; + b2LineJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2LineJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2LineJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * (this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.x), inv_dt * (this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + + this.m_impulse.y) * this.m_axis.y)); + } + b2LineJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.y; + } + b2LineJoint.prototype.GetJointTranslation = function() { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var p1 = bA.GetWorldPoint(this.m_localAnchor1); + var p2 = bB.GetWorldPoint(this.m_localAnchor2); + var dX = p2.x - p1.x; + var dY = p2.y - p1.y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var translation = axis.x * dX + axis.y * dY; + return translation; + } + b2LineJoint.prototype.GetJointSpeed = function() { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var p1X = bA.m_sweep.c.x + r1X; + var p1Y = bA.m_sweep.c.y + r1Y; + var p2X = bB.m_sweep.c.x + r2X; + var p2Y = bB.m_sweep.c.y + r2Y; + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var v1 = bA.m_linearVelocity; + var v2 = bB.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var w2 = bB.m_angularVelocity; + var speed = (dX * ((-w1 * axis.y)) + dY * (w1 * axis.x)) + (axis.x * (((v2.x + ((-w2 * r2Y))) - v1.x) - ((-w1 * r1Y))) + axis.y * (((v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X))); + return speed; + } + b2LineJoint.prototype.IsLimitEnabled = function() { + return this.m_enableLimit; + } + b2LineJoint.prototype.EnableLimit = function(flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableLimit = flag; + } + b2LineJoint.prototype.GetLowerLimit = function() { + return this.m_lowerTranslation; + } + b2LineJoint.prototype.GetUpperLimit = function() { + return this.m_upperTranslation; + } + b2LineJoint.prototype.SetLimits = function(lower, upper) { + if(lower === undefined) lower = 0; + if(upper === undefined) upper = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_lowerTranslation = lower; + this.m_upperTranslation = upper; + } + b2LineJoint.prototype.IsMotorEnabled = function() { + return this.m_enableMotor; + } + b2LineJoint.prototype.EnableMotor = function(flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableMotor = flag; + } + b2LineJoint.prototype.SetMotorSpeed = function(speed) { + if(speed === undefined) speed = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_motorSpeed = speed; + } + b2LineJoint.prototype.GetMotorSpeed = function() { + return this.m_motorSpeed; + } + b2LineJoint.prototype.SetMaxMotorForce = function(force) { + if(force === undefined) force = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_maxMotorForce = force; + } + b2LineJoint.prototype.GetMaxMotorForce = function() { + return this.m_maxMotorForce; + } + b2LineJoint.prototype.GetMotorForce = function() { + return this.m_motorImpulse; + } + b2LineJoint.prototype.b2LineJoint = function(def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_localXAxis1.SetV(def.localAxisA); + this.m_localYAxis1.x = (-this.m_localXAxis1.y); + this.m_localYAxis1.y = this.m_localXAxis1.x; + this.m_impulse.SetZero(); + this.m_motorMass = 0.0; + this.m_motorImpulse = 0.0; + this.m_lowerTranslation = def.lowerTranslation; + this.m_upperTranslation = def.upperTranslation; + this.m_maxMotorForce = def.maxMotorForce; + this.m_motorSpeed = def.motorSpeed; + this.m_enableLimit = def.enableLimit; + this.m_enableMotor = def.enableMotor; + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_axis.SetZero(); + this.m_perp.SetZero(); + } + b2LineJoint.prototype.InitVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + this.m_localCenterA.SetV(bA.GetLocalCenter()); + this.m_localCenterB.SetV(bB.GetLocalCenter()); + var xf1 = bA.GetTransform(); + var xf2 = bB.GetTransform(); + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var dY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + this.m_invMassA = bA.m_invMass; + this.m_invMassB = bB.m_invMass; + this.m_invIA = bA.m_invI; + this.m_invIB = bB.m_invI; { + this.m_axis.SetV(b2Math.MulMV(xf1.R, this.m_localXAxis1)); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + this.m_motorMass = this.m_invMassA + this.m_invMassB + this.m_invIA * this.m_a1 * this.m_a1 + this.m_invIB * this.m_a2 * this.m_a2; + this.m_motorMass = this.m_motorMass > Number.MIN_VALUE ? 1.0 / this.m_motorMass : 0.0; + } { + this.m_perp.SetV(b2Math.MulMV(xf1.R, this.m_localYAxis1)); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var m1 = this.m_invMassA; + var m2 = this.m_invMassB; + var i1 = this.m_invIA; + var i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + } + if(this.m_enableLimit) { + var jointTransition = this.m_axis.x * dX + this.m_axis.y * dY; + if(b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + this.m_limitState = b2Joint.e_equalLimits; + } else if(jointTransition <= this.m_lowerTranslation) { + if(this.m_limitState != b2Joint.e_atLowerLimit) { + this.m_limitState = b2Joint.e_atLowerLimit; + this.m_impulse.y = 0.0; + } + } else if(jointTransition >= this.m_upperTranslation) { + if(this.m_limitState != b2Joint.e_atUpperLimit) { + this.m_limitState = b2Joint.e_atUpperLimit; + this.m_impulse.y = 0.0; + } + } else { + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_impulse.y = 0.0; + } + } else { + this.m_limitState = b2Joint.e_inactiveLimit; + } + if(this.m_enableMotor == false) { + this.m_motorImpulse = 0.0; + } + if(step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_motorImpulse *= step.dtRatio; + var PX = this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.x; + var PY = this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.y; + var L1 = this.m_impulse.x * this.m_s1 + (this.m_motorImpulse + this.m_impulse.y) * this.m_a1; + var L2 = this.m_impulse.x * this.m_s2 + (this.m_motorImpulse + this.m_impulse.y) * this.m_a2; + bA.m_linearVelocity.x -= this.m_invMassA * PX; + bA.m_linearVelocity.y -= this.m_invMassA * PY; + bA.m_angularVelocity -= this.m_invIA * L1; + bB.m_linearVelocity.x += this.m_invMassB * PX; + bB.m_linearVelocity.y += this.m_invMassB * PY; + bB.m_angularVelocity += this.m_invIB * L2; + } else { + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + } + } + b2LineJoint.prototype.SolveVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var v1 = bA.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var v2 = bB.m_linearVelocity; + var w2 = bB.m_angularVelocity; + var PX = 0; + var PY = 0; + var L1 = 0; + var L2 = 0; + if(this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits) { + var Cdot = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var impulse = this.m_motorMass * (this.m_motorSpeed - Cdot); + var oldImpulse = this.m_motorImpulse; + var maxImpulse = step.dt * this.m_maxMotorForce; + this.m_motorImpulse = b2Math.Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_motorImpulse - oldImpulse; + PX = impulse * this.m_axis.x; + PY = impulse * this.m_axis.y; + L1 = impulse * this.m_a1; + L2 = impulse * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + var Cdot1 = this.m_perp.x * (v2.x - v1.x) + this.m_perp.y * (v2.y - v1.y) + this.m_s2 * w2 - this.m_s1 * w1; + if(this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + var Cdot2 = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var f1 = this.m_impulse.Copy(); + var df = this.m_K.Solve(new b2Vec2(), (-Cdot1), (-Cdot2)); + this.m_impulse.Add(df); + if(this.m_limitState == b2Joint.e_atLowerLimit) { + this.m_impulse.y = b2Math.Max(this.m_impulse.y, 0.0); + } else if(this.m_limitState == b2Joint.e_atUpperLimit) { + this.m_impulse.y = b2Math.Min(this.m_impulse.y, 0.0); + } + var b = (-Cdot1) - (this.m_impulse.y - f1.y) * this.m_K.col2.x; + var f2r = 0; + if(this.m_K.col1.x != 0.0) { + f2r = b / this.m_K.col1.x + f1.x; + } else { + f2r = f1.x; + } + this.m_impulse.x = f2r; + df.x = this.m_impulse.x - f1.x; + df.y = this.m_impulse.y - f1.y; + PX = df.x * this.m_perp.x + df.y * this.m_axis.x; + PY = df.x * this.m_perp.y + df.y * this.m_axis.y; + L1 = df.x * this.m_s1 + df.y * this.m_a1; + L2 = df.x * this.m_s2 + df.y * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } else { + var df2 = 0; + if(this.m_K.col1.x != 0.0) { + df2 = ((-Cdot1)) / this.m_K.col1.x; + } else { + df2 = 0.0; + } + this.m_impulse.x += df2; + PX = df2 * this.m_perp.x; + PY = df2 * this.m_perp.y; + L1 = df2 * this.m_s1; + L2 = df2 * this.m_s2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + bA.m_linearVelocity.SetV(v1); + bA.m_angularVelocity = w1; + bB.m_linearVelocity.SetV(v2); + bB.m_angularVelocity = w2; + } + b2LineJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var limitC = 0; + var oldLimitImpulse = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var c1 = bA.m_sweep.c; + var a1 = bA.m_sweep.a; + var c2 = bB.m_sweep.c; + var a2 = bB.m_sweep.a; + var tMat; + var tX = 0; + var m1 = 0; + var m2 = 0; + var i1 = 0; + var i2 = 0; + var linearError = 0.0; + var angularError = 0.0; + var active = false; + var C2 = 0.0; + var R1 = b2Mat22.FromAngle(a1); + var R2 = b2Mat22.FromAngle(a2); + tMat = R1; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = R2; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = c2.x + r2X - c1.x - r1X; + var dY = c2.y + r2Y - c1.y - r1Y; + if(this.m_enableLimit) { + this.m_axis = b2Math.MulMV(R1, this.m_localXAxis1); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + var translation = this.m_axis.x * dX + this.m_axis.y * dY; + if(b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + C2 = b2Math.Clamp(translation, (-b2Settings.b2_maxLinearCorrection), b2Settings.b2_maxLinearCorrection); + linearError = b2Math.Abs(translation); + active = true; + } else if(translation <= this.m_lowerTranslation) { + C2 = b2Math.Clamp(translation - this.m_lowerTranslation + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + linearError = this.m_lowerTranslation - translation; + active = true; + } else if(translation >= this.m_upperTranslation) { + C2 = b2Math.Clamp(translation - this.m_upperTranslation + b2Settings.b2_linearSlop, 0.0, b2Settings.b2_maxLinearCorrection); + linearError = translation - this.m_upperTranslation; + active = true; + } + } + this.m_perp = b2Math.MulMV(R1, this.m_localYAxis1); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var impulse = new b2Vec2(); + var C1 = this.m_perp.x * dX + this.m_perp.y * dY; + linearError = b2Math.Max(linearError, b2Math.Abs(C1)); + angularError = 0.0; + if(active) { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + this.m_K.Solve(impulse, (-C1), (-C2)); + } else { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + var k11 = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + var impulse1 = 0; + if(k11 != 0.0) { + impulse1 = ((-C1)) / k11; + } else { + impulse1 = 0.0; + } + impulse.x = impulse1; + impulse.y = 0.0; + } + var PX = impulse.x * this.m_perp.x + impulse.y * this.m_axis.x; + var PY = impulse.x * this.m_perp.y + impulse.y * this.m_axis.y; + var L1 = impulse.x * this.m_s1 + impulse.y * this.m_a1; + var L2 = impulse.x * this.m_s2 + impulse.y * this.m_a2; + c1.x -= this.m_invMassA * PX; + c1.y -= this.m_invMassA * PY; + a1 -= this.m_invIA * L1; + c2.x += this.m_invMassB * PX; + c2.y += this.m_invMassB * PY; + a2 += this.m_invIB * L2; + bA.m_sweep.a = a1; + bB.m_sweep.a = a2; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return linearError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.inherit(b2LineJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2LineJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2LineJointDef.b2LineJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + this.localAxisA = new b2Vec2(); + }; + b2LineJointDef.prototype.b2LineJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_lineJoint; + this.localAxisA.Set(1.0, 0.0); + this.enableLimit = false; + this.lowerTranslation = 0.0; + this.upperTranslation = 0.0; + this.enableMotor = false; + this.maxMotorForce = 0.0; + this.motorSpeed = 0.0; + } + b2LineJointDef.prototype.Initialize = function(bA, bB, anchor, axis) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.GetLocalPoint(anchor); + this.localAnchorB = this.bodyB.GetLocalPoint(anchor); + this.localAxisA = this.bodyA.GetLocalVector(axis); + } + Box2D.inherit(b2MouseJoint, Box2D.Dynamics.Joints.b2Joint); + b2MouseJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2MouseJoint.b2MouseJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.K = new b2Mat22(); + this.K1 = new b2Mat22(); + this.K2 = new b2Mat22(); + this.m_localAnchor = new b2Vec2(); + this.m_target = new b2Vec2(); + this.m_impulse = new b2Vec2(); + this.m_mass = new b2Mat22(); + this.m_C = new b2Vec2(); + }; + b2MouseJoint.prototype.GetAnchorA = function() { + return this.m_target; + } + b2MouseJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor); + } + b2MouseJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse.x, inv_dt * this.m_impulse.y); + } + b2MouseJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2MouseJoint.prototype.GetTarget = function() { + return this.m_target; + } + b2MouseJoint.prototype.SetTarget = function(target) { + if(this.m_bodyB.IsAwake() == false) { + this.m_bodyB.SetAwake(true); + } + this.m_target = target; + } + b2MouseJoint.prototype.GetMaxForce = function() { + return this.m_maxForce; + } + b2MouseJoint.prototype.SetMaxForce = function(maxForce) { + if(maxForce === undefined) maxForce = 0; + this.m_maxForce = maxForce; + } + b2MouseJoint.prototype.GetFrequency = function() { + return this.m_frequencyHz; + } + b2MouseJoint.prototype.SetFrequency = function(hz) { + if(hz === undefined) hz = 0; + this.m_frequencyHz = hz; + } + b2MouseJoint.prototype.GetDampingRatio = function() { + return this.m_dampingRatio; + } + b2MouseJoint.prototype.SetDampingRatio = function(ratio) { + if(ratio === undefined) ratio = 0; + this.m_dampingRatio = ratio; + } + b2MouseJoint.prototype.b2MouseJoint = function(def) { + this.__super.b2Joint.call(this, def); + this.m_target.SetV(def.target); + var tX = this.m_target.x - this.m_bodyB.m_xf.position.x; + var tY = this.m_target.y - this.m_bodyB.m_xf.position.y; + var tMat = this.m_bodyB.m_xf.R; + this.m_localAnchor.x = (tX * tMat.col1.x + tY * tMat.col1.y); + this.m_localAnchor.y = (tX * tMat.col2.x + tY * tMat.col2.y); + this.m_maxForce = def.maxForce; + this.m_impulse.SetZero(); + this.m_frequencyHz = def.frequencyHz; + this.m_dampingRatio = def.dampingRatio; + this.m_beta = 0.0; + this.m_gamma = 0.0; + } + b2MouseJoint.prototype.InitVelocityConstraints = function(step) { + var b = this.m_bodyB; + var mass = b.GetMass(); + var omega = 2.0 * Math.PI * this.m_frequencyHz; + var d = 2.0 * mass * this.m_dampingRatio * omega; + var k = mass * omega * omega; + this.m_gamma = step.dt * (d + step.dt * k); + this.m_gamma = this.m_gamma != 0 ? 1 / this.m_gamma : 0.0; + this.m_beta = step.dt * k * this.m_gamma; + var tMat; + tMat = b.m_xf.R; + var rX = this.m_localAnchor.x - b.m_sweep.localCenter.x; + var rY = this.m_localAnchor.y - b.m_sweep.localCenter.y; + var tX = (tMat.col1.x * rX + tMat.col2.x * rY); + rY = (tMat.col1.y * rX + tMat.col2.y * rY); + rX = tX; + var invMass = b.m_invMass; + var invI = b.m_invI; + this.K1.col1.x = invMass; + this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; + this.K1.col2.y = invMass; + this.K2.col1.x = invI * rY * rY; + this.K2.col2.x = (-invI * rX * rY); + this.K2.col1.y = (-invI * rX * rY); + this.K2.col2.y = invI * rX * rX; + this.K.SetM(this.K1); + this.K.AddM(this.K2); + this.K.col1.x += this.m_gamma; + this.K.col2.y += this.m_gamma; + this.K.GetInverse(this.m_mass); + this.m_C.x = b.m_sweep.c.x + rX - this.m_target.x; + this.m_C.y = b.m_sweep.c.y + rY - this.m_target.y; + b.m_angularVelocity *= 0.98; + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + b.m_linearVelocity.x += invMass * this.m_impulse.x; + b.m_linearVelocity.y += invMass * this.m_impulse.y; + b.m_angularVelocity += invI * (rX * this.m_impulse.y - rY * this.m_impulse.x); + } + b2MouseJoint.prototype.SolveVelocityConstraints = function(step) { + var b = this.m_bodyB; + var tMat; + var tX = 0; + var tY = 0; + tMat = b.m_xf.R; + var rX = this.m_localAnchor.x - b.m_sweep.localCenter.x; + var rY = this.m_localAnchor.y - b.m_sweep.localCenter.y; + tX = (tMat.col1.x * rX + tMat.col2.x * rY); + rY = (tMat.col1.y * rX + tMat.col2.y * rY); + rX = tX; + var CdotX = b.m_linearVelocity.x + ((-b.m_angularVelocity * rY)); + var CdotY = b.m_linearVelocity.y + (b.m_angularVelocity * rX); + tMat = this.m_mass; + tX = CdotX + this.m_beta * this.m_C.x + this.m_gamma * this.m_impulse.x; + tY = CdotY + this.m_beta * this.m_C.y + this.m_gamma * this.m_impulse.y; + var impulseX = (-(tMat.col1.x * tX + tMat.col2.x * tY)); + var impulseY = (-(tMat.col1.y * tX + tMat.col2.y * tY)); + var oldImpulseX = this.m_impulse.x; + var oldImpulseY = this.m_impulse.y; + this.m_impulse.x += impulseX; + this.m_impulse.y += impulseY; + var maxImpulse = step.dt * this.m_maxForce; + if(this.m_impulse.LengthSquared() > maxImpulse * maxImpulse) { + this.m_impulse.Multiply(maxImpulse / this.m_impulse.Length()); + } + impulseX = this.m_impulse.x - oldImpulseX; + impulseY = this.m_impulse.y - oldImpulseY; + b.m_linearVelocity.x += b.m_invMass * impulseX; + b.m_linearVelocity.y += b.m_invMass * impulseY; + b.m_angularVelocity += b.m_invI * (rX * impulseY - rY * impulseX); + } + b2MouseJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + return true; + } + Box2D.inherit(b2MouseJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2MouseJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2MouseJointDef.b2MouseJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.target = new b2Vec2(); + }; + b2MouseJointDef.prototype.b2MouseJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_mouseJoint; + this.maxForce = 0.0; + this.frequencyHz = 5.0; + this.dampingRatio = 0.7; + } + Box2D.inherit(b2PrismaticJoint, Box2D.Dynamics.Joints.b2Joint); + b2PrismaticJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2PrismaticJoint.b2PrismaticJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_localXAxis1 = new b2Vec2(); + this.m_localYAxis1 = new b2Vec2(); + this.m_axis = new b2Vec2(); + this.m_perp = new b2Vec2(); + this.m_K = new b2Mat33(); + this.m_impulse = new b2Vec3(); + }; + b2PrismaticJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2PrismaticJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2PrismaticJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * (this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.x), inv_dt * (this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + + this.m_impulse.z) * this.m_axis.y)); + } + b2PrismaticJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.y; + } + b2PrismaticJoint.prototype.GetJointTranslation = function() { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var p1 = bA.GetWorldPoint(this.m_localAnchor1); + var p2 = bB.GetWorldPoint(this.m_localAnchor2); + var dX = p2.x - p1.x; + var dY = p2.y - p1.y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var translation = axis.x * dX + axis.y * dY; + return translation; + } + b2PrismaticJoint.prototype.GetJointSpeed = function() { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var p1X = bA.m_sweep.c.x + r1X; + var p1Y = bA.m_sweep.c.y + r1Y; + var p2X = bB.m_sweep.c.x + r2X; + var p2Y = bB.m_sweep.c.y + r2Y; + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var v1 = bA.m_linearVelocity; + var v2 = bB.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var w2 = bB.m_angularVelocity; + var speed = (dX * ((-w1 * axis.y)) + dY * (w1 * axis.x)) + (axis.x * (((v2.x + ((-w2 * r2Y))) - v1.x) - ((-w1 * r1Y))) + axis.y * (((v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X))); + return speed; + } + b2PrismaticJoint.prototype.IsLimitEnabled = function() { + return this.m_enableLimit; + } + b2PrismaticJoint.prototype.EnableLimit = function(flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableLimit = flag; + } + b2PrismaticJoint.prototype.GetLowerLimit = function() { + return this.m_lowerTranslation; + } + b2PrismaticJoint.prototype.GetUpperLimit = function() { + return this.m_upperTranslation; + } + b2PrismaticJoint.prototype.SetLimits = function(lower, upper) { + if(lower === undefined) lower = 0; + if(upper === undefined) upper = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_lowerTranslation = lower; + this.m_upperTranslation = upper; + } + b2PrismaticJoint.prototype.IsMotorEnabled = function() { + return this.m_enableMotor; + } + b2PrismaticJoint.prototype.EnableMotor = function(flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableMotor = flag; + } + b2PrismaticJoint.prototype.SetMotorSpeed = function(speed) { + if(speed === undefined) speed = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_motorSpeed = speed; + } + b2PrismaticJoint.prototype.GetMotorSpeed = function() { + return this.m_motorSpeed; + } + b2PrismaticJoint.prototype.SetMaxMotorForce = function(force) { + if(force === undefined) force = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_maxMotorForce = force; + } + b2PrismaticJoint.prototype.GetMotorForce = function() { + return this.m_motorImpulse; + } + b2PrismaticJoint.prototype.b2PrismaticJoint = function(def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_localXAxis1.SetV(def.localAxisA); + this.m_localYAxis1.x = (-this.m_localXAxis1.y); + this.m_localYAxis1.y = this.m_localXAxis1.x; + this.m_refAngle = def.referenceAngle; + this.m_impulse.SetZero(); + this.m_motorMass = 0.0; + this.m_motorImpulse = 0.0; + this.m_lowerTranslation = def.lowerTranslation; + this.m_upperTranslation = def.upperTranslation; + this.m_maxMotorForce = def.maxMotorForce; + this.m_motorSpeed = def.motorSpeed; + this.m_enableLimit = def.enableLimit; + this.m_enableMotor = def.enableMotor; + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_axis.SetZero(); + this.m_perp.SetZero(); + } + b2PrismaticJoint.prototype.InitVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + this.m_localCenterA.SetV(bA.GetLocalCenter()); + this.m_localCenterB.SetV(bB.GetLocalCenter()); + var xf1 = bA.GetTransform(); + var xf2 = bB.GetTransform(); + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var dY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + this.m_invMassA = bA.m_invMass; + this.m_invMassB = bB.m_invMass; + this.m_invIA = bA.m_invI; + this.m_invIB = bB.m_invI; { + this.m_axis.SetV(b2Math.MulMV(xf1.R, this.m_localXAxis1)); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + this.m_motorMass = this.m_invMassA + this.m_invMassB + this.m_invIA * this.m_a1 * this.m_a1 + this.m_invIB * this.m_a2 * this.m_a2; + if(this.m_motorMass > Number.MIN_VALUE) this.m_motorMass = 1.0 / this.m_motorMass; + } { + this.m_perp.SetV(b2Math.MulMV(xf1.R, this.m_localYAxis1)); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var m1 = this.m_invMassA; + var m2 = this.m_invMassB; + var i1 = this.m_invIA; + var i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 + i2 * this.m_s2; + this.m_K.col1.z = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = i1 + i2; + this.m_K.col2.z = i1 * this.m_a1 + i2 * this.m_a2; + this.m_K.col3.x = this.m_K.col1.z; + this.m_K.col3.y = this.m_K.col2.z; + this.m_K.col3.z = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + } + if(this.m_enableLimit) { + var jointTransition = this.m_axis.x * dX + this.m_axis.y * dY; + if(b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + this.m_limitState = b2Joint.e_equalLimits; + } else if(jointTransition <= this.m_lowerTranslation) { + if(this.m_limitState != b2Joint.e_atLowerLimit) { + this.m_limitState = b2Joint.e_atLowerLimit; + this.m_impulse.z = 0.0; + } + } else if(jointTransition >= this.m_upperTranslation) { + if(this.m_limitState != b2Joint.e_atUpperLimit) { + this.m_limitState = b2Joint.e_atUpperLimit; + this.m_impulse.z = 0.0; + } + } else { + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_impulse.z = 0.0; + } + } else { + this.m_limitState = b2Joint.e_inactiveLimit; + } + if(this.m_enableMotor == false) { + this.m_motorImpulse = 0.0; + } + if(step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_motorImpulse *= step.dtRatio; + var PX = this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.x; + var PY = this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.y; + var L1 = this.m_impulse.x * this.m_s1 + this.m_impulse.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_a1; + var L2 = this.m_impulse.x * this.m_s2 + this.m_impulse.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_a2; + bA.m_linearVelocity.x -= this.m_invMassA * PX; + bA.m_linearVelocity.y -= this.m_invMassA * PY; + bA.m_angularVelocity -= this.m_invIA * L1; + bB.m_linearVelocity.x += this.m_invMassB * PX; + bB.m_linearVelocity.y += this.m_invMassB * PY; + bB.m_angularVelocity += this.m_invIB * L2; + } else { + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + } + } + b2PrismaticJoint.prototype.SolveVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var v1 = bA.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var v2 = bB.m_linearVelocity; + var w2 = bB.m_angularVelocity; + var PX = 0; + var PY = 0; + var L1 = 0; + var L2 = 0; + if(this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits) { + var Cdot = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var impulse = this.m_motorMass * (this.m_motorSpeed - Cdot); + var oldImpulse = this.m_motorImpulse; + var maxImpulse = step.dt * this.m_maxMotorForce; + this.m_motorImpulse = b2Math.Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_motorImpulse - oldImpulse; + PX = impulse * this.m_axis.x; + PY = impulse * this.m_axis.y; + L1 = impulse * this.m_a1; + L2 = impulse * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + var Cdot1X = this.m_perp.x * (v2.x - v1.x) + this.m_perp.y * (v2.y - v1.y) + this.m_s2 * w2 - this.m_s1 * w1; + var Cdot1Y = w2 - w1; + if(this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + var Cdot2 = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var f1 = this.m_impulse.Copy(); + var df = this.m_K.Solve33(new b2Vec3(), (-Cdot1X), (-Cdot1Y), (-Cdot2)); + this.m_impulse.Add(df); + if(this.m_limitState == b2Joint.e_atLowerLimit) { + this.m_impulse.z = b2Math.Max(this.m_impulse.z, 0.0); + } else if(this.m_limitState == b2Joint.e_atUpperLimit) { + this.m_impulse.z = b2Math.Min(this.m_impulse.z, 0.0); + } + var bX = (-Cdot1X) - (this.m_impulse.z - f1.z) * this.m_K.col3.x; + var bY = (-Cdot1Y) - (this.m_impulse.z - f1.z) * this.m_K.col3.y; + var f2r = this.m_K.Solve22(new b2Vec2(), bX, bY); + f2r.x += f1.x; + f2r.y += f1.y; + this.m_impulse.x = f2r.x; + this.m_impulse.y = f2r.y; + df.x = this.m_impulse.x - f1.x; + df.y = this.m_impulse.y - f1.y; + df.z = this.m_impulse.z - f1.z; + PX = df.x * this.m_perp.x + df.z * this.m_axis.x; + PY = df.x * this.m_perp.y + df.z * this.m_axis.y; + L1 = df.x * this.m_s1 + df.y + df.z * this.m_a1; + L2 = df.x * this.m_s2 + df.y + df.z * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } else { + var df2 = this.m_K.Solve22(new b2Vec2(), (-Cdot1X), (-Cdot1Y)); + this.m_impulse.x += df2.x; + this.m_impulse.y += df2.y; + PX = df2.x * this.m_perp.x; + PY = df2.x * this.m_perp.y; + L1 = df2.x * this.m_s1 + df2.y; + L2 = df2.x * this.m_s2 + df2.y; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + bA.m_linearVelocity.SetV(v1); + bA.m_angularVelocity = w1; + bB.m_linearVelocity.SetV(v2); + bB.m_angularVelocity = w2; + } + b2PrismaticJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var limitC = 0; + var oldLimitImpulse = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var c1 = bA.m_sweep.c; + var a1 = bA.m_sweep.a; + var c2 = bB.m_sweep.c; + var a2 = bB.m_sweep.a; + var tMat; + var tX = 0; + var m1 = 0; + var m2 = 0; + var i1 = 0; + var i2 = 0; + var linearError = 0.0; + var angularError = 0.0; + var active = false; + var C2 = 0.0; + var R1 = b2Mat22.FromAngle(a1); + var R2 = b2Mat22.FromAngle(a2); + tMat = R1; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = R2; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = c2.x + r2X - c1.x - r1X; + var dY = c2.y + r2Y - c1.y - r1Y; + if(this.m_enableLimit) { + this.m_axis = b2Math.MulMV(R1, this.m_localXAxis1); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + var translation = this.m_axis.x * dX + this.m_axis.y * dY; + if(b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + C2 = b2Math.Clamp(translation, (-b2Settings.b2_maxLinearCorrection), b2Settings.b2_maxLinearCorrection); + linearError = b2Math.Abs(translation); + active = true; + } else if(translation <= this.m_lowerTranslation) { + C2 = b2Math.Clamp(translation - this.m_lowerTranslation + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + linearError = this.m_lowerTranslation - translation; + active = true; + } else if(translation >= this.m_upperTranslation) { + C2 = b2Math.Clamp(translation - this.m_upperTranslation + b2Settings.b2_linearSlop, 0.0, b2Settings.b2_maxLinearCorrection); + linearError = translation - this.m_upperTranslation; + active = true; + } + } + this.m_perp = b2Math.MulMV(R1, this.m_localYAxis1); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var impulse = new b2Vec3(); + var C1X = this.m_perp.x * dX + this.m_perp.y * dY; + var C1Y = a2 - a1 - this.m_refAngle; + linearError = b2Math.Max(linearError, b2Math.Abs(C1X)); + angularError = b2Math.Abs(C1Y); + if(active) { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 + i2 * this.m_s2; + this.m_K.col1.z = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = i1 + i2; + this.m_K.col2.z = i1 * this.m_a1 + i2 * this.m_a2; + this.m_K.col3.x = this.m_K.col1.z; + this.m_K.col3.y = this.m_K.col2.z; + this.m_K.col3.z = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + this.m_K.Solve33(impulse, (-C1X), (-C1Y), (-C2)); + } else { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + var k11 = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + var k12 = i1 * this.m_s1 + i2 * this.m_s2; + var k22 = i1 + i2; + this.m_K.col1.Set(k11, k12, 0.0); + this.m_K.col2.Set(k12, k22, 0.0); + var impulse1 = this.m_K.Solve22(new b2Vec2(), (-C1X), (-C1Y)); + impulse.x = impulse1.x; + impulse.y = impulse1.y; + impulse.z = 0.0; + } + var PX = impulse.x * this.m_perp.x + impulse.z * this.m_axis.x; + var PY = impulse.x * this.m_perp.y + impulse.z * this.m_axis.y; + var L1 = impulse.x * this.m_s1 + impulse.y + impulse.z * this.m_a1; + var L2 = impulse.x * this.m_s2 + impulse.y + impulse.z * this.m_a2; + c1.x -= this.m_invMassA * PX; + c1.y -= this.m_invMassA * PY; + a1 -= this.m_invIA * L1; + c2.x += this.m_invMassB * PX; + c2.y += this.m_invMassB * PY; + a2 += this.m_invIB * L2; + bA.m_sweep.a = a1; + bB.m_sweep.a = a2; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return linearError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.inherit(b2PrismaticJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2PrismaticJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2PrismaticJointDef.b2PrismaticJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + this.localAxisA = new b2Vec2(); + }; + b2PrismaticJointDef.prototype.b2PrismaticJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_prismaticJoint; + this.localAxisA.Set(1.0, 0.0); + this.referenceAngle = 0.0; + this.enableLimit = false; + this.lowerTranslation = 0.0; + this.upperTranslation = 0.0; + this.enableMotor = false; + this.maxMotorForce = 0.0; + this.motorSpeed = 0.0; + } + b2PrismaticJointDef.prototype.Initialize = function(bA, bB, anchor, axis) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.GetLocalPoint(anchor); + this.localAnchorB = this.bodyB.GetLocalPoint(anchor); + this.localAxisA = this.bodyA.GetLocalVector(axis); + this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle(); + } + Box2D.inherit(b2PulleyJoint, Box2D.Dynamics.Joints.b2Joint); + b2PulleyJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2PulleyJoint.b2PulleyJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_groundAnchor1 = new b2Vec2(); + this.m_groundAnchor2 = new b2Vec2(); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_u1 = new b2Vec2(); + this.m_u2 = new b2Vec2(); + }; + b2PulleyJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2PulleyJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2PulleyJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse * this.m_u2.x, inv_dt * this.m_impulse * this.m_u2.y); + } + b2PulleyJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2PulleyJoint.prototype.GetGroundAnchorA = function() { + var a = this.m_ground.m_xf.position.Copy(); + a.Add(this.m_groundAnchor1); + return a; + } + b2PulleyJoint.prototype.GetGroundAnchorB = function() { + var a = this.m_ground.m_xf.position.Copy(); + a.Add(this.m_groundAnchor2); + return a; + } + b2PulleyJoint.prototype.GetLength1 = function() { + var p = this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + var sX = this.m_ground.m_xf.position.x + this.m_groundAnchor1.x; + var sY = this.m_ground.m_xf.position.y + this.m_groundAnchor1.y; + var dX = p.x - sX; + var dY = p.y - sY; + return Math.sqrt(dX * dX + dY * dY); + } + b2PulleyJoint.prototype.GetLength2 = function() { + var p = this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + var sX = this.m_ground.m_xf.position.x + this.m_groundAnchor2.x; + var sY = this.m_ground.m_xf.position.y + this.m_groundAnchor2.y; + var dX = p.x - sX; + var dY = p.y - sY; + return Math.sqrt(dX * dX + dY * dY); + } + b2PulleyJoint.prototype.GetRatio = function() { + return this.m_ratio; + } + b2PulleyJoint.prototype.b2PulleyJoint = function(def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_ground = this.m_bodyA.m_world.m_groundBody; + this.m_groundAnchor1.x = def.groundAnchorA.x - this.m_ground.m_xf.position.x; + this.m_groundAnchor1.y = def.groundAnchorA.y - this.m_ground.m_xf.position.y; + this.m_groundAnchor2.x = def.groundAnchorB.x - this.m_ground.m_xf.position.x; + this.m_groundAnchor2.y = def.groundAnchorB.y - this.m_ground.m_xf.position.y; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_ratio = def.ratio; + this.m_constant = def.lengthA + this.m_ratio * def.lengthB; + this.m_maxLength1 = b2Math.Min(def.maxLengthA, this.m_constant - this.m_ratio * b2PulleyJoint.b2_minPulleyLength); + this.m_maxLength2 = b2Math.Min(def.maxLengthB, (this.m_constant - b2PulleyJoint.b2_minPulleyLength) / this.m_ratio); + this.m_impulse = 0.0; + this.m_limitImpulse1 = 0.0; + this.m_limitImpulse2 = 0.0; + } + b2PulleyJoint.prototype.InitVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var p1X = bA.m_sweep.c.x + r1X; + var p1Y = bA.m_sweep.c.y + r1Y; + var p2X = bB.m_sweep.c.x + r2X; + var p2Y = bB.m_sweep.c.y + r2Y; + var s1X = this.m_ground.m_xf.position.x + this.m_groundAnchor1.x; + var s1Y = this.m_ground.m_xf.position.y + this.m_groundAnchor1.y; + var s2X = this.m_ground.m_xf.position.x + this.m_groundAnchor2.x; + var s2Y = this.m_ground.m_xf.position.y + this.m_groundAnchor2.y; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + var length1 = this.m_u1.Length(); + var length2 = this.m_u2.Length(); + if(length1 > b2Settings.b2_linearSlop) { + this.m_u1.Multiply(1.0 / length1); + } else { + this.m_u1.SetZero(); + } + if(length2 > b2Settings.b2_linearSlop) { + this.m_u2.Multiply(1.0 / length2); + } else { + this.m_u2.SetZero(); + } + var C = this.m_constant - length1 - this.m_ratio * length2; + if(C > 0.0) { + this.m_state = b2Joint.e_inactiveLimit; + this.m_impulse = 0.0; + } else { + this.m_state = b2Joint.e_atUpperLimit; + } + if(length1 < this.m_maxLength1) { + this.m_limitState1 = b2Joint.e_inactiveLimit; + this.m_limitImpulse1 = 0.0; + } else { + this.m_limitState1 = b2Joint.e_atUpperLimit; + } + if(length2 < this.m_maxLength2) { + this.m_limitState2 = b2Joint.e_inactiveLimit; + this.m_limitImpulse2 = 0.0; + } else { + this.m_limitState2 = b2Joint.e_atUpperLimit; + } + var cr1u1 = r1X * this.m_u1.y - r1Y * this.m_u1.x; + var cr2u2 = r2X * this.m_u2.y - r2Y * this.m_u2.x; + this.m_limitMass1 = bA.m_invMass + bA.m_invI * cr1u1 * cr1u1; + this.m_limitMass2 = bB.m_invMass + bB.m_invI * cr2u2 * cr2u2; + this.m_pulleyMass = this.m_limitMass1 + this.m_ratio * this.m_ratio * this.m_limitMass2; + this.m_limitMass1 = 1.0 / this.m_limitMass1; + this.m_limitMass2 = 1.0 / this.m_limitMass2; + this.m_pulleyMass = 1.0 / this.m_pulleyMass; + if(step.warmStarting) { + this.m_impulse *= step.dtRatio; + this.m_limitImpulse1 *= step.dtRatio; + this.m_limitImpulse2 *= step.dtRatio; + var P1X = ((-this.m_impulse) - this.m_limitImpulse1) * this.m_u1.x; + var P1Y = ((-this.m_impulse) - this.m_limitImpulse1) * this.m_u1.y; + var P2X = ((-this.m_ratio * this.m_impulse) - this.m_limitImpulse2) * this.m_u2.x; + var P2Y = ((-this.m_ratio * this.m_impulse) - this.m_limitImpulse2) * this.m_u2.y; + bA.m_linearVelocity.x += bA.m_invMass * P1X; + bA.m_linearVelocity.y += bA.m_invMass * P1Y; + bA.m_angularVelocity += bA.m_invI * (r1X * P1Y - r1Y * P1X); + bB.m_linearVelocity.x += bB.m_invMass * P2X; + bB.m_linearVelocity.y += bB.m_invMass * P2Y; + bB.m_angularVelocity += bB.m_invI * (r2X * P2Y - r2Y * P2X); + } else { + this.m_impulse = 0.0; + this.m_limitImpulse1 = 0.0; + this.m_limitImpulse2 = 0.0; + } + } + b2PulleyJoint.prototype.SolveVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var v1X = 0; + var v1Y = 0; + var v2X = 0; + var v2Y = 0; + var P1X = 0; + var P1Y = 0; + var P2X = 0; + var P2Y = 0; + var Cdot = 0; + var impulse = 0; + var oldImpulse = 0; + if(this.m_state == b2Joint.e_atUpperLimit) { + v1X = bA.m_linearVelocity.x + ((-bA.m_angularVelocity * r1Y)); + v1Y = bA.m_linearVelocity.y + (bA.m_angularVelocity * r1X); + v2X = bB.m_linearVelocity.x + ((-bB.m_angularVelocity * r2Y)); + v2Y = bB.m_linearVelocity.y + (bB.m_angularVelocity * r2X); + Cdot = (-(this.m_u1.x * v1X + this.m_u1.y * v1Y)) - this.m_ratio * (this.m_u2.x * v2X + this.m_u2.y * v2Y); + impulse = this.m_pulleyMass * ((-Cdot)); + oldImpulse = this.m_impulse; + this.m_impulse = b2Math.Max(0.0, this.m_impulse + impulse); + impulse = this.m_impulse - oldImpulse; + P1X = (-impulse * this.m_u1.x); + P1Y = (-impulse * this.m_u1.y); + P2X = (-this.m_ratio * impulse * this.m_u2.x); + P2Y = (-this.m_ratio * impulse * this.m_u2.y); + bA.m_linearVelocity.x += bA.m_invMass * P1X; + bA.m_linearVelocity.y += bA.m_invMass * P1Y; + bA.m_angularVelocity += bA.m_invI * (r1X * P1Y - r1Y * P1X); + bB.m_linearVelocity.x += bB.m_invMass * P2X; + bB.m_linearVelocity.y += bB.m_invMass * P2Y; + bB.m_angularVelocity += bB.m_invI * (r2X * P2Y - r2Y * P2X); + } + if(this.m_limitState1 == b2Joint.e_atUpperLimit) { + v1X = bA.m_linearVelocity.x + ((-bA.m_angularVelocity * r1Y)); + v1Y = bA.m_linearVelocity.y + (bA.m_angularVelocity * r1X); + Cdot = (-(this.m_u1.x * v1X + this.m_u1.y * v1Y)); + impulse = (-this.m_limitMass1 * Cdot); + oldImpulse = this.m_limitImpulse1; + this.m_limitImpulse1 = b2Math.Max(0.0, this.m_limitImpulse1 + impulse); + impulse = this.m_limitImpulse1 - oldImpulse; + P1X = (-impulse * this.m_u1.x); + P1Y = (-impulse * this.m_u1.y); + bA.m_linearVelocity.x += bA.m_invMass * P1X; + bA.m_linearVelocity.y += bA.m_invMass * P1Y; + bA.m_angularVelocity += bA.m_invI * (r1X * P1Y - r1Y * P1X); + } + if(this.m_limitState2 == b2Joint.e_atUpperLimit) { + v2X = bB.m_linearVelocity.x + ((-bB.m_angularVelocity * r2Y)); + v2Y = bB.m_linearVelocity.y + (bB.m_angularVelocity * r2X); + Cdot = (-(this.m_u2.x * v2X + this.m_u2.y * v2Y)); + impulse = (-this.m_limitMass2 * Cdot); + oldImpulse = this.m_limitImpulse2; + this.m_limitImpulse2 = b2Math.Max(0.0, this.m_limitImpulse2 + impulse); + impulse = this.m_limitImpulse2 - oldImpulse; + P2X = (-impulse * this.m_u2.x); + P2Y = (-impulse * this.m_u2.y); + bB.m_linearVelocity.x += bB.m_invMass * P2X; + bB.m_linearVelocity.y += bB.m_invMass * P2Y; + bB.m_angularVelocity += bB.m_invI * (r2X * P2Y - r2Y * P2X); + } + } + b2PulleyJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var s1X = this.m_ground.m_xf.position.x + this.m_groundAnchor1.x; + var s1Y = this.m_ground.m_xf.position.y + this.m_groundAnchor1.y; + var s2X = this.m_ground.m_xf.position.x + this.m_groundAnchor2.x; + var s2Y = this.m_ground.m_xf.position.y + this.m_groundAnchor2.y; + var r1X = 0; + var r1Y = 0; + var r2X = 0; + var r2Y = 0; + var p1X = 0; + var p1Y = 0; + var p2X = 0; + var p2Y = 0; + var length1 = 0; + var length2 = 0; + var C = 0; + var impulse = 0; + var oldImpulse = 0; + var oldLimitPositionImpulse = 0; + var tX = 0; + var linearError = 0.0; + if(this.m_state == b2Joint.e_atUpperLimit) { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + p1X = bA.m_sweep.c.x + r1X; + p1Y = bA.m_sweep.c.y + r1Y; + p2X = bB.m_sweep.c.x + r2X; + p2Y = bB.m_sweep.c.y + r2Y; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + length1 = this.m_u1.Length(); + length2 = this.m_u2.Length(); + if(length1 > b2Settings.b2_linearSlop) { + this.m_u1.Multiply(1.0 / length1); + } else { + this.m_u1.SetZero(); + } + if(length2 > b2Settings.b2_linearSlop) { + this.m_u2.Multiply(1.0 / length2); + } else { + this.m_u2.SetZero(); + } + C = this.m_constant - length1 - this.m_ratio * length2; + linearError = b2Math.Max(linearError, (-C)); + C = b2Math.Clamp(C + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + impulse = (-this.m_pulleyMass * C); + p1X = (-impulse * this.m_u1.x); + p1Y = (-impulse * this.m_u1.y); + p2X = (-this.m_ratio * impulse * this.m_u2.x); + p2Y = (-this.m_ratio * impulse * this.m_u2.y); + bA.m_sweep.c.x += bA.m_invMass * p1X; + bA.m_sweep.c.y += bA.m_invMass * p1Y; + bA.m_sweep.a += bA.m_invI * (r1X * p1Y - r1Y * p1X); + bB.m_sweep.c.x += bB.m_invMass * p2X; + bB.m_sweep.c.y += bB.m_invMass * p2Y; + bB.m_sweep.a += bB.m_invI * (r2X * p2Y - r2Y * p2X); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + } + if(this.m_limitState1 == b2Joint.e_atUpperLimit) { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + p1X = bA.m_sweep.c.x + r1X; + p1Y = bA.m_sweep.c.y + r1Y; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + length1 = this.m_u1.Length(); + if(length1 > b2Settings.b2_linearSlop) { + this.m_u1.x *= 1.0 / length1; + this.m_u1.y *= 1.0 / length1; + } else { + this.m_u1.SetZero(); + } + C = this.m_maxLength1 - length1; + linearError = b2Math.Max(linearError, (-C)); + C = b2Math.Clamp(C + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + impulse = (-this.m_limitMass1 * C); + p1X = (-impulse * this.m_u1.x); + p1Y = (-impulse * this.m_u1.y); + bA.m_sweep.c.x += bA.m_invMass * p1X; + bA.m_sweep.c.y += bA.m_invMass * p1Y; + bA.m_sweep.a += bA.m_invI * (r1X * p1Y - r1Y * p1X); + bA.SynchronizeTransform(); + } + if(this.m_limitState2 == b2Joint.e_atUpperLimit) { + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + p2X = bB.m_sweep.c.x + r2X; + p2Y = bB.m_sweep.c.y + r2Y; + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + length2 = this.m_u2.Length(); + if(length2 > b2Settings.b2_linearSlop) { + this.m_u2.x *= 1.0 / length2; + this.m_u2.y *= 1.0 / length2; + } else { + this.m_u2.SetZero(); + } + C = this.m_maxLength2 - length2; + linearError = b2Math.Max(linearError, (-C)); + C = b2Math.Clamp(C + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + impulse = (-this.m_limitMass2 * C); + p2X = (-impulse * this.m_u2.x); + p2Y = (-impulse * this.m_u2.y); + bB.m_sweep.c.x += bB.m_invMass * p2X; + bB.m_sweep.c.y += bB.m_invMass * p2Y; + bB.m_sweep.a += bB.m_invI * (r2X * p2Y - r2Y * p2X); + bB.SynchronizeTransform(); + } + return linearError < b2Settings.b2_linearSlop; + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.Joints.b2PulleyJoint.b2_minPulleyLength = 2.0; + }); + Box2D.inherit(b2PulleyJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2PulleyJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2PulleyJointDef.b2PulleyJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.groundAnchorA = new b2Vec2(); + this.groundAnchorB = new b2Vec2(); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2PulleyJointDef.prototype.b2PulleyJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_pulleyJoint; + this.groundAnchorA.Set((-1.0), 1.0); + this.groundAnchorB.Set(1.0, 1.0); + this.localAnchorA.Set((-1.0), 0.0); + this.localAnchorB.Set(1.0, 0.0); + this.lengthA = 0.0; + this.maxLengthA = 0.0; + this.lengthB = 0.0; + this.maxLengthB = 0.0; + this.ratio = 1.0; + this.collideConnected = true; + } + b2PulleyJointDef.prototype.Initialize = function(bA, bB, gaA, gaB, anchorA, anchorB, r) { + if(r === undefined) r = 0; + this.bodyA = bA; + this.bodyB = bB; + this.groundAnchorA.SetV(gaA); + this.groundAnchorB.SetV(gaB); + this.localAnchorA = this.bodyA.GetLocalPoint(anchorA); + this.localAnchorB = this.bodyB.GetLocalPoint(anchorB); + var d1X = anchorA.x - gaA.x; + var d1Y = anchorA.y - gaA.y; + this.lengthA = Math.sqrt(d1X * d1X + d1Y * d1Y); + var d2X = anchorB.x - gaB.x; + var d2Y = anchorB.y - gaB.y; + this.lengthB = Math.sqrt(d2X * d2X + d2Y * d2Y); + this.ratio = r; + var C = this.lengthA + this.ratio * this.lengthB; + this.maxLengthA = C - this.ratio * b2PulleyJoint.b2_minPulleyLength; + this.maxLengthB = (C - b2PulleyJoint.b2_minPulleyLength) / this.ratio; + } + Box2D.inherit(b2RevoluteJoint, Box2D.Dynamics.Joints.b2Joint); + b2RevoluteJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2RevoluteJoint.b2RevoluteJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.K = new b2Mat22(); + this.K1 = new b2Mat22(); + this.K2 = new b2Mat22(); + this.K3 = new b2Mat22(); + this.impulse3 = new b2Vec3(); + this.impulse2 = new b2Vec2(); + this.reduced = new b2Vec2(); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_impulse = new b2Vec3(); + this.m_mass = new b2Mat33(); + }; + b2RevoluteJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2RevoluteJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2RevoluteJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse.x, inv_dt * this.m_impulse.y); + } + b2RevoluteJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.z; + } + b2RevoluteJoint.prototype.GetJointAngle = function() { + return this.m_bodyB.m_sweep.a - this.m_bodyA.m_sweep.a - this.m_referenceAngle; + } + b2RevoluteJoint.prototype.GetJointSpeed = function() { + return this.m_bodyB.m_angularVelocity - this.m_bodyA.m_angularVelocity; + } + b2RevoluteJoint.prototype.IsLimitEnabled = function() { + return this.m_enableLimit; + } + b2RevoluteJoint.prototype.EnableLimit = function(flag) { + this.m_enableLimit = flag; + } + b2RevoluteJoint.prototype.GetLowerLimit = function() { + return this.m_lowerAngle; + } + b2RevoluteJoint.prototype.GetUpperLimit = function() { + return this.m_upperAngle; + } + b2RevoluteJoint.prototype.SetLimits = function(lower, upper) { + if(lower === undefined) lower = 0; + if(upper === undefined) upper = 0; + this.m_lowerAngle = lower; + this.m_upperAngle = upper; + } + b2RevoluteJoint.prototype.IsMotorEnabled = function() { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + return this.m_enableMotor; + } + b2RevoluteJoint.prototype.EnableMotor = function(flag) { + this.m_enableMotor = flag; + } + b2RevoluteJoint.prototype.SetMotorSpeed = function(speed) { + if(speed === undefined) speed = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_motorSpeed = speed; + } + b2RevoluteJoint.prototype.GetMotorSpeed = function() { + return this.m_motorSpeed; + } + b2RevoluteJoint.prototype.SetMaxMotorTorque = function(torque) { + if(torque === undefined) torque = 0; + this.m_maxMotorTorque = torque; + } + b2RevoluteJoint.prototype.GetMotorTorque = function() { + return this.m_maxMotorTorque; + } + b2RevoluteJoint.prototype.b2RevoluteJoint = function(def) { + this.__super.b2Joint.call(this, def); + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_referenceAngle = def.referenceAngle; + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + this.m_lowerAngle = def.lowerAngle; + this.m_upperAngle = def.upperAngle; + this.m_maxMotorTorque = def.maxMotorTorque; + this.m_motorSpeed = def.motorSpeed; + this.m_enableLimit = def.enableLimit; + this.m_enableMotor = def.enableMotor; + this.m_limitState = b2Joint.e_inactiveLimit; + } + b2RevoluteJoint.prototype.InitVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + if(this.m_enableMotor || this.m_enableLimit) {} + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var m1 = bA.m_invMass; + var m2 = bB.m_invMass; + var i1 = bA.m_invI; + var i2 = bB.m_invI; + this.m_mass.col1.x = m1 + m2 + r1Y * r1Y * i1 + r2Y * r2Y * i2; + this.m_mass.col2.x = (-r1Y * r1X * i1) - r2Y * r2X * i2; + this.m_mass.col3.x = (-r1Y * i1) - r2Y * i2; + this.m_mass.col1.y = this.m_mass.col2.x; + this.m_mass.col2.y = m1 + m2 + r1X * r1X * i1 + r2X * r2X * i2; + this.m_mass.col3.y = r1X * i1 + r2X * i2; + this.m_mass.col1.z = this.m_mass.col3.x; + this.m_mass.col2.z = this.m_mass.col3.y; + this.m_mass.col3.z = i1 + i2; + this.m_motorMass = 1.0 / (i1 + i2); + if(this.m_enableMotor == false) { + this.m_motorImpulse = 0.0; + } + if(this.m_enableLimit) { + var jointAngle = bB.m_sweep.a - bA.m_sweep.a - this.m_referenceAngle; + if(b2Math.Abs(this.m_upperAngle - this.m_lowerAngle) < 2.0 * b2Settings.b2_angularSlop) { + this.m_limitState = b2Joint.e_equalLimits; + } else if(jointAngle <= this.m_lowerAngle) { + if(this.m_limitState != b2Joint.e_atLowerLimit) { + this.m_impulse.z = 0.0; + } + this.m_limitState = b2Joint.e_atLowerLimit; + } else if(jointAngle >= this.m_upperAngle) { + if(this.m_limitState != b2Joint.e_atUpperLimit) { + this.m_impulse.z = 0.0; + } + this.m_limitState = b2Joint.e_atUpperLimit; + } else { + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_impulse.z = 0.0; + } + } else { + this.m_limitState = b2Joint.e_inactiveLimit; + } + if(step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_motorImpulse *= step.dtRatio; + var PX = this.m_impulse.x; + var PY = this.m_impulse.y; + bA.m_linearVelocity.x -= m1 * PX; + bA.m_linearVelocity.y -= m1 * PY; + bA.m_angularVelocity -= i1 * ((r1X * PY - r1Y * PX) + this.m_motorImpulse + this.m_impulse.z); + bB.m_linearVelocity.x += m2 * PX; + bB.m_linearVelocity.y += m2 * PY; + bB.m_angularVelocity += i2 * ((r2X * PY - r2Y * PX) + this.m_motorImpulse + this.m_impulse.z); + } else { + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + } + } + b2RevoluteJoint.prototype.SolveVelocityConstraints = function(step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + var newImpulse = 0; + var r1X = 0; + var r1Y = 0; + var r2X = 0; + var r2Y = 0; + var v1 = bA.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var v2 = bB.m_linearVelocity; + var w2 = bB.m_angularVelocity; + var m1 = bA.m_invMass; + var m2 = bB.m_invMass; + var i1 = bA.m_invI; + var i2 = bB.m_invI; + if(this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits) { + var Cdot = w2 - w1 - this.m_motorSpeed; + var impulse = this.m_motorMass * ((-Cdot)); + var oldImpulse = this.m_motorImpulse; + var maxImpulse = step.dt * this.m_maxMotorTorque; + this.m_motorImpulse = b2Math.Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_motorImpulse - oldImpulse; + w1 -= i1 * impulse; + w2 += i2 * impulse; + } + if(this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var Cdot1X = v2.x + ((-w2 * r2Y)) - v1.x - ((-w1 * r1Y)); + var Cdot1Y = v2.y + (w2 * r2X) - v1.y - (w1 * r1X); + var Cdot2 = w2 - w1; + this.m_mass.Solve33(this.impulse3, (-Cdot1X), (-Cdot1Y), (-Cdot2)); + if(this.m_limitState == b2Joint.e_equalLimits) { + this.m_impulse.Add(this.impulse3); + } else if(this.m_limitState == b2Joint.e_atLowerLimit) { + newImpulse = this.m_impulse.z + this.impulse3.z; + if(newImpulse < 0.0) { + this.m_mass.Solve22(this.reduced, (-Cdot1X), (-Cdot1Y)); + this.impulse3.x = this.reduced.x; + this.impulse3.y = this.reduced.y; + this.impulse3.z = (-this.m_impulse.z); + this.m_impulse.x += this.reduced.x; + this.m_impulse.y += this.reduced.y; + this.m_impulse.z = 0.0; + } + } else if(this.m_limitState == b2Joint.e_atUpperLimit) { + newImpulse = this.m_impulse.z + this.impulse3.z; + if(newImpulse > 0.0) { + this.m_mass.Solve22(this.reduced, (-Cdot1X), (-Cdot1Y)); + this.impulse3.x = this.reduced.x; + this.impulse3.y = this.reduced.y; + this.impulse3.z = (-this.m_impulse.z); + this.m_impulse.x += this.reduced.x; + this.m_impulse.y += this.reduced.y; + this.m_impulse.z = 0.0; + } + } + v1.x -= m1 * this.impulse3.x; + v1.y -= m1 * this.impulse3.y; + w1 -= i1 * (r1X * this.impulse3.y - r1Y * this.impulse3.x + this.impulse3.z); + v2.x += m2 * this.impulse3.x; + v2.y += m2 * this.impulse3.y; + w2 += i2 * (r2X * this.impulse3.y - r2Y * this.impulse3.x + this.impulse3.z); + } else { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var CdotX = v2.x + ((-w2 * r2Y)) - v1.x - ((-w1 * r1Y)); + var CdotY = v2.y + (w2 * r2X) - v1.y - (w1 * r1X); + this.m_mass.Solve22(this.impulse2, (-CdotX), (-CdotY)); + this.m_impulse.x += this.impulse2.x; + this.m_impulse.y += this.impulse2.y; + v1.x -= m1 * this.impulse2.x; + v1.y -= m1 * this.impulse2.y; + w1 -= i1 * (r1X * this.impulse2.y - r1Y * this.impulse2.x); + v2.x += m2 * this.impulse2.x; + v2.y += m2 * this.impulse2.y; + w2 += i2 * (r2X * this.impulse2.y - r2Y * this.impulse2.x); + } + bA.m_linearVelocity.SetV(v1); + bA.m_angularVelocity = w1; + bB.m_linearVelocity.SetV(v2); + bB.m_angularVelocity = w2; + } + b2RevoluteJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var oldLimitImpulse = 0; + var C = 0; + var tMat; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var angularError = 0.0; + var positionError = 0.0; + var tX = 0; + var impulseX = 0; + var impulseY = 0; + if(this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + var angle = bB.m_sweep.a - bA.m_sweep.a - this.m_referenceAngle; + var limitImpulse = 0.0; + if(this.m_limitState == b2Joint.e_equalLimits) { + C = b2Math.Clamp(angle - this.m_lowerAngle, (-b2Settings.b2_maxAngularCorrection), b2Settings.b2_maxAngularCorrection); + limitImpulse = (-this.m_motorMass * C); + angularError = b2Math.Abs(C); + } else if(this.m_limitState == b2Joint.e_atLowerLimit) { + C = angle - this.m_lowerAngle; + angularError = (-C); + C = b2Math.Clamp(C + b2Settings.b2_angularSlop, (-b2Settings.b2_maxAngularCorrection), 0.0); + limitImpulse = (-this.m_motorMass * C); + } else if(this.m_limitState == b2Joint.e_atUpperLimit) { + C = angle - this.m_upperAngle; + angularError = C; + C = b2Math.Clamp(C - b2Settings.b2_angularSlop, 0.0, b2Settings.b2_maxAngularCorrection); + limitImpulse = (-this.m_motorMass * C); + } + bA.m_sweep.a -= bA.m_invI * limitImpulse; + bB.m_sweep.a += bB.m_invI * limitImpulse; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + } { + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var CX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var CY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + var CLengthSquared = CX * CX + CY * CY; + var CLength = Math.sqrt(CLengthSquared); + positionError = CLength; + var invMass1 = bA.m_invMass; + var invMass2 = bB.m_invMass; + var invI1 = bA.m_invI; + var invI2 = bB.m_invI; + var k_allowedStretch = 10.0 * b2Settings.b2_linearSlop; + if(CLengthSquared > k_allowedStretch * k_allowedStretch) { + var uX = CX / CLength; + var uY = CY / CLength; + var k = invMass1 + invMass2; + var m = 1.0 / k; + impulseX = m * ((-CX)); + impulseY = m * ((-CY)); + var k_beta = 0.5; + bA.m_sweep.c.x -= k_beta * invMass1 * impulseX; + bA.m_sweep.c.y -= k_beta * invMass1 * impulseY; + bB.m_sweep.c.x += k_beta * invMass2 * impulseX; + bB.m_sweep.c.y += k_beta * invMass2 * impulseY; + CX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + CY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + } + this.K1.col1.x = invMass1 + invMass2; + this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; + this.K1.col2.y = invMass1 + invMass2; + this.K2.col1.x = invI1 * r1Y * r1Y; + this.K2.col2.x = (-invI1 * r1X * r1Y); + this.K2.col1.y = (-invI1 * r1X * r1Y); + this.K2.col2.y = invI1 * r1X * r1X; + this.K3.col1.x = invI2 * r2Y * r2Y; + this.K3.col2.x = (-invI2 * r2X * r2Y); + this.K3.col1.y = (-invI2 * r2X * r2Y); + this.K3.col2.y = invI2 * r2X * r2X; + this.K.SetM(this.K1); + this.K.AddM(this.K2); + this.K.AddM(this.K3); + this.K.Solve(b2RevoluteJoint.tImpulse, (-CX), (-CY)); + impulseX = b2RevoluteJoint.tImpulse.x; + impulseY = b2RevoluteJoint.tImpulse.y; + bA.m_sweep.c.x -= bA.m_invMass * impulseX; + bA.m_sweep.c.y -= bA.m_invMass * impulseY; + bA.m_sweep.a -= bA.m_invI * (r1X * impulseY - r1Y * impulseX); + bB.m_sweep.c.x += bB.m_invMass * impulseX; + bB.m_sweep.c.y += bB.m_invMass * impulseY; + bB.m_sweep.a += bB.m_invI * (r2X * impulseY - r2Y * impulseX); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + } + return positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.postDefs.push(function() { + Box2D.Dynamics.Joints.b2RevoluteJoint.tImpulse = new b2Vec2(); + }); + Box2D.inherit(b2RevoluteJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2RevoluteJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2RevoluteJointDef.b2RevoluteJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2RevoluteJointDef.prototype.b2RevoluteJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_revoluteJoint; + this.localAnchorA.Set(0.0, 0.0); + this.localAnchorB.Set(0.0, 0.0); + this.referenceAngle = 0.0; + this.lowerAngle = 0.0; + this.upperAngle = 0.0; + this.maxMotorTorque = 0.0; + this.motorSpeed = 0.0; + this.enableLimit = false; + this.enableMotor = false; + } + b2RevoluteJointDef.prototype.Initialize = function(bA, bB, anchor) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.GetLocalPoint(anchor); + this.localAnchorB = this.bodyB.GetLocalPoint(anchor); + this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle(); + } + Box2D.inherit(b2WeldJoint, Box2D.Dynamics.Joints.b2Joint); + b2WeldJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2WeldJoint.b2WeldJoint = function() { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchorA = new b2Vec2(); + this.m_localAnchorB = new b2Vec2(); + this.m_impulse = new b2Vec3(); + this.m_mass = new b2Mat33(); + }; + b2WeldJoint.prototype.GetAnchorA = function() { + return this.m_bodyA.GetWorldPoint(this.m_localAnchorA); + } + b2WeldJoint.prototype.GetAnchorB = function() { + return this.m_bodyB.GetWorldPoint(this.m_localAnchorB); + } + b2WeldJoint.prototype.GetReactionForce = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse.x, inv_dt * this.m_impulse.y); + } + b2WeldJoint.prototype.GetReactionTorque = function(inv_dt) { + if(inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.z; + } + b2WeldJoint.prototype.b2WeldJoint = function(def) { + this.__super.b2Joint.call(this, def); + this.m_localAnchorA.SetV(def.localAnchorA); + this.m_localAnchorB.SetV(def.localAnchorB); + this.m_referenceAngle = def.referenceAngle; + this.m_impulse.SetZero(); + this.m_mass = new b2Mat33(); + } + b2WeldJoint.prototype.InitVelocityConstraints = function(step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + this.m_mass.col1.x = mA + mB + rAY * rAY * iA + rBY * rBY * iB; + this.m_mass.col2.x = (-rAY * rAX * iA) - rBY * rBX * iB; + this.m_mass.col3.x = (-rAY * iA) - rBY * iB; + this.m_mass.col1.y = this.m_mass.col2.x; + this.m_mass.col2.y = mA + mB + rAX * rAX * iA + rBX * rBX * iB; + this.m_mass.col3.y = rAX * iA + rBX * iB; + this.m_mass.col1.z = this.m_mass.col3.x; + this.m_mass.col2.z = this.m_mass.col3.y; + this.m_mass.col3.z = iA + iB; + if(step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_impulse.z *= step.dtRatio; + bA.m_linearVelocity.x -= mA * this.m_impulse.x; + bA.m_linearVelocity.y -= mA * this.m_impulse.y; + bA.m_angularVelocity -= iA * (rAX * this.m_impulse.y - rAY * this.m_impulse.x + this.m_impulse.z); + bB.m_linearVelocity.x += mB * this.m_impulse.x; + bB.m_linearVelocity.y += mB * this.m_impulse.y; + bB.m_angularVelocity += iB * (rBX * this.m_impulse.y - rBY * this.m_impulse.x + this.m_impulse.z); + } else { + this.m_impulse.SetZero(); + } + } + b2WeldJoint.prototype.SolveVelocityConstraints = function(step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var vA = bA.m_linearVelocity; + var wA = bA.m_angularVelocity; + var vB = bB.m_linearVelocity; + var wB = bB.m_angularVelocity; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var Cdot1X = vB.x - wB * rBY - vA.x + wA * rAY; + var Cdot1Y = vB.y + wB * rBX - vA.y - wA * rAX; + var Cdot2 = wB - wA; + var impulse = new b2Vec3(); + this.m_mass.Solve33(impulse, (-Cdot1X), (-Cdot1Y), (-Cdot2)); + this.m_impulse.Add(impulse); + vA.x -= mA * impulse.x; + vA.y -= mA * impulse.y; + wA -= iA * (rAX * impulse.y - rAY * impulse.x + impulse.z); + vB.x += mB * impulse.x; + vB.y += mB * impulse.y; + wB += iB * (rBX * impulse.y - rBY * impulse.x + impulse.z); + bA.m_angularVelocity = wA; + bB.m_angularVelocity = wB; + } + b2WeldJoint.prototype.SolvePositionConstraints = function(baumgarte) { + if(baumgarte === undefined) baumgarte = 0; + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + var C1X = bB.m_sweep.c.x + rBX - bA.m_sweep.c.x - rAX; + var C1Y = bB.m_sweep.c.y + rBY - bA.m_sweep.c.y - rAY; + var C2 = bB.m_sweep.a - bA.m_sweep.a - this.m_referenceAngle; + var k_allowedStretch = 10.0 * b2Settings.b2_linearSlop; + var positionError = Math.sqrt(C1X * C1X + C1Y * C1Y); + var angularError = b2Math.Abs(C2); + if(positionError > k_allowedStretch) { + iA *= 1.0; + iB *= 1.0; + } + this.m_mass.col1.x = mA + mB + rAY * rAY * iA + rBY * rBY * iB; + this.m_mass.col2.x = (-rAY * rAX * iA) - rBY * rBX * iB; + this.m_mass.col3.x = (-rAY * iA) - rBY * iB; + this.m_mass.col1.y = this.m_mass.col2.x; + this.m_mass.col2.y = mA + mB + rAX * rAX * iA + rBX * rBX * iB; + this.m_mass.col3.y = rAX * iA + rBX * iB; + this.m_mass.col1.z = this.m_mass.col3.x; + this.m_mass.col2.z = this.m_mass.col3.y; + this.m_mass.col3.z = iA + iB; + var impulse = new b2Vec3(); + this.m_mass.Solve33(impulse, (-C1X), (-C1Y), (-C2)); + bA.m_sweep.c.x -= mA * impulse.x; + bA.m_sweep.c.y -= mA * impulse.y; + bA.m_sweep.a -= iA * (rAX * impulse.y - rAY * impulse.x + impulse.z); + bB.m_sweep.c.x += mB * impulse.x; + bB.m_sweep.c.y += mB * impulse.y; + bB.m_sweep.a += iB * (rBX * impulse.y - rBY * impulse.x + impulse.z); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.inherit(b2WeldJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2WeldJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2WeldJointDef.b2WeldJointDef = function() { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2WeldJointDef.prototype.b2WeldJointDef = function() { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_weldJoint; + this.referenceAngle = 0.0; + } + b2WeldJointDef.prototype.Initialize = function(bA, bB, anchor) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA.SetV(this.bodyA.GetLocalPoint(anchor)); + this.localAnchorB.SetV(this.bodyB.GetLocalPoint(anchor)); + this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle(); + } +})(); +(function() { + var b2DebugDraw = Box2D.Dynamics.b2DebugDraw; + b2DebugDraw.b2DebugDraw = function() { + this.m_drawScale = 1.0; + this.m_lineThickness = 1.0; + this.m_alpha = 1.0; + this.m_fillAlpha = 1.0; + this.m_xformScale = 1.0; + var __this = this; + //#WORKAROUND + this.m_sprite = { + graphics: { + clear: function() { + __this.m_ctx.clearRect(0, 0, __this.m_ctx.canvas.width, __this.m_ctx.canvas.height) + } + } + }; + }; + b2DebugDraw.prototype._color = function(color, alpha) { + return "rgba(" + ((color & 0xFF0000) >> 16) + "," + ((color & 0xFF00) >> 8) + "," + (color & 0xFF) + "," + alpha + ")"; + }; + b2DebugDraw.prototype.b2DebugDraw = function() { + this.m_drawFlags = 0; + }; + b2DebugDraw.prototype.SetFlags = function(flags) { + if(flags === undefined) flags = 0; + this.m_drawFlags = flags; + }; + b2DebugDraw.prototype.GetFlags = function() { + return this.m_drawFlags; + }; + b2DebugDraw.prototype.AppendFlags = function(flags) { + if(flags === undefined) flags = 0; + this.m_drawFlags |= flags; + }; + b2DebugDraw.prototype.ClearFlags = function(flags) { + if(flags === undefined) flags = 0; + this.m_drawFlags &= ~flags; + }; + b2DebugDraw.prototype.SetSprite = function(sprite) { + this.m_ctx = sprite; + }; + b2DebugDraw.prototype.GetSprite = function() { + return this.m_ctx; + }; + b2DebugDraw.prototype.SetDrawScale = function(drawScale) { + if(drawScale === undefined) drawScale = 0; + this.m_drawScale = drawScale; + }; + b2DebugDraw.prototype.GetDrawScale = function() { + return this.m_drawScale; + }; + b2DebugDraw.prototype.SetLineThickness = function(lineThickness) { + if(lineThickness === undefined) lineThickness = 0; + this.m_lineThickness = lineThickness; + this.m_ctx.strokeWidth = lineThickness; + }; + b2DebugDraw.prototype.GetLineThickness = function() { + return this.m_lineThickness; + }; + b2DebugDraw.prototype.SetAlpha = function(alpha) { + if(alpha === undefined) alpha = 0; + this.m_alpha = alpha; + }; + b2DebugDraw.prototype.GetAlpha = function() { + return this.m_alpha; + }; + b2DebugDraw.prototype.SetFillAlpha = function(alpha) { + if(alpha === undefined) alpha = 0; + this.m_fillAlpha = alpha; + }; + b2DebugDraw.prototype.GetFillAlpha = function() { + return this.m_fillAlpha; + }; + b2DebugDraw.prototype.SetXFormScale = function(xformScale) { + if(xformScale === undefined) xformScale = 0; + this.m_xformScale = xformScale; + }; + b2DebugDraw.prototype.GetXFormScale = function() { + return this.m_xformScale; + }; + b2DebugDraw.prototype.DrawPolygon = function(vertices, vertexCount, color) { + if(!vertexCount) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.moveTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + for(var i = 1; i < vertexCount; i++) { + s.lineTo(vertices[i].x * drawScale, vertices[i].y * drawScale); + } + s.lineTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + s.closePath(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawSolidPolygon = function(vertices, vertexCount, color) { + if(!vertexCount) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.fillStyle = this._color(color.color, this.m_fillAlpha); + s.moveTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + for(var i = 1; i < vertexCount; i++) { + s.lineTo(vertices[i].x * drawScale, vertices[i].y * drawScale); + } + s.lineTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + s.closePath(); + s.fill(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawCircle = function(center, radius, color) { + if(!radius) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.arc(center.x * drawScale, center.y * drawScale, radius * drawScale, 0, Math.PI * 2, true); + s.closePath(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawSolidCircle = function(center, radius, axis, color) { + if(!radius) return; + var s = this.m_ctx, + drawScale = this.m_drawScale, + cx = center.x * drawScale, + cy = center.y * drawScale; + s.moveTo(0, 0); + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.fillStyle = this._color(color.color, this.m_fillAlpha); + s.arc(cx, cy, radius * drawScale, 0, Math.PI * 2, true); + s.moveTo(cx, cy); + s.lineTo((center.x + axis.x * radius) * drawScale, (center.y + axis.y * radius) * drawScale); + s.closePath(); + s.fill(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawSegment = function(p1, p2, color) { + var s = this.m_ctx, + drawScale = this.m_drawScale; + s.strokeStyle = this._color(color.color, this.m_alpha); + s.beginPath(); + s.moveTo(p1.x * drawScale, p1.y * drawScale); + s.lineTo(p2.x * drawScale, p2.y * drawScale); + s.closePath(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawTransform = function(xf) { + var s = this.m_ctx, + drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(0xff0000, this.m_alpha); + s.moveTo(xf.position.x * drawScale, xf.position.y * drawScale); + s.lineTo((xf.position.x + this.m_xformScale * xf.R.col1.x) * drawScale, (xf.position.y + this.m_xformScale * xf.R.col1.y) * drawScale); + + s.strokeStyle = this._color(0xff00, this.m_alpha); + s.moveTo(xf.position.x * drawScale, xf.position.y * drawScale); + s.lineTo((xf.position.x + this.m_xformScale * xf.R.col2.x) * drawScale, (xf.position.y + this.m_xformScale * xf.R.col2.y) * drawScale); + s.closePath(); + s.stroke(); + }; +})(); //post-definitions +var i; +for(i = 0; i < Box2D.postDefs.length; ++i) Box2D.postDefs[i](); +delete Box2D.postDefs; diff --git a/websiteUsingBulma/ProjectSketches/Creature Creator/libraries/p5.dom.js b/websiteUsingBulma/ProjectSketches/Creature Creator/libraries/p5.dom.js new file mode 100644 index 0000000..b88ee8d --- /dev/null +++ b/websiteUsingBulma/ProjectSketches/Creature Creator/libraries/p5.dom.js @@ -0,0 +1,2190 @@ +/*! p5.dom.js v0.3.2 March 25, 2017 */ +/** + *

The web is much more than just canvas and p5.dom makes it easy to interact + * with other HTML5 objects, including text, hyperlink, image, input, video, + * audio, and webcam.

+ *

There is a set of creation methods, DOM manipulation methods, and + * an extended p5.Element that supports a range of HTML elements. See the + * + * beyond the canvas tutorial for a full overview of how this addon works. + * + *

Methods and properties shown in black are part of the p5.js core, items in + * blue are part of the p5.dom library. You will need to include an extra file + * in order to access the blue functions. See the + * using a library + * section for information on how to include this library. p5.dom comes with + * p5 complete or you can download the single file + * + * here.

+ *

See tutorial: beyond the canvas + * for more info on how to use this libary. + * + * @module p5.dom + * @submodule p5.dom + * @for p5.dom + * @main + */ + +(function (root, factory) { + if (typeof define === 'function' && define.amd) + define('p5.dom', ['libraries/p5'], function (p5) { (factory(p5));}); + else if (typeof exports === 'object') + factory(require('../p5')); + else + factory(root['p5']); +}(this, function (p5) { + +// ============================================================================= +// p5 additions +// ============================================================================= + + /** + * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' + * prefixes to specify an ID or class respectively, and none for a tag) and returns it as + * a p5.Element. If a class or tag name is given with more than 1 element, + * only the first element will be returned. + * The DOM node itself can be accessed with .elt. + * Returns null if none found. You can also specify a container to search within. + * + * @method select + * @param {String} name id, class, or tag name of element to search for + * @param {String} [container] id, p5.Element, or HTML element to search within + * @return {Object|p5.Element|Null} p5.Element containing node found + * @example + *

+ * function setup() { + * createCanvas(100,100); + * //translates canvas 50px down + * select('canvas').position(100, 100); + * } + *
+ *
+ * // these are all valid calls to select() + * var a = select('#moo'); + * var b = select('#blah', '#myContainer'); + * var c = select('#foo', b); + * var d = document.getElementById('beep'); + * var e = select('p', d); + *
+ * + */ + p5.prototype.select = function (e, p) { + var res = null; + var container = getContainer(p); + if (e[0] === '.'){ + e = e.slice(1); + res = container.getElementsByClassName(e); + if (res.length) { + res = res[0]; + } else { + res = null; + } + }else if (e[0] === '#'){ + e = e.slice(1); + res = container.getElementById(e); + }else { + res = container.getElementsByTagName(e); + if (res.length) { + res = res[0]; + } else { + res = null; + } + } + if (res) { + return wrapElement(res); + } else { + return null; + } + }; + + /** + * Searches the page for elements with the given class or tag name (using the '.' prefix + * to specify a class and no prefix for a tag) and returns them as p5.Elements + * in an array. + * The DOM node itself can be accessed with .elt. + * Returns an empty array if none found. + * You can also specify a container to search within. + * + * @method selectAll + * @param {String} name class or tag name of elements to search for + * @param {String} [container] id, p5.Element, or HTML element to search within + * @return {Array} Array of p5.Elements containing nodes found + * @example + *
+ * function setup() { + * createButton('btn'); + * createButton('2nd btn'); + * createButton('3rd btn'); + * var buttons = selectAll('button'); + * + * for (var i = 0; i < buttons.length; i++){ + * buttons[i].size(100,100); + * } + * } + *
+ *
+ * // these are all valid calls to selectAll() + * var a = selectAll('.moo'); + * var b = selectAll('div'); + * var c = selectAll('button', '#myContainer'); + * var d = select('#container'); + * var e = selectAll('p', d); + * var f = document.getElementById('beep'); + * var g = select('.blah', f); + *
+ * + */ + p5.prototype.selectAll = function (e, p) { + var arr = []; + var res; + var container = getContainer(p); + if (e[0] === '.'){ + e = e.slice(1); + res = container.getElementsByClassName(e); + } else { + res = container.getElementsByTagName(e); + } + if (res) { + for (var j = 0; j < res.length; j++) { + var obj = wrapElement(res[j]); + arr.push(obj); + } + } + return arr; + }; + + /** + * Helper function for select and selectAll + */ + function getContainer(p) { + var container = document; + if (typeof p === 'string' && p[0] === '#'){ + p = p.slice(1); + container = document.getElementById(p) || document; + } else if (p instanceof p5.Element){ + container = p.elt; + } else if (p instanceof HTMLElement){ + container = p; + } + return container; + } + + /** + * Helper function for getElement and getElements. + */ + function wrapElement(elt) { + if(elt.tagName === "INPUT" && elt.type === "checkbox") { + var converted = new p5.Element(elt); + converted.checked = function(){ + if (arguments.length === 0){ + return this.elt.checked; + } else if(arguments[0]) { + this.elt.checked = true; + } else { + this.elt.checked = false; + } + return this; + }; + return converted; + } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { + return new p5.MediaElement(elt); + } else { + return new p5.Element(elt); + } + } + + /** + * Removes all elements created by p5, except any canvas / graphics + * elements created by createCanvas or createGraphics. + * Event handlers are removed, and element is removed from the DOM. + * @method removeElements + * @example + *
+ * function setup() { + * createCanvas(100, 100); + * createDiv('this is some text'); + * createP('this is a paragraph'); + * } + * function mousePressed() { + * removeElements(); // this will remove the div and p, not canvas + * } + *
+ * + */ + p5.prototype.removeElements = function (e) { + for (var i=0; i + * var myDiv; + * function setup() { + * myDiv = createDiv('this is some text'); + * } + * + */ + + /** + * Creates a <p></p> element in the DOM with given inner HTML. Used + * for paragraph length text. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createP + * @param {String} html inner HTML for element created + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var myP; + * function setup() { + * myP = createP('this is some text'); + * } + *
+ */ + + /** + * Creates a <span></span> element in the DOM with given inner HTML. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createSpan + * @param {String} html inner HTML for element created + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var mySpan; + * function setup() { + * mySpan = createSpan('this is some text'); + * } + *
+ */ + var tags = ['div', 'p', 'span']; + tags.forEach(function(tag) { + var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); + p5.prototype[method] = function(html) { + var elt = document.createElement(tag); + elt.innerHTML = typeof html === undefined ? "" : html; + return addElement(elt, this); + } + }); + + /** + * Creates an <img> element in the DOM with given src and + * alternate text. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createImg + * @param {String} src src path or url for image + * @param {String} [alt] alternate text to be used if image does not load + * @param {Function} [successCallback] callback to be called once image data is loaded + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var img; + * function setup() { + * img = createImg('http://p5js.org/img/asterisk-01.png'); + * } + *
+ */ + p5.prototype.createImg = function() { + var elt = document.createElement('img'); + var args = arguments; + var self; + var setAttrs = function(){ + self.width = elt.offsetWidth || elt.width; + self.height = elt.offsetHeight || elt.height; + if (args.length > 1 && typeof args[1] === 'function'){ + self.fn = args[1]; + self.fn(); + }else if (args.length > 1 && typeof args[2] === 'function'){ + self.fn = args[2]; + self.fn(); + } + }; + elt.src = args[0]; + if (args.length > 1 && typeof args[1] === 'string'){ + elt.alt = args[1]; + } + elt.onload = function(){ + setAttrs(); + } + self = addElement(elt, this); + return self; + }; + + /** + * Creates an <a></a> element in the DOM for including a hyperlink. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createA + * @param {String} href url of page to link to + * @param {String} html inner html of link element to display + * @param {String} [target] target where new link should open, + * could be _blank, _self, _parent, _top. + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var myLink; + * function setup() { + * myLink = createA('http://p5js.org/', 'this is a link'); + * } + *
+ */ + p5.prototype.createA = function(href, html, target) { + var elt = document.createElement('a'); + elt.href = href; + elt.innerHTML = html; + if (target) elt.target = target; + return addElement(elt, this); + }; + + /** INPUT **/ + + + /** + * Creates a slider <input></input> element in the DOM. + * Use .size() to set the display length of the slider. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createSlider + * @param {Number} min minimum value of the slider + * @param {Number} max maximum value of the slider + * @param {Number} [value] default value of the slider + * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value) + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var slider; + * function setup() { + * slider = createSlider(0, 255, 100); + * slider.position(10, 10); + * slider.style('width', '80px'); + * } + * + * function draw() { + * var val = slider.value(); + * background(val); + * } + *
+ * + *
+ * var slider; + * function setup() { + * colorMode(HSB); + * slider = createSlider(0, 360, 60, 40); + * slider.position(10, 10); + * slider.style('width', '80px'); + * } + * + * function draw() { + * var val = slider.value(); + * background(val, 100, 100, 1); + * } + *
+ */ + p5.prototype.createSlider = function(min, max, value, step) { + var elt = document.createElement('input'); + elt.type = 'range'; + elt.min = min; + elt.max = max; + if (step === 0) { + elt.step = .000000000000000001; // smallest valid step + } else if (step) { + elt.step = step; + } + if (typeof(value) === "number") elt.value = value; + return addElement(elt, this); + }; + + /** + * Creates a <button></button> element in the DOM. + * Use .size() to set the display size of the button. + * Use .mousePressed() to specify behavior on press. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createButton + * @param {String} label label displayed on the button + * @param {String} [value] value of the button + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var button; + * function setup() { + * createCanvas(100, 100); + * background(0); + * button = createButton('click me'); + * button.position(19, 19); + * button.mousePressed(changeBG); + * } + * + * function changeBG() { + * var val = random(255); + * background(val); + * } + *
+ */ + p5.prototype.createButton = function(label, value) { + var elt = document.createElement('button'); + elt.innerHTML = label; + elt.value = value; + if (value) elt.value = value; + return addElement(elt, this); + }; + + /** + * Creates a checkbox <input></input> element in the DOM. + * Calling .checked() on a checkbox returns if it is checked or not + * + * @method createCheckbox + * @param {String} [label] label displayed after checkbox + * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var checkbox; + * + * function setup() { + * checkbox = createCheckbox('label', false); + * checkbox.changed(myCheckedEvent); + * } + * + * function myCheckedEvent() { + * if (this.checked()) { + * console.log("Checking!"); + * } else { + * console.log("Unchecking!"); + * } + * } + *
+ */ + p5.prototype.createCheckbox = function() { + var elt = document.createElement('div'); + var checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + elt.appendChild(checkbox); + //checkbox must be wrapped in p5.Element before label so that label appears after + var self = addElement(elt, this); + self.checked = function(){ + var cb = self.elt.getElementsByTagName('input')[0]; + if (cb) { + if (arguments.length === 0){ + return cb.checked; + }else if(arguments[0]){ + cb.checked = true; + }else{ + cb.checked = false; + } + } + return self; + }; + this.value = function(val){ + self.value = val; + return this; + }; + if (arguments[0]){ + var ran = Math.random().toString(36).slice(2); + var label = document.createElement('label'); + checkbox.setAttribute('id', ran); + label.htmlFor = ran; + self.value(arguments[0]); + label.appendChild(document.createTextNode(arguments[0])); + elt.appendChild(label); + } + if (arguments[1]){ + checkbox.checked = true; + } + return self; + }; + + /** + * Creates a dropdown menu <select></select> element in the DOM. + * @method createSelect + * @param {boolean} [multiple] true if dropdown should support multiple selections + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var sel; + * + * function setup() { + * textAlign(CENTER); + * background(200); + * sel = createSelect(); + * sel.position(10, 10); + * sel.option('pear'); + * sel.option('kiwi'); + * sel.option('grape'); + * sel.changed(mySelectEvent); + * } + * + * function mySelectEvent() { + * var item = sel.value(); + * background(200); + * text("it's a "+item+"!", 50, 50); + * } + *
+ */ + p5.prototype.createSelect = function(mult) { + var elt = document.createElement('select'); + if (mult){ + elt.setAttribute('multiple', 'true'); + } + var self = addElement(elt, this); + self.option = function(name, value){ + var opt = document.createElement('option'); + opt.innerHTML = name; + if (arguments.length > 1) + opt.value = value; + else + opt.value = name; + elt.appendChild(opt); + }; + self.selected = function(value){ + var arr = []; + if (arguments.length > 0){ + for (var i = 0; i < this.elt.length; i++){ + if (value.toString() === this.elt[i].value){ + this.elt.selectedIndex = i; + } + } + return this; + }else{ + if (mult){ + for (var i = 0; i < this.elt.selectedOptions.length; i++){ + arr.push(this.elt.selectedOptions[i].value); + } + return arr; + }else{ + return this.elt.value; + } + } + }; + return self; + }; + + /** + * Creates a radio button <input></input> element in the DOM. + * The .option() method can be used to set options for the radio after it is + * created. The .value() method will return the currently selected option. + * + * @method createRadio + * @param {String} [divId] the id and name of the created div and input field respectively + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * var radio; + * + * function setup() { + * radio = createRadio(); + * radio.option("black"); + * radio.option("white"); + * radio.option("gray"); + * radio.style('width', '60px'); + * textAlign(CENTER); + * fill(255, 0, 0); + * } + * + * function draw() { + * var val = radio.value(); + * background(val); + * text(val, width/2, height/2); + * } + *
+ *
+ * var radio; + * + * function setup() { + * radio = createRadio(); + * radio.option('apple', 1); + * radio.option('bread', 2); + * radio.option('juice', 3); + * radio.style('width', '60px'); + * textAlign(CENTER); + * } + * + * function draw() { + * background(200); + * var val = radio.value(); + * if (val) { + * text('item cost is $'+val, width/2, height/2); + * } + * } + *
+ */ + p5.prototype.createRadio = function() { + var radios = document.querySelectorAll("input[type=radio]"); + var count = 0; + if(radios.length > 1){ + var length = radios.length; + var prev=radios[0].name; + var current = radios[1].name; + count = 1; + for(var i = 1; i < length; i++) { + current = radios[i].name; + if(prev != current){ + count++; + } + prev = current; + } + } + else if (radios.length == 1){ + count = 1; + } + var elt = document.createElement('div'); + var self = addElement(elt, this); + var times = -1; + self.option = function(name, value){ + var opt = document.createElement('input'); + opt.type = 'radio'; + opt.innerHTML = name; + if (arguments.length > 1) + opt.value = value; + else + opt.value = name; + opt.setAttribute('name',"defaultradio"+count); + elt.appendChild(opt); + if (name){ + times++; + var ran = Math.random().toString(36).slice(2); + var label = document.createElement('label'); + opt.setAttribute('id', "defaultradio"+count+"-"+times); + label.htmlFor = "defaultradio"+count+"-"+times; + label.appendChild(document.createTextNode(name)); + elt.appendChild(label); + } + return opt; + }; + self.selected = function(){ + var length = this.elt.childNodes.length; + if(arguments.length == 1) { + for (var i = 0; i < length; i+=2){ + if(this.elt.childNodes[i].value == arguments[0]) + this.elt.childNodes[i].checked = true; + } + return this; + } else { + for (var i = 0; i < length; i+=2){ + if(this.elt.childNodes[i].checked == true) + return this.elt.childNodes[i].value; + } + } + }; + self.value = function(){ + var length = this.elt.childNodes.length; + if(arguments.length == 1) { + for (var i = 0; i < length; i+=2){ + if(this.elt.childNodes[i].value == arguments[0]) + this.elt.childNodes[i].checked = true; + } + return this; + } else { + for (var i = 0; i < length; i+=2){ + if(this.elt.childNodes[i].checked == true) + return this.elt.childNodes[i].value; + } + return ""; + } + }; + return self + }; + + /** + * Creates an <input></input> element in the DOM for text input. + * Use .size() to set the display length of the box. + * Appends to the container node if one is specified, otherwise + * appends to body. + * + * @method createInput + * @param {Number} [value] default value of the input box + * @param {String} [type] type of text, ie text, password etc. Defaults to text + * @return {Object|p5.Element} pointer to p5.Element holding created node + * @example + *
+ * function setup(){ + * var inp = createInput(''); + * inp.input(myInputEvent); + * } + * + * function myInputEvent(){ + * console.log('you are typing: ', this.value()); + * } + * + *
+ */ + p5.prototype.createInput = function(value, type) { + var elt = document.createElement('input'); + elt.type = type ? type : 'text'; + if (value) elt.value = value; + return addElement(elt, this); + }; + + /** + * Creates an <input></input> element in the DOM of type 'file'. + * This allows users to select local files for use in a sketch. + * + * @method createFileInput + * @param {Function} [callback] callback function for when a file loaded + * @param {String} [multiple] optional to allow multiple files selected + * @return {Object|p5.Element} pointer to p5.Element holding created DOM element + * @example + * var input; + * var img; + * + * function setup() { + * input = createFileInput(handleFile); + * input.position(0, 0); + * } + * + * function draw() { + * if (img) { + * image(img, 0, 0, width, height); + * } + * } + * + * function handleFile(file) { + * print(file); + * if (file.type === 'image') { + * img = createImg(file.data); + * img.hide(); + * } + * } + */ + p5.prototype.createFileInput = function(callback, multiple) { + + // Is the file stuff supported? + if (window.File && window.FileReader && window.FileList && window.Blob) { + // Yup, we're ok and make an input file selector + var elt = document.createElement('input'); + elt.type = 'file'; + + // If we get a second argument that evaluates to true + // then we are looking for multiple files + if (multiple) { + // Anything gets the job done + elt.multiple = 'multiple'; + } + + // Function to handle when a file is selected + // We're simplifying life and assuming that we always + // want to load every selected file + function handleFileSelect(evt) { + // These are the files + var files = evt.target.files; + // Load each one and trigger a callback + for (var i = 0; i < files.length; i++) { + var f = files[i]; + var reader = new FileReader(); + function makeLoader(theFile) { + // Making a p5.File object + var p5file = new p5.File(theFile); + return function(e) { + p5file.data = e.target.result; + callback(p5file); + }; + }; + reader.onload = makeLoader(f); + + // Text or data? + // This should likely be improved + if (f.type.indexOf('text') > -1) { + reader.readAsText(f); + } else { + reader.readAsDataURL(f); + } + } + } + + // Now let's handle when a file was selected + elt.addEventListener('change', handleFileSelect, false); + return addElement(elt, this); + } else { + console.log('The File APIs are not fully supported in this browser. Cannot create element.'); + } + }; + + + /** VIDEO STUFF **/ + + function createMedia(pInst, type, src, callback) { + var elt = document.createElement(type); + + // allow src to be empty + var src = src || ''; + if (typeof src === 'string') { + src = [src]; + } + for (var i=0; i