1
0
forked from 0ad/0ad

Move "IsInRange" family of functions to the Obstruction Manager and make the commutative.

These functions were placed in UnitMotion, which had nothing to do with
range checks and made them available only to moving entities for no
particular reason.

This patch also adds support for square-square range checks and
shape-shape distance checks.

Modified from a patch by bb on top of work from wraitii.

Differential Revision: https://code.wildfiregames.com/D981
This was SVN commit r22345.
This commit is contained in:
wraitii 2019-06-06 19:37:23 +00:00
parent ba736916fc
commit c219ee54b2
13 changed files with 475 additions and 219 deletions

View File

@ -1635,8 +1635,8 @@ UnitAI.prototype.UnitFsmSpec = {
"MoveStarted": function(msg) {
// Adapt the speed to the one of the target if needed
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
if (cmpUnitMotion.IsInTargetRange(this.isGuardOf, 0, 3*this.guardRange))
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
if (cmpObstructionManager.IsInTargetRange(this.entity, this.isGuardOf, 0, 3 * this.guardRange, true))
{
var cmpUnitAI = Engine.QueryInterface(this.isGuardOf, IID_UnitAI);
if (cmpUnitAI)
@ -4308,8 +4308,8 @@ UnitAI.prototype.MoveToGarrisonRange = function(target)
UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max)
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.IsInPointRange(x, z, min, max);
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
return cmpObstructionManager.IsInPointRange(this.entity, x, z, min, max, true);
};
UnitAI.prototype.CheckTargetRange = function(target, iid, type)
@ -4319,8 +4319,8 @@ UnitAI.prototype.CheckTargetRange = function(target, iid, type)
return false;
var range = cmpRanged.GetRange(type);
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max, true);
};
/**
@ -4368,14 +4368,14 @@ UnitAI.prototype.CheckTargetAttackRange = function(target, type)
if (maxRangeSq < 0)
return false;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.IsInTargetRange(target, range.min, Math.sqrt(maxRangeSq));
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, Math.sqrt(maxRangeSq), true);
};
UnitAI.prototype.CheckTargetRangeExplicit = function(target, min, max)
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.IsInTargetRange(target, min, max);
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
return cmpObstructionManager.IsInTargetRange(this.entity, target, min, max, true);
};
UnitAI.prototype.CheckGarrisonRange = function(target)
@ -4389,8 +4389,8 @@ UnitAI.prototype.CheckGarrisonRange = function(target)
if (cmpObstruction)
range.max += cmpObstruction.GetUnitRadius()*1.5; // multiply by something larger than sqrt(2)
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max, true);
};
/**

View File

@ -276,29 +276,6 @@ UnitMotionFlying.prototype.MoveToTargetRange = function(target, minRange, maxRan
return true;
};
UnitMotionFlying.prototype.IsInPointRange = function(x, y, minRange, maxRange)
{
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
var pos = cmpPosition.GetPosition2D();
var distFromTarget = Math.euclidDistance2D(x, y, pos.x, pos.y);
if (minRange <= distFromTarget && distFromTarget <= maxRange)
return true;
return false;
};
UnitMotionFlying.prototype.IsInTargetRange = function(target, minRange, maxRange)
{
var cmpTargetPosition = Engine.QueryInterface(target, IID_Position);
if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld())
return false;
var targetPos = cmpTargetPosition.GetPosition2D();
return this.IsInPointRange(targetPos.x, targetPos.y, minRange, maxRange);
};
UnitMotionFlying.prototype.GetWalkSpeed = function()
{
return +this.template.MaxSpeed;

View File

@ -64,6 +64,9 @@ function TestFormationExiting(mode)
GetEnemies: function() { return []; },
});
AddMock(SYSTEM_ENTITY, IID_ObstructionManager, {
"IsInTargetRange": (ent, target, min, max, opposite) => true
});
var unitAI = ConstructComponent(unit, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
@ -84,12 +87,11 @@ function TestFormationExiting(mode)
});
AddMock(unit, IID_UnitMotion, {
GetWalkSpeed: function() { return 1; },
MoveToFormationOffset: function(target, x, z) { },
IsInTargetRange: function(target, min, max) { return true; },
MoveToTargetRange: function(target, min, max) { return true; },
StopMoving: function() { },
GetPassabilityClassName: function() { return "default"; },
"GetWalkSpeed": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"StopMoving": () => {},
"GetPassabilityClassName": () => "default"
});
AddMock(unit, IID_Vision, {
@ -207,6 +209,10 @@ function TestMoveIntoFormationWhileAttacking()
GetNumPlayers: function() { return 2; },
});
AddMock(SYSTEM_ENTITY, IID_ObstructionManager, {
"IsInTargetRange": (ent, target, min, max) => true
});
AddMock(playerEntity, IID_Player, {
IsAlly: function() { return false; },
IsEnemy: function() { return true; },
@ -237,12 +243,11 @@ function TestMoveIntoFormationWhileAttacking()
});
AddMock(unit + i, IID_UnitMotion, {
GetWalkSpeed: function() { return 1; },
MoveToFormationOffset: function(target, x, z) { },
IsInTargetRange: function(target, min, max) { return true; },
MoveToTargetRange: function(target, min, max) { return true; },
StopMoving: function() { },
GetPassabilityClassName: function() { return "default"; },
"GetWalkSpeed": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"StopMoving": () => {},
"GetPassabilityClassName": () => "default"
});
AddMock(unit + i, IID_Vision, {
@ -283,12 +288,11 @@ function TestMoveIntoFormationWhileAttacking()
});
AddMock(controller, IID_UnitMotion, {
GetWalkSpeed: function() { return 1; },
SetSpeedMultiplier: function(speed) { },
MoveToPointRange: function(x, z, minRange, maxRange) { },
IsInTargetRange: function(target, min, max) { return true; },
StopMoving: function() { },
GetPassabilityClassName: function() { return "default"; },
"GetWalkSpeed": () => 1,
"SetSpeedMultiplier": (speed) => {},
"MoveToPointRange": (x, z, minRange, maxRange) => {},
"StopMoving": () => {},
"GetPassabilityClassName": () => "default"
});
AddMock(controller, IID_Attack, {

View File

@ -53,10 +53,6 @@ AddMock(target, IID_Position, {
"GetPosition2D": () => { return { "x": 100, "y": 200 }; }
});
TS_ASSERT_EQUALS(cmpUnitMotionFlying.IsInTargetRange(target, 10, 112), true);
TS_ASSERT_EQUALS(cmpUnitMotionFlying.IsInTargetRange(target, 50, 111), false);
TS_ASSERT_EQUALS(cmpUnitMotionFlying.IsInTargetRange(target, 112, 200), false);
AddMock(entity, IID_GarrisonHolder, {
"AllowGarrisoning": () => {}
});

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -466,6 +466,15 @@ public:
}
virtual fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const;
virtual fixed MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const;
virtual fixed DistanceToTarget(entity_id_t ent, entity_id_t target) const;
virtual fixed MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const;
virtual fixed DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const;
virtual fixed MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const;
virtual bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const;
virtual bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const;
virtual bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const;
virtual bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false) const;
virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out) const;
@ -657,18 +666,173 @@ private:
REGISTER_COMPONENT_TYPE(ObstructionManager)
/**
* DistanceTo function family, all end up in calculating a vector length, DistanceBetweenShapes or
* MaxDistanceBetweenShapes. The MaxFoo family calculates the opposite edge opposite edge distance.
* When the distance is undefined we return -1.
*/
fixed CCmpObstructionManager::DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const
{
ObstructionSquare obstruction;
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
if (cmpObstruction && cmpObstruction->GetObstructionSquare(obstruction))
{
ObstructionSquare point;
point.x = px;
point.z = pz;
return DistanceBetweenShapes(obstruction, point);
}
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
if (!cmpPosition || !cmpPosition->IsInWorld())
return fixed::FromInt(-1);
ObstructionSquare s;
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(s))
return (CFixedVector2D(px, pz) - cmpPosition->GetPosition2D()).Length();
return (CFixedVector2D(cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y) - CFixedVector2D(px, pz)).Length();
}
return Geometry::DistanceToSquare(CFixedVector2D(px - s.x, pz - s.z), s.u, s.v, CFixedVector2D(s.hw, s.hh));
fixed CCmpObstructionManager::MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const
{
ObstructionSquare obstruction;
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction))
{
ObstructionSquare point;
point.x = px;
point.z = pz;
return MaxDistanceBetweenShapes(obstruction, point);
}
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
if (!cmpPosition || !cmpPosition->IsInWorld())
return fixed::FromInt(-1);
return (CFixedVector2D(cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y) - CFixedVector2D(px, pz)).Length();
}
fixed CCmpObstructionManager::DistanceToTarget(entity_id_t ent, entity_id_t target) const
{
ObstructionSquare obstruction;
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction))
{
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
if (!cmpPosition || !cmpPosition->IsInWorld())
return fixed::FromInt(-1);
return DistanceToPoint(target, cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y);
}
ObstructionSquare target_obstruction;
CmpPtr<ICmpObstruction> cmpObstructionTarget(GetSimContext(), target);
if (!cmpObstructionTarget || !cmpObstructionTarget->GetObstructionSquare(target_obstruction))
{
CmpPtr<ICmpPosition> cmpPositionTarget(GetSimContext(), target);
if (!cmpPositionTarget || !cmpPositionTarget->IsInWorld())
return fixed::FromInt(-1);
return DistanceToPoint(ent, cmpPositionTarget->GetPosition2D().X, cmpPositionTarget->GetPosition2D().Y);
}
return DistanceBetweenShapes(obstruction, target_obstruction);
}
fixed CCmpObstructionManager::MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const
{
ObstructionSquare obstruction;
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent);
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(obstruction))
{
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
if (!cmpPosition || !cmpPosition->IsInWorld())
return fixed::FromInt(-1);
return MaxDistanceToPoint(target, cmpPosition->GetPosition2D().X, cmpPosition->GetPosition2D().Y);
}
ObstructionSquare target_obstruction;
CmpPtr<ICmpObstruction> cmpObstructionTarget(GetSimContext(), target);
if (!cmpObstructionTarget || !cmpObstructionTarget->GetObstructionSquare(target_obstruction))
{
CmpPtr<ICmpPosition> cmpPositionTarget(GetSimContext(), target);
if (!cmpPositionTarget || !cmpPositionTarget->IsInWorld())
return fixed::FromInt(-1);
return MaxDistanceToPoint(ent, cmpPositionTarget->GetPosition2D().X, cmpPositionTarget->GetPosition2D().Y);
}
return MaxDistanceBetweenShapes(obstruction, target_obstruction);
}
fixed CCmpObstructionManager::DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const
{
// Sphere-sphere collision.
if (source.hh == fixed::Zero() && target.hh == fixed::Zero())
return (CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z)).Length() - source.hw - target.hw;
// Square to square.
if (source.hh != fixed::Zero() && target.hh != fixed::Zero())
return Geometry::DistanceSquareToSquare(
CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z),
source.u, source.v, CFixedVector2D(source.hw, source.hh),
target.u, target.v, CFixedVector2D(target.hw, target.hh));
// To cover both remaining cases, shape a is the square one, shape b is the circular one.
const ObstructionSquare& a = source.hh == fixed::Zero() ? target : source;
const ObstructionSquare& b = source.hh == fixed::Zero() ? source : target;
return Geometry::DistanceToSquare(
CFixedVector2D(b.x, b.z) - CFixedVector2D(a.x, a.z),
a.u, a.v, CFixedVector2D(a.hw, a.hh), true) - b.hw;
}
fixed CCmpObstructionManager::MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const
{
// Sphere-sphere collision.
if (source.hh == fixed::Zero() && target.hh == fixed::Zero())
return (CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z)).Length() + source.hw + target.hw;
// Square to square.
if (source.hh != fixed::Zero() && target.hh != fixed::Zero())
return Geometry::MaxDistanceSquareToSquare(
CFixedVector2D(target.x, target.z) - CFixedVector2D(source.x, source.z),
source.u, source.v, CFixedVector2D(source.hw, source.hh),
target.u, target.v, CFixedVector2D(target.hw, target.hh));
// To cover both remaining cases, shape a is the square one, shape b is the circular one.
const ObstructionSquare& a = source.hh == fixed::Zero() ? target : source;
const ObstructionSquare& b = source.hh == fixed::Zero() ? source : target;
return Geometry::MaxDistanceToSquare(
CFixedVector2D(b.x, b.z) - CFixedVector2D(a.x, a.z),
a.u, a.v, CFixedVector2D(a.hw, a.hh), true) + b.hw;
}
/**
* IsInRange function family depending on the DistanceTo family.
*
* In range if the edge to edge distance is inferior to maxRange
* and if the opposite edge to opposite edge distance is greater than minRange when the opposite bool is true
* or when the opposite bool is false the edge to edge distance is more than minRange.
*
* Using the opposite egde for minRange means that a unit is in range of a building if it is farther than
* clearance-buildingsize, which is generally going to be negative (and thus this returns true).
* NB: from a game POV, this means units can easily fire on buildings, which is good,
* but it also means that buildings can easily fire on units. Buildings are usually meant
* to fire from the edge, not the opposite edge, so this looks odd. For this reason one can choose
* to set the opposite bool false and use the edge to egde distance.
*
* We don't use squares because the are likely to overflow.
* We use a 0.0001 margin to avoid rounding errors.
*/
bool CCmpObstructionManager::IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const
{
fixed dist = DistanceToPoint(ent, px, pz);
return dist != fixed::FromInt(-1) && dist <= maxRange + fixed::FromFloat(0.0001) && (opposite ? MaxDistanceToPoint(ent, px, pz) : dist) >= minRange - fixed::FromFloat(0.0001);
}
bool CCmpObstructionManager::IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const
{
fixed dist = DistanceToTarget(ent, target);
return dist != fixed::FromInt(-1) && dist <= maxRange + fixed::FromFloat(0.0001) && (opposite ? MaxDistanceToTarget(ent, target) : dist) >= minRange- fixed::FromFloat(0.0001);
}
bool CCmpObstructionManager::IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const
{
entity_pos_t distance = (CFixedVector2D(x, z) - CFixedVector2D(px, pz)).Length();
return distance <= maxRange + fixed::FromFloat(0.0001) && distance >= minRange - fixed::FromFloat(0.0001);
}
bool CCmpObstructionManager::TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits) const

View File

@ -503,9 +503,7 @@ public:
}
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange);
virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const;
virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange);
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const;
virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z);
virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z);
@ -1057,8 +1055,9 @@ void CCmpUnitMotion::Move(fixed dt)
else
{
// check if target was reached in case of a moving target
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_TargetEntity);
if (!cmpUnitMotion || cmpUnitMotion->IsInTargetRange(GetEntityId(), m_TargetMinRange, m_TargetMaxRange))
if (!cmpUnitMotion || cmpObstructionManager->IsInTargetRange(GetEntityId(), m_TargetEntity, m_TargetMinRange, m_TargetMaxRange, false))
{
// Not in formation, so just finish moving
StopMoving();
@ -1471,49 +1470,6 @@ bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos
return true;
}
bool CCmpUnitMotion::IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const
{
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
if (!cmpPosition || !cmpPosition->IsInWorld())
return false;
CFixedVector2D pos = cmpPosition->GetPosition2D();
bool hasObstruction = false;
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
ICmpObstructionManager::ObstructionSquare obstruction;
//TODO if (cmpObstructionManager)
// hasObstruction = cmpObstructionManager->FindMostImportantObstruction(GetObstructionFilter(), x, z, m_Radius, obstruction);
if (minRange.IsZero() && maxRange.IsZero() && hasObstruction)
{
// Handle the non-ranged mode:
CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize);
// See if we're too close to the target square
if (distance < minRange)
return false;
// See if we're close enough to the target square
if (maxRange < entity_pos_t::Zero() || distance <= maxRange)
return true;
return false;
}
else
{
entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length();
if (distance < minRange)
return false;
else if (maxRange >= entity_pos_t::Zero() && distance > maxRange)
return false;
else
return true;
}
}
bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const
{
// Given a square, plus a target range we should reach, the shape at that distance
@ -1684,76 +1640,6 @@ bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange
return true;
}
bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const
{
// This function closely mirrors MoveToTargetRange - it needs to return true
// after that Move has completed
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
if (!cmpPosition || !cmpPosition->IsInWorld())
return false;
CFixedVector2D pos = cmpPosition->GetPosition2D();
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
if (!cmpObstructionManager)
return false;
bool hasObstruction = false;
ICmpObstructionManager::ObstructionSquare obstruction;
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), target);
if (cmpObstruction)
hasObstruction = cmpObstruction->GetObstructionSquare(obstruction);
if (hasObstruction)
{
CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize, true);
// Compare with previous obstruction
ICmpObstructionManager::ObstructionSquare previousObstruction;
cmpObstruction->GetPreviousObstructionSquare(previousObstruction);
entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize, true);
// See if we're too close to the target square
bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero();
if ((distance < minRange && previousDistance < minRange) || inside)
return false;
// See if we're close enough to the target square
if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange)
return true;
entity_pos_t circleRadius = halfSize.Length();
if (ShouldTreatTargetAsCircle(maxRange, circleRadius))
{
// The target is small relative to our range, so pretend it's a circle
// and see if we're close enough to that.
// Also check circle around previous position.
entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius;
entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
return circleDistance <= maxRange || previousCircleDistance <= maxRange;
}
// take minimal clearance required in MoveToTargetRange into account, multiplying by 3/2 for diagonals
entity_pos_t maxDist = std::max(maxRange, (m_Clearance + entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/16)*3/2);
return distance <= maxDist || previousDistance <= maxDist;
}
else
{
CmpPtr<ICmpPosition> cmpTargetPosition(GetSimContext(), target);
if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())
return false;
CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D();
entity_pos_t distance = (pos - targetPos).Length();
return minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange);
}
}
void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z)
{
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -23,6 +23,12 @@
BEGIN_INTERFACE_WRAPPER(ObstructionManager)
DEFINE_INTERFACE_METHOD_1("SetPassabilityCircular", void, ICmpObstructionManager, SetPassabilityCircular, bool)
DEFINE_INTERFACE_METHOD_CONST_3("DistanceToPoint", fixed, ICmpObstructionManager, DistanceToPoint, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpObstructionManager, SetDebugOverlay, bool)
DEFINE_INTERFACE_METHOD_CONST_3("DistanceToPoint", fixed, ICmpObstructionManager, DistanceToPoint, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_CONST_3("MaxDistanceToPoint", fixed, ICmpObstructionManager, MaxDistanceToPoint, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_CONST_2("DistanceToTarget", fixed, ICmpObstructionManager, DistanceToTarget, entity_id_t, entity_id_t)
DEFINE_INTERFACE_METHOD_CONST_2("MaxDistanceToTarget", fixed, ICmpObstructionManager, MaxDistanceToTarget, entity_id_t, entity_id_t)
DEFINE_INTERFACE_METHOD_CONST_6("IsInPointRange", bool, ICmpObstructionManager, IsInPointRange, entity_id_t, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t, bool)
DEFINE_INTERFACE_METHOD_CONST_5("IsInTargetRange", bool, ICmpObstructionManager, IsInTargetRange, entity_id_t, entity_id_t, entity_pos_t, entity_pos_t, bool)
DEFINE_INTERFACE_METHOD_CONST_6("IsPointInPointRange", bool, ICmpObstructionManager, IsPointInPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
END_INTERFACE_WRAPPER(ObstructionManager)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -53,6 +53,16 @@ class IObstructionTestFilter;
class ICmpObstructionManager : public IComponent
{
public:
/**
* Standard representation for all types of shapes, for use with geometry processing code.
*/
struct ObstructionSquare
{
entity_pos_t x, z; // position of center
CFixedVector2D u, v; // 'horizontal' and 'vertical' orthogonal unit vectors, representing orientation
entity_pos_t hw, hh; // half width, half height of square
};
/**
* External identifiers for shapes.
* (This is a struct rather than a raw u32 for type-safety.)
@ -164,6 +174,46 @@ public:
*/
virtual fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const = 0;
/**
* Calculate the largest straight line distance between the entity and the point.
*/
virtual fixed MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const = 0;
/**
* Calculate the shortest distance between the entity and the target.
*/
virtual fixed DistanceToTarget(entity_id_t ent, entity_id_t target) const = 0;
/**
* Calculate the largest straight line distance between the entity and the target.
*/
virtual fixed MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const = 0;
/**
* Calculate the shortest straight line distance between the source and the target
*/
virtual fixed DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const = 0;
/**
* Calculate the largest straight line distance between the source and the target
*/
virtual fixed MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const = 0;
/**
* Check if the given entity is in range of the other point given those parameters.
*/
virtual bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const = 0;
/**
* Check if the given entity is in range of the target given those parameters.
*/
virtual bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const = 0;
/**
* Check if the given point is in range of the other point given those parameters.
*/
virtual bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const = 0;
/**
* Collision test a flat-ended thick line against the current set of shapes.
* The line caps extend by @p r beyond the end points.
@ -224,16 +274,6 @@ public:
*/
virtual void UpdateInformations(GridUpdateInformation& informations) = 0;
/**
* Standard representation for all types of shapes, for use with geometry processing code.
*/
struct ObstructionSquare
{
entity_pos_t x, z; // position of center
CFixedVector2D u, v; // 'horizontal' and 'vertical' orthogonal unit vectors, representing orientation
entity_pos_t hw, hh; // half width, half height of square
};
/**
* Find all the obstructions that are inside (or partially inside) the given range.
* @param filter filter to restrict the shapes that are counted

View File

@ -24,8 +24,6 @@
BEGIN_INTERFACE_WRAPPER(UnitMotion)
DEFINE_INTERFACE_METHOD_4("MoveToPointRange", bool, ICmpUnitMotion, MoveToPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_CONST_4("IsInPointRange", bool, ICmpUnitMotion, IsInPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_CONST_3("IsInTargetRange", bool, ICmpUnitMotion, IsInTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("MoveToFormationOffset", void, ICmpUnitMotion, MoveToFormationOffset, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_2("FaceTowardsPoint", void, ICmpUnitMotion, FaceTowardsPoint, entity_pos_t, entity_pos_t)
@ -52,16 +50,6 @@ public:
return m_Script.Call<bool>("MoveToPointRange", x, z, minRange, maxRange);
}
virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const
{
return m_Script.Call<bool>("IsInPointRange", x, z, minRange, maxRange);
}
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const
{
return m_Script.Call<bool>("IsInTargetRange", target, minRange, maxRange);
}
virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
{
return m_Script.Call<bool>("MoveToTargetRange", target, minRange, maxRange);

View File

@ -44,18 +44,6 @@ public:
*/
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0;
/**
* Determine wether the givven point is within the given range, using the same measurement
* as MoveToPointRange.
*/
virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const = 0;
/**
* Determine whether the target is within the given range, using the same measurement
* as MoveToTargetRange.
*/
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const = 0;
/**
* Attempt to walk into range of a given target entity, or as close as possible.
* The range is measured between approximately the edges of the unit and the target, so that

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,6 +18,39 @@
#include "simulation2/system/ComponentTest.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpObstruction.h"
class MockObstruction : public ICmpObstruction
{
public:
DEFAULT_MOCK_COMPONENT()
ICmpObstructionManager::ObstructionSquare obstruction;
virtual ICmpObstructionManager::tag_t GetObstruction() const { return ICmpObstructionManager::tag_t(); }
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) const { out = obstruction; return true; }
virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& UNUSED(out)) const { return true; }
virtual entity_pos_t GetSize() const { return entity_pos_t::Zero(); }
virtual entity_pos_t GetUnitRadius() const { return entity_pos_t::Zero(); }
virtual void SetUnitClearance(const entity_pos_t& UNUSED(clearance)) { }
virtual bool IsControlPersistent() const { return true; }
virtual bool CheckShorePlacement() const { return true; }
virtual EFoundationCheck CheckFoundation(const std::string& UNUSED(className)) const { return EFoundationCheck(); }
virtual EFoundationCheck CheckFoundation(const std::string& UNUSED(className), bool UNUSED(onlyCenterPoint)) const { return EFoundationCheck(); }
virtual std::string CheckFoundation_wrapper(const std::string& UNUSED(className), bool UNUSED(onlyCenterPoint)) const { return std::string(); }
virtual bool CheckDuplicateFoundation() const { return true; }
virtual std::vector<entity_id_t> GetEntitiesByFlags(ICmpObstructionManager::flags_t UNUSED(flags)) const { return std::vector<entity_id_t>(); }
virtual std::vector<entity_id_t> GetEntitiesBlockingConstruction() const { return std::vector<entity_id_t>(); }
virtual std::vector<entity_id_t> GetEntitiesDeletedUponConstruction() const { return std::vector<entity_id_t>(); }
virtual void ResolveFoundationCollisions() const { }
virtual void SetActive(bool UNUSED(active)) { }
virtual void SetMovingFlag(bool UNUSED(enabled)) { }
virtual void SetDisableBlockMovementPathfinding(bool UNUSED(movementDisabled), bool UNUSED(pathfindingDisabled), int32_t UNUSED(shape)) { }
virtual bool GetBlockMovementFlag() const { return true; }
virtual void SetControlGroup(entity_id_t UNUSED(group)) { }
virtual entity_id_t GetControlGroup() const { return INVALID_ENTITY; }
virtual void SetControlGroup2(entity_id_t UNUSED(group2)) { }
virtual entity_id_t GetControlGroup2() const { return INVALID_ENTITY; }
};
class TestCmpObstructionManager : public CxxTest::TestSuite
{
@ -474,4 +507,85 @@ public:
TS_ASSERT_EQUALS(obSquare3.u, CFixedVector2D(fixed::FromInt(1), fixed::FromInt(0)));
TS_ASSERT_EQUALS(obSquare3.v, CFixedVector2D(fixed::FromInt(0), fixed::FromInt(1)));
}
/**
* Verifies the calculations of distances between shapes.
*/
void test_distance_to()
{
// Create two more entities to have non-zero distances
entity_id_t ent4 = 4,
ent4g1 = ent4,
ent4g2 = INVALID_ENTITY,
ent5 = 5,
ent5g1 = ent5,
ent5g2 = INVALID_ENTITY;
entity_pos_t ent4a = fixed::Zero(),
ent4w = fixed::FromInt(6),
ent4h = fixed::Zero(),
ent4x = ent1x,
ent4z = fixed::FromInt(20),
ent5a = fixed::Zero(),
ent5w = fixed::FromInt(2),
ent5h = fixed::FromInt(4),
ent5x = fixed::FromInt(20),
ent5z = ent1z;
tag_t shape4 = cmp->AddStaticShape(ent4, ent4x, ent4z, ent4a, ent4w, ent4h,
ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION |
ICmpObstructionManager::FLAG_BLOCK_MOVEMENT |
ICmpObstructionManager::FLAG_MOVING, ent4g1, ent4g2);
tag_t shape5 = cmp->AddStaticShape(ent5, ent5x, ent5z, ent5a, ent5w, ent5h,
ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION |
ICmpObstructionManager::FLAG_BLOCK_MOVEMENT |
ICmpObstructionManager::FLAG_MOVING, ent5g1, ent5g2);
MockObstruction obstruction1, obstruction2, obstruction3, obstruction4, obstruction5;
testHelper->AddMock(ent1, IID_Obstruction, obstruction1);
testHelper->AddMock(ent2, IID_Obstruction, obstruction2);
testHelper->AddMock(ent3, IID_Obstruction, obstruction3);
testHelper->AddMock(ent4, IID_Obstruction, obstruction4);
testHelper->AddMock(ent5, IID_Obstruction, obstruction5);
obstruction1.obstruction = cmp->GetObstruction(shape1);
obstruction2.obstruction = cmp->GetObstruction(shape2);
obstruction3.obstruction = cmp->GetObstruction(shape3);
obstruction4.obstruction = cmp->GetObstruction(shape4);
obstruction5.obstruction = cmp->GetObstruction(shape5);
TS_ASSERT_EQUALS(fixed::Zero(), cmp->DistanceToTarget(ent1, ent2));
TS_ASSERT_EQUALS(fixed::Zero(), cmp->DistanceToTarget(ent2, ent1));
TS_ASSERT_EQUALS(fixed::Zero(), cmp->DistanceToTarget(ent2, ent3));
TS_ASSERT_EQUALS(fixed::Zero(), cmp->DistanceToTarget(ent3, ent2));
// Due to rounding errors we need to use some leeway
TS_ASSERT_DELTA(fixed::FromFloat(std::sqrt(80)), cmp->MaxDistanceToTarget(ent2, ent3), fixed::FromFloat(0.0001));
TS_ASSERT_DELTA(fixed::FromFloat(std::sqrt(80)), cmp->MaxDistanceToTarget(ent3, ent2), fixed::FromFloat(0.0001));
TS_ASSERT_EQUALS(fixed::Zero(), cmp->DistanceToTarget(ent1, ent3));
TS_ASSERT_EQUALS(fixed::Zero(), cmp->DistanceToTarget(ent3, ent1));
TS_ASSERT_EQUALS(fixed::FromInt(6), cmp->DistanceToTarget(ent1, ent4));
TS_ASSERT_EQUALS(fixed::FromInt(6), cmp->DistanceToTarget(ent4, ent1));
TS_ASSERT_DELTA(fixed::FromFloat(std::sqrt(125) + 3), cmp->MaxDistanceToTarget(ent1, ent4), fixed::FromFloat(0.0001));
TS_ASSERT_DELTA(fixed::FromFloat(std::sqrt(125) + 3), cmp->MaxDistanceToTarget(ent4, ent1), fixed::FromFloat(0.0001));
TS_ASSERT_EQUALS(fixed::FromInt(7), cmp->DistanceToTarget(ent1, ent5));
TS_ASSERT_EQUALS(fixed::FromInt(7), cmp->DistanceToTarget(ent5, ent1));
TS_ASSERT_DELTA(fixed::FromFloat(std::sqrt(178)), cmp->MaxDistanceToTarget(ent1, ent5), fixed::FromFloat(0.0001));
TS_ASSERT_DELTA(fixed::FromFloat(std::sqrt(178)), cmp->MaxDistanceToTarget(ent5, ent1), fixed::FromFloat(0.0001));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent2, fixed::Zero(), fixed::FromInt(1), true));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent2, fixed::Zero(), fixed::FromInt(1), false));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent2, fixed::FromInt(1), fixed::FromInt(1), true));
TS_ASSERT(!cmp->IsInTargetRange(ent1, ent2, fixed::FromInt(1), fixed::FromInt(1), false));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent5, fixed::Zero(), fixed::FromInt(10), true));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent5, fixed::Zero(), fixed::FromInt(10), false));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent5, fixed::FromInt(1), fixed::FromInt(10), true));
TS_ASSERT(!cmp->IsInTargetRange(ent1, ent5, fixed::FromInt(1), fixed::FromInt(5), false));
TS_ASSERT(!cmp->IsInTargetRange(ent1, ent5, fixed::FromInt(10), fixed::FromInt(10), false));
TS_ASSERT(cmp->IsInTargetRange(ent1, ent5, fixed::FromInt(10), fixed::FromInt(10), true));
}
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -186,6 +186,73 @@ CFixedVector2D Geometry::NearestPointOnSquare(const CFixedVector2D& point, const
}
}
fixed Geometry::DistanceSquareToSquare(const CFixedVector2D& relativePos, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2)
{
/*
* The shortest distance between two non colliding squares equals the distance between a corner
* and other square. Thus calculating all 8 those distances and taking the smallest.
* For colliding squares we simply return 0. When one of the points is inside the other square
* we depend on DistanceToSquare's countInsideAsZero. When no point is inside the other square,
* it is enough to check that two adjacent edges of one square does not collide with the other square.
*/
fixed hw1 = halfSize1.X;
fixed hh1 = halfSize1.Y;
fixed hw2 = halfSize2.X;
fixed hh2 = halfSize2.Y;
if (TestRaySquare(relativePos + u1.Multiply(hw1) + v1.Multiply(hh1), relativePos - u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2) ||
TestRaySquare(relativePos + u1.Multiply(hw1) + v1.Multiply(hh1), relativePos + u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2))
return fixed::Zero();
return std::min(std::min(std::min(
DistanceToSquare(relativePos + u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true),
DistanceToSquare(relativePos + u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true)),
std::min(
DistanceToSquare(relativePos - u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true),
DistanceToSquare(relativePos - u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true))),
std::min(std::min(
DistanceToSquare(relativePos + u2.Multiply(hw2) + v2.Multiply(hh2), u1, v1, halfSize1, true),
DistanceToSquare(relativePos + u2.Multiply(hw2) - v2.Multiply(hh2), u1, v1, halfSize1, true)),
std::min(
DistanceToSquare(relativePos - u2.Multiply(hw2) + v2.Multiply(hh2), u1, v1, halfSize1, true),
DistanceToSquare(relativePos - u2.Multiply(hw2) - v2.Multiply(hh2), u1, v1, halfSize1, true))));
}
fixed Geometry::MaxDistanceToSquare(const CFixedVector2D& point, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize, bool countInsideAsZero)
{
fixed hw = halfSize.X;
fixed hh = halfSize.Y;
if (point.Dot(u).Absolute() < hw && point.Dot(v).Absolute() < hh && countInsideAsZero)
return fixed::Zero();
/*
* The maximum distance from a point to an edge of a square equals the greatest distance
* from the point to the a corner. Thus calculating all and taking the greatest.
*/
return std::max(std::max(
(point + u.Multiply(hw) + v.Multiply(hh)).Length(),
(point + u.Multiply(hw) - v.Multiply(hh)).Length()),
std::max(
(point - u.Multiply(hw) + v.Multiply(hh)).Length(),
(point - u.Multiply(hw) - v.Multiply(hh)).Length()));
}
fixed Geometry::MaxDistanceSquareToSquare(const CFixedVector2D& relativePos, const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1, const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2)
{
/*
* The maximum distance from an edge of a square to the edge of another square
* equals the greatest distance from the any of the 16 corner corner distances.
*/
fixed hw1 = halfSize1.X;
fixed hh1 = halfSize1.Y;
return std::max(std::max(
MaxDistanceToSquare(relativePos + u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true),
MaxDistanceToSquare(relativePos + u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true)),
std::max(MaxDistanceToSquare(relativePos - u1.Multiply(hw1) + v1.Multiply(hh1), u2, v2, halfSize2, true),
MaxDistanceToSquare(relativePos - u1.Multiply(hw1) - v1.Multiply(hh1), u2, v2, halfSize2, true)));
}
bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize)
{
/*

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -91,6 +91,32 @@ fixed DistanceToSquareSquared(const CFixedVector2D& point,
CFixedVector2D NearestPointOnSquare(const CFixedVector2D& point,
const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize);
/**
* Returns the shortest distance between two squares.
*/
fixed DistanceSquareToSquare(const CFixedVector2D& relativePos,
const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1,
const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2);
/**
* Returns the greatest straight line distance from a point to a square.
*
* If @p countInsideAsZero is true, and the point is inside the rectangle,
* it will return 0.
* If @p countInsideAsZero is false, the greatest (positive) distance to the boundary
* will be returned regardless of where the point is.
*/
fixed MaxDistanceToSquare(const CFixedVector2D& point,
const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize,
bool countInsideAsZero = false);
/**
* Return the greatest straight line distance between two squares.
*/
fixed MaxDistanceSquareToSquare(const CFixedVector2D& relativePos,
const CFixedVector2D& u1, const CFixedVector2D& v1, const CFixedVector2D& halfSize1,
const CFixedVector2D& u2, const CFixedVector2D& v2, const CFixedVector2D& halfSize2);
bool TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& u, const CFixedVector2D& v, const CFixedVector2D& halfSize);
bool TestRayAASquare(const CFixedVector2D& a, const CFixedVector2D& b, const CFixedVector2D& halfSize);