From 7d172a9b1dd071d264de354fd249cbed5263fc59 Mon Sep 17 00:00:00 2001 From: temple Date: Tue, 16 Jan 2018 02:15:04 +0000 Subject: [PATCH] Consider that the point could be inside the goal area in some PathGoal functions Reviewed By: wraitii Comments By: mimo Differential Revision: https://code.wildfiregames.com/D1089 This was SVN commit r20878. --- .../components/tests/test_Pathfinder.h | 49 ++++++++++++++++++- source/simulation2/helpers/PathGoal.cpp | 46 ++++++++++++++--- source/simulation2/helpers/PathGoal.h | 11 ++--- 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/source/simulation2/components/tests/test_Pathfinder.h b/source/simulation2/components/tests/test_Pathfinder.h index 707a58a03f..ae9a11af13 100644 --- a/source/simulation2/components/tests/test_Pathfinder.h +++ b/source/simulation2/components/tests/test_Pathfinder.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2018 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -64,6 +64,53 @@ public: TS_ASSERT_EQUALS((Pathfinding::NAVCELL_SIZE >> 1).ToInt_RoundToZero(), Pathfinding::NAVCELL_SIZE_LOG2); } + void test_pathgoal() + { + entity_pos_t i = Pathfinding::NAVCELL_SIZE; + CFixedVector2D u(i*1, i*0); + CFixedVector2D v(i*0, i*1); + + { + PathGoal goal = { PathGoal::POINT, i*8, i*6 }; + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*8 + v*4), u*8 + v*6); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*8 + v*4), i*2); + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*0 + v*0), u*8 + v*6); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*0 + v*0), i*10); + } + + { + PathGoal goal = { PathGoal::CIRCLE, i*8, i*6, i*5 }; + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*8 + v*4), u*8 + v*4); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*8 + v*4), i*0); + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*0 + v*0), u*4 + v*3); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*0 + v*0), i*5); + } + + { + PathGoal goal = { PathGoal::INVERTED_CIRCLE, i*8, i*6, i*5 }; + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*8 + v*4), u*8 + v*1); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*8 + v*4), i*3); + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*0 + v*0), u*0 + v*0); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*0 + v*0), i*0); + } + + { + PathGoal goal = { PathGoal::SQUARE, i*8, i*6, i*4, i*3, u, v }; + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*8 + v*4), u*8 + v*4); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*8 + v*4), i*0); + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*0 + v*0), u*4 + v*3); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*0 + v*0), i*5); + } + + { + PathGoal goal = { PathGoal::INVERTED_SQUARE, i*8, i*6, i*4, i*3, u, v }; + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*8 + v*4), u*8 + v*3); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*8 + v*4), i*1); + TS_ASSERT_EQUALS(goal.NearestPointOnGoal(u*0 + v*0), u*0 + v*0); + TS_ASSERT_EQUALS(goal.DistanceToPoint(u*0 + v*0), i*0); + } + } + void test_performance_DISABLED() { CTerrain terrain; diff --git a/source/simulation2/helpers/PathGoal.cpp b/source/simulation2/helpers/PathGoal.cpp index 462a630c38..d6710849f2 100644 --- a/source/simulation2/helpers/PathGoal.cpp +++ b/source/simulation2/helpers/PathGoal.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2018 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -293,21 +293,31 @@ bool PathGoal::RectContainsGoal(entity_pos_t x0, entity_pos_t z0, entity_pos_t x fixed PathGoal::DistanceToPoint(CFixedVector2D pos) const { + CFixedVector2D d(pos.X - x, pos.Y - z); + switch (type) { case POINT: - return (pos - CFixedVector2D(x, z)).Length(); + return d.Length(); case CIRCLE: + return d.CompareLength(hw) <= 0 ? fixed::Zero() : d.Length() - hw; + case INVERTED_CIRCLE: - return ((pos - CFixedVector2D(x, z)).Length() - hw).Absolute(); + return d.CompareLength(hw) >= 0 ? fixed::Zero() : hw - d.Length(); case SQUARE: + { + CFixedVector2D halfSize(hw, hh); + return Geometry::PointIsInSquare(d, u, v, halfSize) ? + fixed::Zero() : Geometry::DistanceToSquare(d, u, v, halfSize); + } + case INVERTED_SQUARE: { CFixedVector2D halfSize(hw, hh); - CFixedVector2D d(pos.X - x, pos.Y - z); - return Geometry::DistanceToSquare(d, u, v, halfSize); + return !Geometry::PointIsInSquare(d, u, v, halfSize) ? + fixed::Zero() : Geometry::DistanceToSquare(d, u, v, halfSize); } NODEFAULT; @@ -324,9 +334,21 @@ CFixedVector2D PathGoal::NearestPointOnGoal(CFixedVector2D pos) const return g; case CIRCLE: + { + CFixedVector2D d(pos.X - x, pos.Y - z); + if (d.CompareLength(hw) <= 0) + return pos; + + d.Normalize(hw); + return g + d; + } + case INVERTED_CIRCLE: { - CFixedVector2D d = pos - g; + CFixedVector2D d(pos.X - x, pos.Y - z); + if (d.CompareLength(hw) >= 0) + return pos; + if (d.IsZero()) d = CFixedVector2D(fixed::FromInt(1), fixed::Zero()); // some arbitrary direction d.Normalize(hw); @@ -334,11 +356,19 @@ CFixedVector2D PathGoal::NearestPointOnGoal(CFixedVector2D pos) const } case SQUARE: + { + CFixedVector2D halfSize(hw, hh); + CFixedVector2D d(pos.X - x, pos.Y - z); + return Geometry::PointIsInSquare(d, u, v, halfSize) ? + pos : g + Geometry::NearestPointOnSquare(d, u, v, halfSize); + } + case INVERTED_SQUARE: { CFixedVector2D halfSize(hw, hh); - CFixedVector2D d = pos - g; - return g + Geometry::NearestPointOnSquare(d, u, v, halfSize); + CFixedVector2D d(pos.X - x, pos.Y - z); + return !Geometry::PointIsInSquare(d, u, v, halfSize) ? + pos : g + Geometry::NearestPointOnSquare(d, u, v, halfSize); } NODEFAULT; diff --git a/source/simulation2/helpers/PathGoal.h b/source/simulation2/helpers/PathGoal.h index b2ba9f2911..83ccbe92be 100644 --- a/source/simulation2/helpers/PathGoal.h +++ b/source/simulation2/helpers/PathGoal.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2018 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -42,10 +42,10 @@ public: entity_pos_t x, z; // position of center - CFixedVector2D u, v; // if [INVERTED_]SQUARE, then orthogonal unit axes - entity_pos_t hw, hh; // if [INVERTED_]SQUARE, then half width & height; if [INVERTED_]CIRCLE, then hw is radius + CFixedVector2D u, v; // if [INVERTED_]SQUARE, then orthogonal unit axes + entity_pos_t maxdist; // maximum distance wanted between two path waypoints /** @@ -70,13 +70,12 @@ public: bool RectContainsGoal(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) const; /** - * Returns the minimum distance from the point with the given @p pos - * to any point on the outline of the goal shape. + * Returns the minimum distance from the point pos to any point on the goal shape. */ fixed DistanceToPoint(CFixedVector2D pos) const; /** - * Returns the coordinates of the point on the goal that is closest to pos in a straight line. + * Returns the coordinates of the point on the goal that is closest to the point pos. */ CFixedVector2D NearestPointOnGoal(CFixedVector2D pos) const; };