www.pudn.com > Sterren_ASe_Explorer.rar > ase_losapproxboard.cpp


/* Copyright (C) William van der Sterren, 2002.  
 * All rights reserved worldwide. 
 * 
 * This software is provided "as is" without express or implied 
 * warranties. You may freely copy and compile this source into 
 * applications you distribute provided that the copyright text 
 * below is included in the resulting source code, for example: 
 * "Portions Copyright (C) William van der Sterren, 2002" 
 */ 
 
/***************************************************************************   
 *                                                                             
 *     purpose: approximated line-of-sight lookup table and overlay 
*/ 
 
 
#include "stdafx.h" 
#include "ase_losapproxboard.h" 
 
#include "ase_terrain.h" 
#include "ase_threatboard.h" 
 
#include  
#include  // though MSVC6 doesn't feature min/max there 
using namespace std; 
 
#include  
#include  
 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
 
COLORREF ASE_LOSApproximationBoard::m_LOSApproximationBrushes[] = 
  { 
    RGB(128,248, 96),  //!< safe  
    RGB(  0,180,  0),  //!<  
    RGB(  0,120,  0),  //!<  
    RGB(  0,  0,  0),  //!< not used? 
 
    RGB(248,208, 96),  //!< deep yellow 
    RGB(255,180,190),  //!<  
    RGB(255,120,120),  //!<  
    RGB(000,000,000),  //!< not used? 
 
    RGB(248,176, 96),  //!< orange 
    RGB(180,180,255),  //!<  
    RGB(120,120,255),  //!<  
    RGB(000,000,000),  //!< not used? 
 
    RGB(248,128, 96),  //!< red 
    RGB(180,255,180),  //!<  
    RGB(120,255,120),  //!<  
    RGB(000,000,000)   //!<  
  }; 
 
 
 
ASE_LOSApproximationBoard::ASE_LOSApproximationBoard(unsigned int            aNumberOfRows,  
                                                     unsigned int            aNumberOfColumns, 
                                                     const ASE_TerrainBoard* theTerrainBoard, 
                                                     const ASE_ThreatBoard*  theThreatBoard 
                                                    ) 
  : ASE_Board(aNumberOfRows, aNumberOfColumns, 5, m_LOSApproximationBrushes), 
    m_kTerrainBoard(theTerrainBoard), 
    m_kThreatBoard(theThreatBoard), 
    m_kDangerFromFarThreats(1), 
    m_kDangerFromNearThreats(2), 
    m_kDangerFromNearAndFarThreats(3) 
  { 
    assert( m_kTerrainBoard ); 
    assert( m_kThreatBoard ); 
    assert( GetNumberOfColumns() == m_kTerrainBoard->GetNumberOfColumns() ); 
    assert( GetNumberOfRows()    == m_kTerrainBoard->GetNumberOfRows() ); 
    assert( GetNumberOfColumns() == m_kThreatBoard->GetNumberOfColumns() ); 
    assert( GetNumberOfRows()    == m_kThreatBoard->GetNumberOfRows() ); 
 
    m_LineOfSightScanInfos.resize(GetNumberOfColumns() * GetNumberOfRows()); 
 
    Clear(); 
  } 
 
 
 
 
ASE_LOSApproximationBoard::~ASE_LOSApproximationBoard() 
  { 
    m_LineOfSightScanInfos.clear(); 
  } 
 
 
void      ASE_LOSApproximationBoard::Clear() 
  { 
    ASE_Board::Clear(); 
 
    // init changes 
    InitChangeRange(); 
  } 
 
 
float     ASE_LOSApproximationBoard::GetRiskValue(int aRow, int aCol) const 
  { 
    unsigned int idx; 
    idx = GetIndexForRowColumn(aRow, aCol); 
    return GetRiskValue(idx); 
  } 
 
 
float     ASE_LOSApproximationBoard::GetRiskValue(unsigned int anIndex) const 
  { 
    float result; 
    result  = static_cast(m_Cells[anIndex]); 
    if ( result > 0 ) 
      result -= 1; 
          
    result /= 3.0; 
    assert( result <= 1.0 ); 
    assert( result >= 0.0 ); 
    return result; 
  } 
 
COLORREF  ASE_LOSApproximationBoard::GetCellColorForOffset(unsigned int anIndex, CellValue anOffset) const 
  { 
    assert( !IsEmptyCell(anIndex) ); 
 
    unsigned int idx; 
    idx  = GetCellValue(anIndex); 
    idx  = 4 * (idx - 1); 
    idx += static_cast(anOffset); 
 
    assert( idx < sizeof(m_LOSApproximationBrushes) ); 
 
    return m_LOSApproximationBrushes[idx]; 
  } 
 
 
void ASE_LOSApproximationBoard::ResetChangeRange() 
  { 
    m_MinThreatX  = GetNumberOfColumns(); 
    m_MaxThreatX  = 0; 
    m_MinThreatY  = GetNumberOfRows(); 
    m_MaxThreatY  = 0; 
 
    m_MinTerrainX = GetNumberOfColumns();  
    m_MaxTerrainX = 0;                     
    m_MinTerrainY = GetNumberOfRows();     
    m_MaxTerrainY = 0;                     
  } 
 
 
void ASE_LOSApproximationBoard::InitChangeRange() 
  { 
    m_MinThreatX  = 0;  
    m_MaxThreatX  = GetNumberOfColumns(); 
    m_MinThreatY  = 0; 
    m_MaxThreatY  = GetNumberOfRows(); 
 
    m_MinTerrainX = 0;                      
    m_MaxTerrainX = GetNumberOfColumns();   
    m_MinTerrainY = 0;                      
    m_MaxTerrainY = GetNumberOfRows();      
  } 
 
 
void ASE_LOSApproximationBoard::GetChangeRange(int& aMinX, int& aMinY, int& aMaxX, int& aMaxY) 
  { 
    assert( DidAnythingChange() ); 
    aMinX = min(m_MinThreatX, m_MinTerrainX); 
    aMinY = min(m_MinThreatY, m_MinTerrainY); 
    aMaxX = max(m_MaxThreatX, m_MaxTerrainX); 
    aMaxY = max(m_MaxThreatY, m_MaxTerrainY); 
  } 
 
 
bool ASE_LOSApproximationBoard::DidAnythingChange() const 
  { 
    assert( ( m_MaxThreatX   >= m_MinThreatX  ) == ( m_MaxThreatY   >= m_MinThreatY  ) ); 
    assert( ( m_MaxTerrainX  >= m_MinTerrainX ) == ( m_MaxTerrainY  >= m_MinTerrainY ) ); 
    return (   ( m_MaxThreatX  >= m_MinThreatX  ) 
            || ( m_MaxTerrainX >= m_MinTerrainX ) 
           ); 
  } 
 
 
bool ASE_LOSApproximationBoard::IsPositionInRangeOfAnyThreat(int anX, int aY, int& /*!out*/ theDistance) 
  { 
    const int kRange    = m_kThreatBoard->GetMaxThreatReach(); 
    const int kRangeSqr = kRange * kRange; 
 
    CoordPairList kThreatPositions; 
    m_kThreatBoard->GetThreatPositions(kThreatPositions); 
 
    theDistance = 0; 
 
    // iterate over all known threats, and update value accordingly 
    CoordPairList::const_iterator threat; 
    for ( threat =  kThreatPositions.begin();  
          threat != kThreatPositions.end();  
          ++threat 
        ) 
      { 
        int dsqr; 
        dsqr = (anX - threat->x) * (anX - threat->x) + (aY - threat->y) * (aY - threat->y); 
        if ( dsqr <= kRangeSqr ) 
          { 
            int   dist; 
            float d; 
            d    = sqrt(dsqr); 
            dist = 1 + (kRange - static_cast(d)); 
            theDistance = max(theDistance, dist); 
          } 
      } 
 
    return ( theDistance > 0 ); 
  } 
 
 
void ASE_LOSApproximationBoard::ApplyTerrainModification(int anX, int aY) 
  { 
    // irrelevant if not near a threat 
    int threatviewingdist; 
 
    if ( !IsPositionInRangeOfAnyThreat(anX, aY, threatviewingdist) ) 
      return; 
 
    m_MinTerrainX = min(m_MinTerrainX, anX - threatviewingdist); 
    m_MinTerrainY = min(m_MinTerrainY, aY  - threatviewingdist); 
    m_MaxTerrainX = max(m_MaxTerrainX, anX + threatviewingdist); 
    m_MaxTerrainY = max(m_MaxTerrainY, aY  + threatviewingdist); 
  } 
 
 
void ASE_LOSApproximationBoard::ApplyThreatModification(int anX, int aY) 
  { 
    const int kMaxThreatReach = 1 + m_kThreatBoard->GetMaxThreatReach(); 
 
    // always relevant 
    m_MinThreatX = min(m_MinThreatX, anX - kMaxThreatReach); 
    m_MinThreatY = min(m_MinThreatY, aY  - kMaxThreatReach); 
    m_MaxThreatX = max(m_MaxThreatX, anX + kMaxThreatReach); 
    m_MaxThreatY = max(m_MaxThreatY, aY  + kMaxThreatReach); 
  } 
 
 
void ASE_LOSApproximationBoard::ComputeLinesOfFireApproximation() 
  { 
    // iterate over each cell, and compute its approximation 
    // use a number of short-cuts to cut down on computation time 
 
    assert( m_LineOfSightScanInfos.size() == GetNumberOfColumns() * GetNumberOfRows() ); 
 
    const int kEntries = GetNumberOfColumns() * GetNumberOfRows(); 
    const int kRange   = static_cast(m_kThreatBoard->GetMaxThreatReach()); 
 
    ASE_LineOfSightScan::SetThreatReachDistance(m_kThreatBoard->GetMaxThreatReach()); 
 
   
    int minr; 
    int minc; 
    int maxr; 
    int maxc; 
    if ( !DidAnythingChange() ) 
      return; 
 
    GetChangeRange(minr, minc, maxr, maxc); 
/* 
    minr = 0; 
    minc = 0; 
    maxr = -1 + static_cast(GetNumberOfRows()); 
    maxc = -1 + static_cast(GetNumberOfColumns()); 
*/ 
    PrepareListOfObstacles(); 
 
    for ( unsigned int idx = 0; idx < kEntries; ++idx ) 
      { 
        unsigned int row; 
        unsigned int col; 
        GetRowAndColumnForIndex(idx, row, col); 
 
        int irow; 
        int icol; 
        irow = static_cast(row); 
        icol = static_cast(col); 
 
        if (   ( irow >= minr ) 
            && ( irow <= maxr ) 
            && ( icol >= minc ) 
            && ( icol <= maxc ) 
           ) 
          { 
            if ( !IsValidBoardLocation(row, col) ) 
              continue; 
 
            // test for any nearby obstacle 
            if ( !IsAnyObstacleWithinRange(row, col, kRange) ) 
              { 
                SetLocationAsLackingAnyCover(row, col); 
              } 
            else 
              { 
                ApproximateLOFForLocationBySector(row, col); 
              } 
          } 
      } 
 
    UpdateBoard(); 
 
    ResetChangeRange(); 
  } 
 
 
void ASE_LOSApproximationBoard::PrepareListOfObstacles() 
  { 
    m_ObstaclePositions.clear(); 
 
    const int kEntries = GetNumberOfColumns() * GetNumberOfRows(); 
 
    for ( unsigned int idx = 0; idx < kEntries; ++idx ) 
      { 
        unsigned int row; 
        unsigned int col; 
        GetRowAndColumnForIndex(idx, row, col); 
 
        if ( m_kTerrainBoard->IsLocationImpassable(row, col) ) 
          { 
            ASE_CoordPair pos(row, col); 
            m_ObstaclePositions.push_back(pos); 
          } 
      } 
  } 
 
 
bool ASE_LOSApproximationBoard::IsAnyObstacleWithinRange(int x, int y, int theRange) const 
  { 
    const int rangeSqr = theRange * theRange; 
 
    CoordPairList::const_iterator pos; 
    for ( pos =  m_ObstaclePositions.begin(); 
          pos != m_ObstaclePositions.end(); 
          ++pos 
        ) 
      { 
        int dx; 
        int dy; 
        int dsqr; 
        dx   = pos->x - x; 
        dy   = pos->y - y; 
        dsqr = dx * dx + dy * dy; 
  
        if ( dsqr <= rangeSqr ) 
          return true; 
      } 
    return false; 
  } 
 
 
bool ASE_LOSApproximationBoard::IsAnyObstacleWithinRangeAndSector(int x, int y, int theRange, unsigned int aSector) const 
  { 
    const int rangeSqr = theRange * theRange; 
 
    CoordPairList::const_iterator pos; 
    for ( pos =  m_ObstaclePositions.begin(); 
          pos != m_ObstaclePositions.end(); 
          ++pos 
        ) 
      { 
        int dx; 
        int dy; 
        int dsqr; 
        dx   = pos->x - x; 
        dy   = pos->y - y; 
        dsqr = dx * dx + dy * dy; 
  
        if ( dsqr > rangeSqr ) 
          continue; 
 
        unsigned int sector; 
        sector = ASE_LineOfSightScan::GetSectorForLine(dx, dy); 
  
        if ( sector == aSector ) 
          return true; 
      } 
    return false; 
  } 
 
 
unsigned int ASE_LOSApproximationBoard::GetDistanceToNearestObstacleWithinRangeAndSector(int x, int y, int theRange, unsigned int aSector) const 
  { 
    const int rangeSqr = theRange * theRange; 
 
    int minSqr = ( (theRange + 1) * (theRange + 1) ); 
 
    CoordPairList::const_iterator pos; 
    for ( pos =  m_ObstaclePositions.begin(); 
          pos != m_ObstaclePositions.end(); 
          ++pos 
        ) 
      { 
        int dx; 
        int dy; 
        int dsqr; 
        dx   = pos->x - x; 
        dy   = pos->y - y; 
        dsqr = dx * dx + dy * dy; 
  
        if ( dsqr > rangeSqr ) 
          continue; 
 
        unsigned int sector; 
        sector = ASE_LineOfSightScan::GetSectorForLine(dx, dy); 
  
        if ( sector == aSector ) 
          { 
            minSqr = min(minSqr, dsqr); 
          } 
      } 
    return static_cast(minSqr); 
  } 
 
 
void ASE_LOSApproximationBoard::SetLocationAsLackingAnyCover(int x, int y) 
  { 
    ASE_LineOfSightScan los; 
    los.SetNoCoverAtAll(); 
 
    unsigned int idx; 
    idx = GetIndexForRowColumn(x, y); 
 
    m_LineOfSightScanInfos[idx] = los; 
  } 
 
 
void ASE_LOSApproximationBoard::ApproximateLOFForLocationBySector(int x, int y) 
  { 
    unsigned int idx; 
    idx = GetIndexForRowColumn(x, y); 
 
    m_LineOfSightScanInfos[idx].Clear(); 
 
    for ( unsigned int sector = 0; sector < ASE_LineOfSightScan::GetNumberOfSectors(); ++sector ) 
      { 
        unsigned int obstSqr; 
        obstSqr = GetDistanceToNearestObstacleWithinRangeAndSector(x, y, m_kThreatBoard->GetMaxThreatReach(), sector); 
 
        if (   ( ASE_LineOfSightScan::IsOutOfReachSqrd(obstSqr) ) 
            || ( ASE_LineOfSightScan::IsAFarDistanceSqrd(obstSqr) ) 
           ) 
          { 
            m_LineOfSightScanInfos[idx].UpdateSector(sector, ASE_LineOfSightScan::eObservedFromFarDistance); 
          } 
        else 
          { 
            ComputeSectorLOSForLocation(x, y, sector, m_kThreatBoard->GetMaxThreatReach()); 
          } 
      } 
  }  
 
 
void ASE_LOSApproximationBoard::ComputeSectorLOSForLocation(int x, int y, unsigned int aSector, unsigned int aRange) 
  { 
    // try each applicable ray from the template for this sector, until getting a eObservedFromFarDistance 
    const ASE_PieScan* pie = m_kThreatBoard->GetLineOfSightTemplate(); 
 
    unsigned int nr_of_rays; 
    nr_of_rays = pie->GetNumberOfRays(); 
 
    ASE_CoordPair here(x, y); 
    CoordPairList raytrace; 
 
    const int minmaxdistsqr = static_cast(aRange * aRange); 
    int maxdistsqr          = 0; 
 
    for ( unsigned int ray = 0; ray < nr_of_rays; ++ray ) 
      { 
        if ( pie->IsRayInSector(ray, aSector) ) 
          { 
            raytrace.clear(); 
            pie->GetRayForLocation(ray, here, raytrace); 
 
            CoordPairList::const_iterator pos; 
            for ( pos =  raytrace.begin();  
                  pos != raytrace.end();  
                  ++pos 
                ) 
              { 
                if ( !IsValidBoardLocation(pos->x, pos->y) ) 
                  break; 
 
                if ( m_kTerrainBoard->IsLocationImpassable(pos->x, pos->y) ) 
                  break; 
              } 
 
            // determine distance sqr 
            if (   ( pos != raytrace.end() ) 
                && ( pos != raytrace.begin() ) 
               )  
              { 
                int dx; 
                int dy; 
                int dsqr; 
                dx   = pos->x - x; 
                dy   = pos->y - y; 
                dsqr = (dx * dx) + (dy * dy); 
 
                maxdistsqr = max(maxdistsqr, dsqr);  
              } 
            else 
            if ( pos == raytrace.end() ) 
              { 
                maxdistsqr = minmaxdistsqr;  
              } 
 
            // test for bail out 
            if ( maxdistsqr >= minmaxdistsqr ) 
              break; 
          } 
      } 
 
    // check max distance, and set los accordingly 
    ASE_LineOfSightScan::ScanValue value; 
    if (   ( ASE_LineOfSightScan::IsOutOfReachSqrd(maxdistsqr) ) 
        || ( ASE_LineOfSightScan::IsAFarDistanceSqrd(maxdistsqr) ) 
       )  
      value = ASE_LineOfSightScan::eObservedFromFarDistance; 
    else 
    if ( maxdistsqr == 0 ) 
      value = ASE_LineOfSightScan::eNotObserved; 
    else 
    if ( ASE_LineOfSightScan::IsAMediumDistanceSqrd(maxdistsqr) ) 
      value = ASE_LineOfSightScan::eObservedFromMediumDistance; 
    else 
      value = ASE_LineOfSightScan::eObservedFromNearDistance; 
 
    unsigned int idx; 
    idx = GetIndexForRowColumn(x, y); 
    m_LineOfSightScanInfos[idx].UpdateSector(aSector, value); 
  } 
 
 
void ASE_LOSApproximationBoard::UpdateBoard() 
  { 
    /*! now update the board values, signaling danger positions 
        given current threats 
 
        1. set all cells to safe (0) 
        2. iterate over each threat 
           - then iterate over every position within reach of the threat 
           - if position is visible by threat, mark as danger 
    */ 
    const int kEntries = GetNumberOfColumns() * GetNumberOfRows(); 
 
    CoordPairList kThreatPositions; 
    m_kThreatBoard->GetThreatPositions(kThreatPositions); 
 
    for ( unsigned int idx = 0; idx < kEntries; ++idx ) 
      { 
        unsigned int row; 
        unsigned int col; 
        GetRowAndColumnForIndex(idx, row, col); 
 
        CellValue value; 
        value = 0; 
         
        // iterate over all known threats, and update value accordingly 
        CoordPairList::const_iterator threat; 
        for ( threat =  kThreatPositions.begin();  
              threat != kThreatPositions.end();  
              ++threat 
            ) 
          { 
            ASE_LineOfSightScan::ScanValue los1; 
            ASE_LineOfSightScan::ScanValue los2; 
            los1 = m_LineOfSightScanInfos[idx].GetObservationFromPosition(row, col, threat->x, threat->y); 
 
            unsigned int jdx; 
            jdx  = GetIndexForRowColumn(threat->x, threat->y); 
            los2 = m_LineOfSightScanInfos[jdx].GetObservationFromPosition(threat->x, threat->y, row, col); 
 
            if (   ( los1 != ASE_LineOfSightScan::eNotObserved ) 
                && ( los2 != ASE_LineOfSightScan::eNotObserved ) 
               ) 
              {   
                value = max(value, static_cast(los1)); 
              } 
          } 
        // skip over 'green' value 
        if ( value > 0 ) 
          value++; 
 
        SetCellValue(idx, value); 
      } 
  } 
 
 
/* 
void ASE_LOSApproximationBoard::ComputeLinesOfFireApproximation() 
  { 
     ComputeLinesOfFireApproximation2(); 
 
     if ( !DidAnythingChange() ) 
       return; 
 
     ASE_LineOfSightScan::SetThreatReachDistance(m_kThreatBoard->GetMaxThreatReach()); 
 
     int minx; 
     int miny; 
     int maxx; 
     int maxy; 
     GetChangeRange(minx, miny, maxx, maxy); 
 
     // adjust for threat range 
     minx = max(0, minx - m_kThreatBoard->GetMaxThreatReach()); 
     miny = max(0, miny - m_kThreatBoard->GetMaxThreatReach()); 
     maxx = min(-1 + GetNumberOfColumns(), maxx + m_kThreatBoard->GetMaxThreatReach()); 
     maxy = min(-1 + GetNumberOfRows(),    maxy + m_kThreatBoard->GetMaxThreatReach()); 
 
     InitObserversLOS(minx, miny, maxx, maxy); 
 
     // fall back on original area 
     GetChangeRange(minx, miny, maxx, maxy); 
     minx = max(0, minx); 
     miny = max(0, miny); 
     maxx = min(-1 + GetNumberOfColumns(), maxx); 
     maxy = min(-1 + GetNumberOfRows(),    maxy); 
 
     ProcessLocations(minx, miny, maxx, maxy); 
     UpdateBoard(minx, miny, maxx, maxy); 
 
     ResetChangeRange(); 
  } 
*/