From ecaded076f35b88c1cefc32171d1876d44b3261a Mon Sep 17 00:00:00 2001 From: sanderd17 Date: Fri, 24 Jan 2014 19:51:00 +0000 Subject: [PATCH] Improve the Vector prototype with static functions. Replace the usage of the Math vector functions with functions from the Vector class. This was SVN commit r14659. --- .../data/mods/public/globalscripts/Math.js | 39 ----- .../data/mods/public/globalscripts/vector.js | 142 +++++++++++++++--- .../public/simulation/components/Attack.js | 45 +++--- .../public/simulation/helpers/Commands.js | 6 +- .../mods/public/simulation/helpers/Damage.js | 18 +-- .../mods/public/simulation/helpers/Entity.js | 10 +- 6 files changed, 154 insertions(+), 106 deletions(-) diff --git a/binaries/data/mods/public/globalscripts/Math.js b/binaries/data/mods/public/globalscripts/Math.js index 5f2cb40831..01054972ef 100644 --- a/binaries/data/mods/public/globalscripts/Math.js +++ b/binaries/data/mods/public/globalscripts/Math.js @@ -315,42 +315,3 @@ Math.intPow = function(x, y) }; -/** - * Gets the 2D interpreted version of the cross product of two vectors. - */ -Math.VectorCross = function(p1, p2) -{ - return p1.x * p2.z - p1.z * p2.x; -}; - -/** - * Gets the straight line distance between p1 and p2. - */ -Math.VectorDistance = function(p1, p2) -{ - return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.z - p2.z) * (p1.z - p2.z)); -}; - -/** - * Gets the straight line distance between p1 and p2 squared. - */ -Math.VectorDistanceSquared = function(p1, p2) -{ - return (p1.x - p2.x) * (p1.x - p2.x) + (p1.z - p2.z) * (p1.z - p2.z); -}; - -/** - * Gets the dot product of two vectors. - */ -Math.VectorDot = function(p1, p2) -{ - return p1.x * p2.x + p1.z * p2.z; -}; - -/** - * Get the total length of a given vector p. - */ -Math.VectorLength = function(p) -{ - return Math.sqrt(p.x * p.x + p.z * p.z); -}; diff --git a/binaries/data/mods/public/globalscripts/vector.js b/binaries/data/mods/public/globalscripts/vector.js index 170022aae3..88d613ad4d 100644 --- a/binaries/data/mods/public/globalscripts/vector.js +++ b/binaries/data/mods/public/globalscripts/vector.js @@ -16,10 +16,10 @@ function Vector2D(x, y) this.set(0, 0); } -Vector2D.prototype.clone = function() -{ - return new Vector2D(this.x, this.y); -}; +// Mutating 2D functions +// +// These functions modify the current object, +// and always return this object to allow chaining Vector2D.prototype.set = function(x, y) { @@ -56,6 +56,33 @@ Vector2D.prototype.div = function(f) return this; }; +Vector2D.prototype.normalize = function() +{ + var mag = this.length(); + if (!mag) + return this; + + return this.div(mag); +}; + +/** + * Rotate a radians anti-clockwise + */ +Vector2D.prototype.rotate = function(a) +{ + var sin = Math.sin(a); + var cos = Math.cos(a); + var x = this.x * cos + this.y * sin; + var y = this.y * cos - this.x * sin; + this.x = x; + this.y = y; + return this; +} + +// Numeric 2D info functions (non-mutating) +// +// These methods serve to get numeric info on the vector, they don't modify the vector + Vector2D.prototype.dot = function(v) { return this.x * v.x + this.y * v.y; @@ -104,13 +131,39 @@ Vector2D.prototype.distanceTo = function(v) return Math.sqrt(this.distanceToSquared(v)); }; -Vector2D.prototype.normalize = function() -{ - var mag = this.length(); - if (!mag) - return this; +// Static 2D functions +// +// Static functions that return a new vector object. +// Note that object creation is slow in JS, so use them only when necessary - return this.div(mag); +Vector2D.clone = function(v) +{ + return new Vector2D(v.x, v.y); +}; + +Vector2D.from3D = function(v) +{ + return new Vector2D(v.x, v.z); +}; + +Vector2D.add = function(v1, v2) +{ + return new Vector2D(v1.x + v2.x, v1.y + v2.y); +}; + +Vector2D.sub = function(v1, v2) +{ + return new Vector2D(v1.x - v2.x, v1.y - v2.y); +}; + +Vector2D.mult = function(v, f) +{ + return new Vector2D(v.x * f, v.y * f); +}; + +Vector2D.div = function(v, f) +{ + return new Vector2D(v.x / f, v.y / f); }; ///////////////////////////////////////////////////////////////////// @@ -123,15 +176,15 @@ Vector2D.prototype.normalize = function() function Vector3D(x, y, z) { if (arguments.length == 3) - this.set(x, y, y); + this.set(x, y, z); else this.set(0, 0, 0); } -Vector3D.prototype.clone = function() -{ - return new Vector3D(this.x, this.y, this.z); -}; +// Mutating 3D functions +// +// These functions modify the current object, +// and always return this object to allow chaining Vector3D.prototype.set = function(x, y, z) { @@ -173,6 +226,19 @@ Vector3D.prototype.div = function(f) return this; }; +Vector3D.prototype.normalize = function() +{ + var mag = this.length(); + if (!mag) + return this; + + return this.div(mag); +}; + +// Numeric 3D info functions (non-mutating) +// +// These methods serve to get numeric info on the vector, they don't modify the vector + Vector3D.prototype.dot = function(v) { return this.x * v.x + this.y * v.y + this.z * v.z; @@ -216,15 +282,49 @@ Vector3D.prototype.distanceTo = function(v) return Math.sqrt(this.distanceToSquared(v)); }; -Vector3D.prototype.normalize = function() +Vector3D.prototype.horizDistanceToSquared = function(v) { - var mag = this.length(); - if (!mag) - return this; - - return this.div(mag); + var dx = this.x - v.x; + var dz = this.z - v.z; + return dx * dx + dz * dz; }; +Vector3D.prototype.horizDistanceTo = function(v) +{ + return Math.sqrt(this.horizDistanceToSquared(v)); +}; + +// Static 3D functions +// +// Static functions that return a new vector object. +// Note that object creation is slow in JS, so use them only when really necessary + +Vector3D.clone = function(v) +{ + return new Vector3D(v.x, v.y, v.z); +}; + +Vector3D.add = function(v1, v2) +{ + return new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); +}; + +Vector3D.sub = function(v1, v2) +{ + return new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); +}; + +Vector3D.mult = function(v, f) +{ + return new Vector3D(v.x * f, v.y * f, v.z * f); +}; + +Vector3D.div = function(v, f) +{ + return new Vector3D(v.x / f, v.y / f, v.z / f); +}; + + // make the prototypes easily accessible to C++ const Vector2Dprototype = Vector2D.prototype; const Vector3Dprototype = Vector3D.prototype; diff --git a/binaries/data/mods/public/simulation/components/Attack.js b/binaries/data/mods/public/simulation/components/Attack.js index 20923926b8..e26fc91e86 100644 --- a/binaries/data/mods/public/simulation/components/Attack.js +++ b/binaries/data/mods/public/simulation/components/Attack.js @@ -457,14 +457,14 @@ Attack.prototype.PerformAttack = function(type, target) return; var targetPosition = cmpTargetPosition.GetPosition(); - var relativePosition = {"x": targetPosition.x - selfPosition.x, "z": targetPosition.z - selfPosition.z} + var relativePosition = Vector3D.sub(targetPosition, selfPosition); var previousTargetPosition = Engine.QueryInterface(target, IID_Position).GetPreviousPosition(); - var targetVelocity = {"x": (targetPosition.x - previousTargetPosition.x) / this.turnLength, "z": (targetPosition.z - previousTargetPosition.z) / this.turnLength} + var targetVelocity = Vector3D.sub(targetPosition, previousTargetPosition).div(this.turnLength); // the component of the targets velocity radially away from the archer - var radialSpeed = Math.VectorDot(relativePosition, targetVelocity) / Math.VectorLength(relativePosition); + var radialSpeed = relativePosition.dot(targetVelocity) / relativePosition.length(); - var horizDistance = Math.VectorDistance(targetPosition, selfPosition); + var horizDistance = targetPosition.horizDistanceTo(selfPosition); // This is an approximation of the time ot the target, it assumes that the target has a constant radial // velocity, but since units move in straight lines this is not true. The exact value would be more @@ -473,8 +473,7 @@ Attack.prototype.PerformAttack = function(type, target) var timeToTarget = horizDistance / (horizSpeed - radialSpeed); // Predict where the unit is when the missile lands. - var predictedPosition = {"x": targetPosition.x + targetVelocity.x * timeToTarget, - "z": targetPosition.z + targetVelocity.z * timeToTarget}; + var predictedPosition = Vector3D.mult(targetVelocity, timeToTarget).add(targetPosition); // Compute the real target point (based on spread and target speed) var range = this.GetRange(type); @@ -483,19 +482,19 @@ Attack.prototype.PerformAttack = function(type, target) var distanceModifiedSpread = spread * horizDistance/elevationAdaptedMaxRange; var randNorm = this.GetNormalDistribution(); - var offsetX = randNorm[0] * distanceModifiedSpread * (1 + Math.VectorLength(targetVelocity) / 20); - var offsetZ = randNorm[1] * distanceModifiedSpread * (1 + Math.VectorLength(targetVelocity) / 20); + var offsetX = randNorm[0] * distanceModifiedSpread * (1 + targetVelocity.length() / 20); + var offsetZ = randNorm[1] * distanceModifiedSpread * (1 + targetVelocity.length() / 20); - var realTargetPosition = { "x": predictedPosition.x + offsetX, "y": targetPosition.y, "z": predictedPosition.z + offsetZ }; + var realTargetPosition = new Vector3D(predictedPosition.x + offsetX, targetPosition.y, predictedPosition.z + offsetZ); // Calculate when the missile will hit the target position - var realHorizDistance = Math.VectorDistance(realTargetPosition, selfPosition); + var realHorizDistance = realTargetPosition.horizDistanceTo(selfPosition); var timeToTarget = realHorizDistance / horizSpeed; - var missileDirection = {"x": (realTargetPosition.x - selfPosition.x) / realHorizDistance, "z": (realTargetPosition.z - selfPosition.z) / realHorizDistance}; + var missileDirection = Vector3D.sub(realTargetPosition, selfPosition).div(realHorizDistance); // Make the arrow appear to land slightly behind the target so that arrows landing next to a guys foot don't count but arrows that go through the torso do - var graphicalPosition = {"x": realTargetPosition.x + 2*missileDirection.x, "y": realTargetPosition.y + 2*missileDirection.y}; + var graphicalPosition = Vector3D.mult(missileDirection, 2).add(realTargetPosition); // Launch the graphical projectile var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); var id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity); @@ -520,8 +519,9 @@ Attack.prototype.InterpolatedLocation = function(ent, lateness) var curPos = cmpTargetPosition.GetPosition(); var prevPos = cmpTargetPosition.GetPreviousPosition(); lateness /= 1000; - return {"x": (curPos.x * (this.turnLength - lateness) + prevPos.x * lateness) / this.turnLength, - "z": (curPos.z * (this.turnLength - lateness) + prevPos.z * lateness) / this.turnLength}; + return new Vector3D((curPos.x * (this.turnLength - lateness) + prevPos.x * lateness) / this.turnLength, + 0, + (curPos.z * (this.turnLength - lateness) + prevPos.z * lateness) / this.turnLength); }; // Tests whether it point is inside of ent's footprint @@ -541,19 +541,16 @@ Attack.prototype.testCollision = function(ent, point, lateness) if (targetShape.type === 'circle') { // Use VectorDistanceSquared and square targetShape.radius to avoid square roots. - return (Math.VectorDistanceSquared(point, targetPosition) < (targetShape.radius * targetShape.radius)); + return (point.horizDistanceTo(targetPosition) < (targetShape.radius * targetShape.radius)); } else { - var targetRotation = Engine.QueryInterface(ent, IID_Position).GetRotation().y; + var angle = Engine.QueryInterface(ent, IID_Position).GetRotation().y; - var dx = point.x - targetPosition.x; - var dz = point.z - targetPosition.z; + var d = Vector3D.sub(point, targetPosition); + d = Vector2D.from3D(d).rotate(-angle); - var dxr = Math.cos(targetRotation) * dx - Math.sin(targetRotation) * dz; - var dzr = Math.sin(targetRotation) * dx + Math.cos(targetRotation) * dz; - - return (-targetShape.width/2 <= dxr && dxr < targetShape.width/2 && -targetShape.depth/2 <= dzr && dzr < targetShape.depth/2); + return d.x < Math.abs(targetShape.width/2) && d.y < Math.abs(targetShape.depth/2); } }; @@ -576,7 +573,7 @@ Attack.prototype.MissileHit = function(data, lateness) playersToDamage = cmpPlayer.GetEnemies(); } // Damage the units. - Damage.CauseSplashDamage({"attacker":this.entity, "origin":data.position, "radius":splashRadius, "shape":splashShape, "strengths":this.GetAttackStrengths(data.type), "direction":data.direction, "playersToDamage":playersToDamage, "type":data.type}); + Damage.CauseSplashDamage({"attacker":this.entity, "origin":Vector2D.from3D(data.position), "radius":splashRadius, "shape":splashShape, "strengths":this.GetAttackStrengths(data.type), "direction":data.direction, "playersToDamage":playersToDamage, "type":data.type}); } if (this.testCollision(data.target, data.position, lateness)) @@ -595,7 +592,7 @@ Attack.prototype.MissileHit = function(data, lateness) { // If we didn't hit the main target look for nearby units var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player) - var ents = Damage.EntitiesNearPoint(data.position, Math.VectorDistance(data.position, targetPosition) * 2, cmpPlayer.GetEnemies()); + var ents = Damage.EntitiesNearPoint(data.position, data.position.horizDistanceTo(targetPosition) * 2, cmpPlayer.GetEnemies()); for (var i = 0; i < ents.length; i++) { diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js index 1204dc8865..c8d79f869f 100644 --- a/binaries/data/mods/public/simulation/helpers/Commands.js +++ b/binaries/data/mods/public/simulation/helpers/Commands.js @@ -1311,11 +1311,7 @@ function ClusterEntities(ents, separationDistance) } positions.push(cmpPosition.GetPosition2D()); for (var j = 0; j < i; j++) - { - var dx = positions[i].x - positions[j].x; - var dy = positions[i].y - positions[j].y; - matrix[i][j] = dx * dx + dy * dy; - } + matrix[i][j] = positions[i].distanceToSquared(positions[j]); } while (clusters.length > 1) { diff --git a/binaries/data/mods/public/simulation/helpers/Damage.js b/binaries/data/mods/public/simulation/helpers/Damage.js index b261c1ce81..7d3a387e42 100644 --- a/binaries/data/mods/public/simulation/helpers/Damage.js +++ b/binaries/data/mods/public/simulation/helpers/Damage.js @@ -4,7 +4,7 @@ var Damage = {}; /** * Damages units around a given origin. * data.attacker = - * data.origin = {'x':, 'z':} + * data.origin = * data.radius = * data.shape = * data.strengths = {'hack':, 'pierce':, 'crush':} @@ -21,23 +21,23 @@ Damage.CauseSplashDamage = function(data) // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin. for each (var entity in nearEnts) { - var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition(); + var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition2D(); if(data.shape == 'Circular') // circular effect with quadratic falloff in every direction { - var squaredDistanceFromOrigin = Math.VectorDistanceSquared(data.origin, entityPosition); + var squaredDistanceFromOrigin = data.origin.distanceToSquared(entityPosition); damageMultiplier == 1 - squaredDistanceFromOrigin / (data.radius * data.radius); } else if(data.shape == 'Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles) { // Get position of entity relative to splash origin. - var relativePos = {"x":entityPosition.x - data.origin.x, "z":entityPosition.z - data.origin.z}; + var relativePos = entityPosition.sub(data.origin); // The width of linear splash is one fifth of the normal splash radius. var width = data.radius/5; // Effectivly rotate the axis to align with the missile direction. - var parallelDist = Math.VectorDot(relativePos, data.direction); // z axis - var perpDist = Math.abs(Math.VectorCross(relativePos, data.direction)); // y axis + var parallelDist = relativePos.dot(data.direction); // z axis + var perpDist = Math.abs(relativePos.cross(data.direction)); // y axis // Check that the unit is within the distance at which it will get damaged. if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions @@ -87,15 +87,13 @@ Damage.CauseDamage = function(data) /** * Gets entities near a give point for given players. - * origin = {'x':, 'z':} + * origin = * radius = * players = * If players is not included, entities from all players are used. */ Damage.EntitiesNearPoint = function(origin, radius, players) { - // Path to dummy template. - var dummyPath = "special/dummy"; // If there is insufficient data return an empty array. if (!origin || !radius) return []; @@ -111,7 +109,7 @@ Damage.EntitiesNearPoint = function(origin, radius, players) // Call RangeManager with dummy entity and return the result. var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); - var rangeQuery = rangeManager.ExecuteQueryAroundPos({"x": origin.x, "y": origin.z}, 0, radius, players, IID_DamageReceiver); + var rangeQuery = rangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, IID_DamageReceiver); return rangeQuery; }; diff --git a/binaries/data/mods/public/simulation/helpers/Entity.js b/binaries/data/mods/public/simulation/helpers/Entity.js index 9b89e1548f..0d74248941 100755 --- a/binaries/data/mods/public/simulation/helpers/Entity.js +++ b/binaries/data/mods/public/simulation/helpers/Entity.js @@ -3,18 +3,14 @@ function DistanceBetweenEntities(first, second) var cmpFirstPosition = Engine.QueryInterface(first, IID_Position); if (!cmpFirstPosition || !cmpFirstPosition.IsInWorld()) return Infinity; - var firstPosition = cmpFirstPosition.GetPosition(); + var firstPosition = cmpFirstPosition.GetPosition2D(); var cmpSecondPosition = Engine.QueryInterface(second, IID_Position); if (!cmpSecondPosition || !cmpSecondPosition.IsInWorld()) return Infinity; - var secondPosition = cmpSecondPosition.GetPosition(); + var secondPosition = cmpSecondPosition.GetPosition2D(); - var dx = secondPosition.x - firstPosition.x; - var dz = secondPosition.z - firstPosition.z; - - var horizDistance = Math.sqrt(dx * dx + dz * dz); - return horizDistance; + return firstPosition.distanceTo(secondPosition); } /**