From a9b961db50773fabf6b08bb3fd06ac3e1aa4931a Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Tue, 21 Jun 2011 21:43:06 +0400 Subject: [PATCH] [11659] Rewrite object pos selector. * Instead coordinates use angle projections to expected pos distance circle. This let do mostly only angle comparison and then speedup checks. * Fixed some long existed bugs in algo and simplify code. Possible more work need. --- src/game/Object.cpp | 127 ++++++-------- src/game/ObjectPosSelector.cpp | 295 ++++++++++++++++++++++----------- src/game/ObjectPosSelector.h | 144 ++++------------ src/shared/revision_nr.h | 2 +- 4 files changed, 283 insertions(+), 285 deletions(-) diff --git a/src/game/Object.cpp b/src/game/Object.cpp index a0f458c5b07..ce7741a41a7 100644 --- a/src/game/Object.cpp +++ b/src/game/Object.cpp @@ -1703,8 +1703,8 @@ namespace MaNGOS class NearUsedPosDo { public: - NearUsedPosDo(WorldObject const& obj, WorldObject const* searcher, float angle, ObjectPosSelector& selector) - : i_object(obj), i_searcher(searcher), i_angle(angle), i_selector(selector) {} + NearUsedPosDo(WorldObject const& obj, WorldObject const* searcher, float absAngle, ObjectPosSelector& selector) + : i_object(obj), i_searcher(searcher), i_absAngle(absAngle), i_selector(selector) {} void operator()(Corpse*) const {} void operator()(DynamicObject*) const {} @@ -1712,13 +1712,12 @@ namespace MaNGOS void operator()(Creature* c) const { // skip self or target - if(c==i_searcher || c==&i_object) + if (c == i_searcher || c == &i_object) return; - float x,y,z; + float x, y, z; - if( !c->isAlive() || c->hasUnitState(UNIT_STAT_NOT_MOVE) || - !c->GetMotionMaster()->GetDestination(x,y,z) ) + if (c->IsStopped() || !c->GetMotionMaster()->GetDestination(x, y, z)) { x = c->GetPositionX(); y = c->GetPositionY(); @@ -1728,10 +1727,10 @@ namespace MaNGOS } template - void operator()(T* u) const + void operator()(T* u) const { // skip self or target - if(u==i_searcher || u==&i_object) + if (u == i_searcher || u == &i_object) return; float x,y; @@ -1745,26 +1744,32 @@ namespace MaNGOS // we must add used pos that can fill places around center void add(WorldObject* u, float x, float y) const { + // dist include size of u and i_object + float dx = i_object.GetPositionX() - x; + float dy = i_object.GetPositionY() - y; + float dist2d = sqrt((dx * dx) + (dy * dy)); + + float delta = i_selector.m_searcherSize + u->GetObjectBoundingRadius(); + // u is too nearest/far away to i_object - if(!i_object.IsInRange2d(x,y,i_selector.m_dist - i_selector.m_size,i_selector.m_dist + i_selector.m_size)) + if (dist2d < i_selector.m_searcherDist - delta || + dist2d >= i_selector.m_searcherDist + delta) return; - float angle = i_object.GetAngle(u)-i_angle; + float angle = i_object.GetAngle(u) - i_absAngle; // move angle to range -pi ... +pi - while( angle > M_PI_F) + while (angle > M_PI_F) angle -= 2.0f * M_PI_F; - while(angle < -M_PI_F) + while (angle < -M_PI_F) angle += 2.0f * M_PI_F; - // dist include size of u - float dist2d = i_object.GetDistance2d(x,y); - i_selector.AddUsedPos(u->GetObjectBoundingRadius(), angle, dist2d + i_object.GetObjectBoundingRadius()); + i_selector.AddUsedArea(u->GetObjectBoundingRadius(), angle, dist2d); } private: WorldObject const& i_object; WorldObject const* i_searcher; - float i_angle; + float i_absAngle; ObjectPosSelector& i_selector; }; } // namespace MaNGOS @@ -1782,16 +1787,16 @@ void WorldObject::GetNearPoint2D(float &x, float &y, float distance2d, float abs void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float searcher_bounding_radius, float distance2d, float absAngle) const { - GetNearPoint2D(x, y, distance2d+searcher_bounding_radius, absAngle); + GetNearPoint2D(x, y, distance2d + searcher_bounding_radius, absAngle); z = GetPositionZ(); // if detection disabled, return first point if(!sWorld.getConfig(CONFIG_BOOL_DETECT_POS_COLLISION)) { if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available else - UpdateGroundPositionZ(x,y,z); + UpdateGroundPositionZ(x, y, z); return; } @@ -1801,113 +1806,79 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, bool first_los_conflict = false; // first point LOS problems // prepare selector for work - ObjectPosSelector selector(GetPositionX(), GetPositionY(), GetObjectBoundingRadius(), distance2d+searcher_bounding_radius); + ObjectPosSelector selector(GetPositionX(), GetPositionY(), distance2d + searcher_bounding_radius + GetObjectBoundingRadius(), searcher_bounding_radius); // adding used positions around object { - MaNGOS::NearUsedPosDo u_do(*this,searcher,absAngle,selector); - MaNGOS::WorldObjectWorker worker(this,u_do); + MaNGOS::NearUsedPosDo u_do(*this, searcher, absAngle, selector); + MaNGOS::WorldObjectWorker worker(this, u_do); - Cell::VisitAllObjects(this, worker, distance2d); + Cell::VisitAllObjects(this, worker, distance2d + searcher_bounding_radius); } // maybe can just place in primary position - if( selector.CheckOriginal() ) + if (selector.CheckOriginalAngle()) { if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available else - UpdateGroundPositionZ(x,y,z); + UpdateGroundPositionZ(x, y, z); - if(IsWithinLOS(x,y,z)) + if (IsWithinLOS(x, y, z)) return; first_los_conflict = true; // first point have LOS problems } - float angle; // candidate of angle for free pos - - // special case when one from list empty and then empty side preferred - if(selector.FirstAngle(angle)) - { - GetNearPoint2D(x,y,distance2d,absAngle+angle); - z = GetPositionZ(); - - if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available - else - UpdateGroundPositionZ(x,y,z); - - if(IsWithinLOS(x,y,z)) - return; - } - // set first used pos in lists selector.InitializeAngle(); + float angle; // candidate of angle for free pos + // select in positions after current nodes (selection one by one) - while(selector.NextAngle(angle)) // angle for free pos + while (selector.NextAngle(angle)) // angle for free pos { - GetNearPoint2D(x,y,distance2d,absAngle+angle); + GetNearPoint2D(x, y, distance2d + searcher_bounding_radius, absAngle + angle); z = GetPositionZ(); if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available else - UpdateGroundPositionZ(x,y,z); + UpdateGroundPositionZ(x, y, z); - if(IsWithinLOS(x,y,z)) + if (IsWithinLOS(x, y, z)) return; } // BAD NEWS: not free pos (or used or have LOS problems) // Attempt find _used_ pos without LOS problem - - if(!first_los_conflict) + if (!first_los_conflict) { x = first_x; y = first_y; if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available else - UpdateGroundPositionZ(x,y,z); + UpdateGroundPositionZ(x, y, z); return; } - // special case when one from list empty and then empty side preferred - if( selector.IsNonBalanced() ) - { - if(!selector.FirstAngle(angle)) // _used_ pos - { - GetNearPoint2D(x,y,distance2d,absAngle+angle); - z = GetPositionZ(); - - if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available - else - UpdateGroundPositionZ(x,y,z); - - if(IsWithinLOS(x,y,z)) - return; - } - } - // set first used pos in lists selector.InitializeAngle(); // select in positions after current nodes (selection one by one) - while(selector.NextUsedAngle(angle)) // angle for used pos but maybe without LOS problem + while (selector.NextUsedAngle(angle)) // angle for used pos but maybe without LOS problem { - GetNearPoint2D(x,y,distance2d,absAngle+angle); + GetNearPoint2D(x, y, distance2d + searcher_bounding_radius, absAngle + angle); z = GetPositionZ(); if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available else - UpdateGroundPositionZ(x,y,z); + UpdateGroundPositionZ(x, y, z); - if(IsWithinLOS(x,y,z)) + if (IsWithinLOS(x, y, z)) return; } @@ -1916,9 +1887,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, y = first_y; if (searcher) - searcher->UpdateAllowedPositionZ(x,y,z); // update to LOS height if available + searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available else - UpdateGroundPositionZ(x,y,z); + UpdateGroundPositionZ(x, y, z); } void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update) diff --git a/src/game/ObjectPosSelector.cpp b/src/game/ObjectPosSelector.cpp index b69263240ae..d7735fdee25 100644 --- a/src/game/ObjectPosSelector.cpp +++ b/src/game/ObjectPosSelector.cpp @@ -19,144 +19,253 @@ #include "ObjectPosSelector.h" #include "Object.h" -ObjectPosSelector::ObjectPosSelector(float x,float y,float size,float dist) -: m_center_x(x),m_center_y(y),m_size(size),m_dist(dist) +ObjectPosSelector::ObjectPosSelector(float x, float y, float dist, float searcher_size) : + m_centerX(x), m_centerY(y), m_searcherDist(dist), m_searcherSize(searcher_size) { // if size == 0, m_anglestep will become 0 -> freeze - if(m_size == 0.0f) - m_size = DEFAULT_WORLD_OBJECT_SIZE; + if (m_searcherSize == 0.0f) + m_searcherSize = DEFAULT_WORLD_OBJECT_SIZE; - m_anglestep = acos(m_dist/(m_dist+2*m_size)); + m_searcherHalfSize = asin(m_searcherSize/m_searcherDist); - m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].end(); - m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].end(); + // Really init in InitilizeAngle + m_nextUsedAreaItr[USED_POS_PLUS] = m_UsedAreaLists[USED_POS_PLUS].begin(); + m_nextUsedAreaItr[USED_POS_MINUS] = m_UsedAreaLists[USED_POS_MINUS].begin(); + m_nextUsedAreaStart[USED_POS_PLUS] = 0.0f; + m_nextUsedAreaStart[USED_POS_MINUS] = 0.0f; + m_stepAngle[USED_POS_PLUS] = 0.0f; + m_stepAngle[USED_POS_MINUS] = 0.0f; +} - m_smallStepAngle[USED_POS_PLUS] = 0; - m_smallStepAngle[USED_POS_MINUS] = 0; +/** + * Add used area (circle) near target object excluded from possible searcher position + * + * + * @param size Size of used circle + * @param angle Angle of used circle center point from target-searcher line + * @param dist Distance from target object center point to used circle center point + * + * Used circles data stored as projections to searcher dist size circle as angle coordinate and half angle size + */ +void ObjectPosSelector::AddUsedArea(float size, float angle, float dist) +{ + float sr_dist = size + m_searcherSize; - m_smallStepOk[USED_POS_PLUS] = false; - m_smallStepOk[USED_POS_MINUS] = false; + // by Law of cosines, angle of searcher/used centers + float sr_angle = acos((m_searcherDist * m_searcherDist + dist * dist - sr_dist * sr_dist) / (2 * m_searcherDist * dist)); - m_smallStepNextUsedPos[USED_POS_PLUS] = NULL; - m_smallStepNextUsedPos[USED_POS_MINUS] = NULL; + if (angle >= 0) + m_UsedAreaLists[USED_POS_PLUS].insert(UsedArea(angle, sr_angle)); + else + m_UsedAreaLists[USED_POS_MINUS].insert(UsedArea(-angle, sr_angle)); } -ObjectPosSelector::UsedPosList::value_type const* ObjectPosSelector::nextUsedPos(UsedPosType uptype) +/** + * Check searcher circle not intercepting with used circle + * + * @param usedArea Used circle as projection to searcher distance circle in angles form + * @param side Side of used circle + * @param angle Checked angle + * + * @return true, if used circle not intercepted with searcher circle in terms projection angles + */ +bool ObjectPosSelector::CheckAngle(UsedArea const& usedArea, UsedAreaSide side, float angle) const { - UsedPosList::const_iterator itr = m_nextUsedPos[uptype]; - if(itr!=m_UsedPosLists[uptype].end()) - ++itr; + float used_angle = usedArea.first * SignOf(side); + float used_offset = usedArea.second; - if(itr==m_UsedPosLists[uptype].end()) - { - if(!m_UsedPosLists[~uptype].empty()) - return &*m_UsedPosLists[~uptype].rbegin(); - else - return NULL; - } - else - return &*itr; + return fabs(used_angle - angle) > used_offset; } -void ObjectPosSelector::AddUsedPos(float size,float angle,float dist) +/** + * Check searcher circle not intercepting with used circle at side (only start angle provided) + * + * @param side Side of used circle + * @param angle Checked angle at side, positive always + * + * @return true, if used circle not intercepted with searcher circle in terms projection angles + */ +bool ObjectPosSelector::CheckSideAngle(UsedAreaSide side, float angle) const { - if(angle>=0) - m_UsedPosLists[USED_POS_PLUS].insert(UsedPosList::value_type(angle,UsedPos(1.0,size,dist))); - else - m_UsedPosLists[USED_POS_MINUS].insert(UsedPosList::value_type(-angle,UsedPos(-1.0,size,dist))); + return angle + m_searcherHalfSize < m_nextUsedAreaStart[side]; } +/** + * Check original (0.0f) angle fit to existed used area excludes + * + * @return true, if 0.0f angle with m_searcher_halfangle*2 angle size not intercept with used circles + */ +bool ObjectPosSelector::CheckOriginalAngle() const +{ + // check first left/right used angles if exists + return (m_UsedAreaLists[USED_POS_PLUS].empty() || CheckAngle(*m_UsedAreaLists[USED_POS_PLUS].begin(), USED_POS_PLUS, 0.0f)) && + (m_UsedAreaLists[USED_POS_MINUS].empty() || CheckAngle(*m_UsedAreaLists[USED_POS_MINUS].begin(), USED_POS_MINUS, 0.0f)); +} + +/** + * Initialize data for search angles starting from first possible angle at both sides + */ void ObjectPosSelector::InitializeAngle() { - m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].begin(); - m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].begin(); + InitializeAngle(USED_POS_PLUS); + InitializeAngle(USED_POS_MINUS); +} - m_smallStepAngle[USED_POS_PLUS] = 0; - m_smallStepAngle[USED_POS_MINUS] = 0; +/** + * Initialize data for search angles starting from first possible angle at side + */ +void ObjectPosSelector::InitializeAngle(UsedAreaSide side) +{ + m_nextUsedAreaItr[side] = m_UsedAreaLists[side].begin(); + UpdateNextAreaStart(side); - m_smallStepOk[USED_POS_PLUS] = true; - m_smallStepOk[USED_POS_MINUS] = true; + // if another side not alow use 0.0f angle calculate possible value in 0..m_searcherHalfSize range + if (!m_UsedAreaLists[~side].empty()) + { + UsedArea const& otherArea = *m_UsedAreaLists[~side].begin(); + + // if other are near start + if (otherArea.first < otherArea.second) + m_stepAngle[side] = otherArea.second - otherArea.first; + else + m_stepAngle[side] = 0.0f; + } + else + m_stepAngle[side] = 0.0f; } -bool ObjectPosSelector::FirstAngle(float& angle) +/** + * Update next used area start angle for current m_nextUsedAreaItr value at side + */ +void ObjectPosSelector::UpdateNextAreaStart(UsedAreaSide side) { - if(m_UsedPosLists[USED_POS_PLUS].empty() && !m_UsedPosLists[USED_POS_MINUS].empty() ) - return NextAngleFor(*m_UsedPosLists[USED_POS_MINUS].begin(),1.0,USED_POS_PLUS,angle); - else if(m_UsedPosLists[USED_POS_MINUS].empty() && !m_UsedPosLists[USED_POS_PLUS].empty() ) - return NextAngleFor(*m_UsedPosLists[USED_POS_PLUS].begin(),-1.0,USED_POS_MINUS,angle); + // not last next area at side + if (m_nextUsedAreaItr[side] != m_UsedAreaLists[side].end()) + { + m_nextUsedAreaStart[side] = m_nextUsedAreaItr[side]->first - m_nextUsedAreaItr[side]->second + m_searcherHalfSize; + return; + } - return false; + // last area at side and not another side areas + if (m_UsedAreaLists[~side].empty()) + { + m_nextUsedAreaStart[side] = M_PI_F + m_searcherHalfSize + 0.01f; + return; + } + + UsedArea const& lastArea = *m_UsedAreaLists[~side].rbegin(); + + // another side have used area near to end (near to PI) + if (lastArea.first + lastArea.second > M_PI_F - m_searcherHalfSize) + { + m_nextUsedAreaStart[side] = M_PI_F + (M_PI_F - lastArea.first - lastArea.second) + m_searcherHalfSize; + return; + } + + // last area and fail find any used area at another side, prepare fake data as stopper + m_nextUsedAreaStart[side] = M_PI_F + m_searcherHalfSize + 0.01f; } +/** + * Find next angle in free area + * + * @param angle Return at success found angle + * + * @return true, if angle found + */ bool ObjectPosSelector::NextAngle(float& angle) { - while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() || - m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() || - m_smallStepOk[USED_POS_PLUS] || m_smallStepOk[USED_POS_MINUS] ) + // loop until both side fail and leave 0..PI + for(;;) { - // calculate next possible angle - if(NextPosibleAngle(angle)) - return true; + // ++ direction less updated + if (m_stepAngle[USED_POS_PLUS] < M_PI_F && m_stepAngle[USED_POS_PLUS] <= m_stepAngle[USED_POS_MINUS]) + { + if (NextSideAngle(USED_POS_PLUS, angle)) + return true; + } + // -- direction less updated + else if (m_stepAngle[USED_POS_MINUS] < M_PI_F) + { + if (NextSideAngle(USED_POS_MINUS, angle)) + return true; + } + // both sides finishes + else + break; } + // no angles return false; } -bool ObjectPosSelector::NextUsedAngle(float& angle) +/** + * Find next angle at side + * + * @param side Side of angle + * @param angle Return at success found angle + * + * @return true, if angle found + * + */ +bool ObjectPosSelector::NextSideAngle(UsedAreaSide side, float &angle ) { - while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() || - m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() ) + // next possible angle + m_stepAngle[side] += (m_searcherHalfSize + 0.01); + + // prevent jump to another side + if (m_stepAngle[side] > M_PI_F) + return false; + + // update angle at attempt jump after next used area + while (m_stepAngle[side] <= M_PI_F && m_stepAngle[side] + m_searcherHalfSize >= m_nextUsedAreaStart[side]) { - // calculate next possible angle - if(!NextPosibleAngle(angle)) - return true; + // no used area for pass + if (m_nextUsedAreaItr[side] == m_UsedAreaLists[side].end()) + { + m_stepAngle[side] = M_PI_F + m_searcherHalfSize;// prevent continue search at side + return false; + } + + // angle set at first possible pos after passed m_nextUsedAreaItr + m_stepAngle[side] = m_nextUsedAreaItr[side]->first + m_nextUsedAreaItr[side]->second; + + ++m_nextUsedAreaItr[side]; + UpdateNextAreaStart(side); } - return false; + angle = m_stepAngle[side] * SignOf(side); + + // if next node not allow use selected angle, mark and fail + return CheckSideAngle(side, m_stepAngle[side]); } -bool ObjectPosSelector::NextPosibleAngle( float& angle ) +/** + * Find next angle in used area, that used if no angle found in free area with LoS + * + * @param angle Return at success found angle + * + * @return true, if angle found + */ +bool ObjectPosSelector::NextUsedAngle(float& angle) { + if (m_nextUsedAreaItr[USED_POS_PLUS] == m_UsedAreaLists[USED_POS_PLUS].end() && + m_nextUsedAreaItr[USED_POS_MINUS] == m_UsedAreaLists[USED_POS_MINUS].end()) + return false; + // ++ direction less updated - if( m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() && - (m_nextUsedPos[USED_POS_MINUS]==m_UsedPosLists[USED_POS_MINUS].end() || m_nextUsedPos[USED_POS_PLUS]->first <= m_nextUsedPos[USED_POS_MINUS]->first) ) + if (m_nextUsedAreaItr[USED_POS_PLUS] != m_UsedAreaLists[USED_POS_PLUS].end() && + (m_nextUsedAreaItr[USED_POS_MINUS] == m_UsedAreaLists[USED_POS_MINUS].end() || + m_nextUsedAreaItr[USED_POS_PLUS]->first <= m_nextUsedAreaItr[USED_POS_MINUS]->first)) { - bool ok; - if(m_smallStepOk[USED_POS_PLUS]) - ok = NextSmallStepAngle(1.0,USED_POS_PLUS,angle); - else - ok = NextAngleFor(*m_nextUsedPos[USED_POS_PLUS],1.0,USED_POS_PLUS,angle); - - if(!ok) - ++m_nextUsedPos[USED_POS_PLUS]; // increase. only at fail (original or checked) - return ok; + angle = m_nextUsedAreaItr[USED_POS_PLUS]->first * SignOf(USED_POS_PLUS); + ++m_nextUsedAreaItr[USED_POS_PLUS]; } - // -- direction less updated - else if( m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end()) - { - bool ok; - if(m_smallStepOk[USED_POS_MINUS]) - ok = NextSmallStepAngle(-1.0,USED_POS_MINUS,angle); - else - ok = NextAngleFor(*m_nextUsedPos[USED_POS_MINUS],-1.0,USED_POS_MINUS,angle); - - if(!ok) - ++m_nextUsedPos[USED_POS_MINUS]; - return ok; - } - else // both list empty + else { - if( m_smallStepOk[USED_POS_PLUS] && (!m_smallStepOk[USED_POS_MINUS] || m_smallStepAngle[USED_POS_PLUS] <= m_smallStepAngle[USED_POS_MINUS]) ) - { - return NextSmallStepAngle(1.0,USED_POS_PLUS,angle); - } - // -- direction less updated - else if( m_smallStepOk[USED_POS_MINUS] ) - { - return NextSmallStepAngle(-1.0,USED_POS_MINUS,angle); - } + angle = m_nextUsedAreaItr[USED_POS_MINUS]->first * SignOf(USED_POS_MINUS); + ++m_nextUsedAreaItr[USED_POS_MINUS]; } - // no angles - return false; + return true; } diff --git a/src/game/ObjectPosSelector.h b/src/game/ObjectPosSelector.h index ce55edc7b7d..2c57bc8801a 100644 --- a/src/game/ObjectPosSelector.h +++ b/src/game/ObjectPosSelector.h @@ -23,133 +23,51 @@ #include -enum UsedPosType { USED_POS_PLUS, USED_POS_MINUS }; +enum UsedAreaSide { USED_POS_PLUS, USED_POS_MINUS }; -inline UsedPosType operator ~(UsedPosType uptype) +inline UsedAreaSide operator ~(UsedAreaSide side) { - return uptype==USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS; + return side == USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS; } -struct ObjectPosSelector +inline float SignOf(UsedAreaSide side) { - struct UsedPos - { - UsedPos(float sign_, float size_,float dist_) : sign(sign_), size(size_),dist(dist_) {} + return side == USED_POS_PLUS ? 1.0f : -1.0f; +} - float sign; +struct ObjectPosSelector +{ + typedef std::multimap UsedAreaList; // angle pos -> angle offset + typedef UsedAreaList::value_type UsedArea; - float size; // size of point - float dist; // dist to central point (including central point size) - }; + ObjectPosSelector(float x, float y, float dist, float searcher_size); - typedef std::multimap UsedPosList; // abs(angle)->Node + void AddUsedArea(float size, float angle, float dist); - ObjectPosSelector(float x,float y,float size,float dist); + bool CheckOriginalAngle() const; - void AddUsedPos(float size,float angle,float dist); void InitializeAngle(); - bool FirstAngle(float& angle); bool NextAngle(float& angle); bool NextUsedAngle(float& angle); - bool NextPosibleAngle( float& angle ); - - bool CheckAngle(UsedPosList::value_type const& nextUsedPos, float sign, float angle ) const - { - float angle_step2 = GetAngle(nextUsedPos.second); - - float next_angle = nextUsedPos.first; - if(nextUsedPos.second.sign * sign < 0) // last node from diff. list (-pi+alpha) - next_angle = 2.0f*M_PI_F-next_angle; // move to positive - - return fabs(angle)+angle_step2 <= next_angle; - } - - bool CheckOriginal() const - { - return (m_UsedPosLists[USED_POS_PLUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_PLUS].begin(),1.0,0)) && - (m_UsedPosLists[USED_POS_MINUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_MINUS].begin(),-1.0,0)); - } - - bool IsNonBalanced() const { return m_UsedPosLists[USED_POS_PLUS].empty() != m_UsedPosLists[USED_POS_MINUS].empty(); } - - bool NextAngleFor( UsedPosList::value_type const& usedPos, float sign, UsedPosType uptype, float &angle ) - { - float angle_step = GetAngle(usedPos.second); - - // next possible angle - angle = usedPos.first * usedPos.second.sign + angle_step * sign; - - UsedPosList::value_type const* nextNode = nextUsedPos(uptype); - if(nextNode) - { - // if next node permit use selected angle, then do it - if(!CheckAngle(*nextNode, sign, angle)) - { - m_smallStepOk[uptype] = false; - return false; - } - } - - // possible more points - m_smallStepOk[uptype] = true; - m_smallStepAngle[uptype] = angle; - m_smallStepNextUsedPos[uptype] = nextNode; - - return true; - } - - bool NextSmallStepAngle( float sign, UsedPosType uptype, float &angle ) - { - // next possible angle - angle = m_smallStepAngle[uptype] + m_anglestep * sign; - - if(fabs(angle) > M_PI) - { - m_smallStepOk[uptype] = false; - return false; - } - - if(m_smallStepNextUsedPos[uptype]) - { - if(fabs(angle) >= m_smallStepNextUsedPos[uptype]->first) - { - m_smallStepOk[uptype] = false; - return false; - } - - // if next node permit use selected angle, then do it - if(!CheckAngle(*m_smallStepNextUsedPos[uptype], sign, angle)) - { - m_smallStepOk[uptype] = false; - return false; - } - } - - // possible more points - m_smallStepAngle[uptype] = angle; - return true; - } - - // next used post for m_nextUsedPos[uptype] - UsedPosList::value_type const* nextUsedPos(UsedPosType uptype); - - // angle from used pos to next possible free pos - float GetAngle(UsedPos const& usedPos) const { return acos(m_dist/(usedPos.dist+usedPos.size+m_size)); } - - float m_center_x; - float m_center_y; - float m_size; // size of object in center - float m_dist; // distance for searching pos (including central object size) - float m_anglestep; - - UsedPosList m_UsedPosLists[2]; - UsedPosList::const_iterator m_nextUsedPos[2]; - - // field for small step from first after next used pos until next pos - float m_smallStepAngle[2]; - bool m_smallStepOk[2]; - UsedPosList::value_type const* m_smallStepNextUsedPos[2]; + bool CheckAngle(UsedArea const& usedArea, UsedAreaSide side, float angle) const; + bool CheckSideAngle(UsedAreaSide side, float angle) const; + void InitializeAngle(UsedAreaSide side); + void UpdateNextAreaStart(UsedAreaSide side); + bool NextSideAngle(UsedAreaSide side, float& angle); + + float m_centerX; + float m_centerY; + float m_searcherDist; // distance for searching pos (including searcher size and target object size) + float m_searcherSize; // searcher object radius + float m_searcherHalfSize; // angle size/2 of searcher object (at dist distance) + + UsedAreaList m_UsedAreaLists[2]; // list left/right side used angles (with angle size) + + UsedAreaList::const_iterator m_nextUsedAreaItr[2]; // next used used areas for check at left/right side, possible angles selected in range m_smallStepAngle..m_nextUsedAreaItr + float m_nextUsedAreaStart[2]; // cached angle for next used area from m_nextUsedAreaItr or another side + + float m_stepAngle[2]; // current checked angle position at sides (less m_nextUsedArea), positive value }; #endif diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 7fd0441a6c3..f68bf5c2d77 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11658" + #define REVISION_NR "11659" #endif // __REVISION_NR_H__