Package rlforj.los

Source Code of rlforj.los.PrecisePermissive

package rlforj.los;

import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import rlforj.math.Line2I;
import rlforj.math.Point2I;

/**
* Precise permissive visibility algorithm.
*
* Refer to
* {@link http://roguebasin.roguelikedevelopment.org/index.php?title=Precise_Permissive_Field_of_View}
* Copyright (c) 2007, Jonathon Duerig. Licensed under the BSD
* license. See LICENSE.txt for details.
*
* TODO : Do multitile organism by replacing offsetT(0,1)(1, 0) by offsetT(0,
* size.y) (size.x, 0). Also need to consider border tiles.
*
* @author sdatta
*
*/
public class PrecisePermissive implements IFovAlgorithm, ILosAlgorithm
{

  class permissiveMaskT
  {
    /*
     * Do not interact with the members directly. Use the provided
     * functions.
     */
    int north;

    int south;

    int east;

    int west;

    // int width;
    // int height;
    int[] mask;

    public FovType fovType;

    public int distPlusOneSq;

    ILosBoard board;
  }

//  class offsetT
//  {
//    public offsetT(int newX, int newY)
//    {
//      x = newX;
//      y = newY;
//    }
//
//    public int x;
//
//    public int y;
//
//    public String toString()
//    {
//      return "(" + x + ", " + y + ")";
//    }
//  }

  class fovStateT
  {
    Point2I source;

    permissiveMaskT mask;

    Object context;

    Point2I quadrant;

    Point2I extent;

    public int quadrantIndex;

    ILosBoard board;

    public boolean isLos = false;
  };

  class bumpT
  {
    public bumpT()
    {
    }

    Point2I location;

    bumpT parent = null;
   
    public String toString() {
      return location.toString()+" p( "+parent+" ) ";
    }
  }

  class fieldT
  {
    public fieldT(fieldT f)
    {
      steep = new Line2I(new Point2I(f.steep.near.x, f.steep.near.y),
          new Point2I(f.steep.far.x, f.steep.far.y));
      shallow = new Line2I(
          new Point2I(f.shallow.near.x, f.shallow.near.y),
          new Point2I(f.shallow.far.x, f.shallow.far.y));
      steepBump = f.steepBump;
      shallowBump = f.shallowBump;
    }

    public fieldT()
    {
      // TODO Auto-generated constructor stub
    }

    Line2I steep = new Line2I(new Point2I(0, 0), new Point2I(0, 0));

    Line2I shallow = new Line2I(new Point2I(0, 0), new Point2I(0, 0));

    bumpT steepBump;

    bumpT shallowBump;
   
    public String toString() {
      return "[ steep "+steep+",  shallow "+shallow+"]";
    }
  }

  private Vector<Point2I> path;
  private ILosAlgorithm fallBackLos=new BresLos(true);

  void calculateFovQuadrant(final fovStateT state)
  {
    // System.out.println("calcfovq called");
    LinkedList<bumpT> steepBumps = new LinkedList<bumpT>();
    LinkedList<bumpT> shallowBumps = new LinkedList<bumpT>();
    // activeFields is sorted from shallow-to-steep.
    LinkedList<fieldT> activeFields = new LinkedList<fieldT>();
    activeFields.addLast(new fieldT());
    activeFields.getLast().shallow.near = new Point2I(0, 1);
    activeFields.getLast().shallow.far = new Point2I(state.extent.x, 0);
    activeFields.getLast().steep.near = new Point2I(1, 0);
    activeFields.getLast().steep.far = new Point2I(0, state.extent.y);

    Point2I dest = new Point2I(0, 0);

    // Visit the source square exactly once (in quadrant 1).
    if (state.quadrant.x == 1 && state.quadrant.y == 1)
    {
      actIsBlocked(state, dest);
    }

    CLikeIterator<fieldT> currentField = new CLikeIterator<fieldT>(
        activeFields.listIterator());
    int i = 0;
    int j = 0;
    int maxI = state.extent.x + state.extent.y;
    // For each square outline
    for (i = 1; i <= maxI && !activeFields.isEmpty(); ++i)
    {
      int startJ = max(0, i - state.extent.x);
      int maxJ = min(i, state.extent.y);
      // System.out.println("Startj "+startJ+" maxj "+maxJ);
      // Visit the nodes in the outline
      for (j = startJ; j <= maxJ && !currentField.isAtEnd(); ++j)
      {
        // System.out.println("i j "+i+" "+j);
        dest.x = i - j;
        dest.y = j;
        visitSquare(state, dest, currentField, steepBumps,
            shallowBumps, activeFields);
      }
      // System.out.println("Activefields size "+activeFields.size());
      currentField = new CLikeIterator<fieldT>(activeFields
          .listIterator());
    }
  }

  private final int max(int i, int j)
  {
    return i > j ? i : j;
  }

  private final int min(int i, int j)
  {
    return i < j ? i : j;
  }

  void visitSquare(final fovStateT state, final Point2I dest,
      CLikeIterator<fieldT> currentField, LinkedList<bumpT> steepBumps,
      LinkedList<bumpT> shallowBumps, LinkedList<fieldT> activeFields)
  {
//    System.out.println("-> "+steepBumps+" - "+shallowBumps);
    // System.out.println("visitsq called "+dest);
    // The top-left and bottom-right corners of the destination square.
    Point2I topLeft = new Point2I(dest.x, dest.y + 1);
    Point2I bottomRight = new Point2I(dest.x + 1, dest.y);

    // fieldT currFld=null;

    while (!currentField.isAtEnd()
        && currentField.getCurrent().steep
            .isBelowOrContains(bottomRight))
    {
//      System.out.println("currFld.steep.isBelowOrContains(bottomRight) "
//          + currentField.getCurrent().steep
//              .isBelowOrContains(bottomRight));
      // case ABOVE
      // The square is in case 'above'. This means that it is ignored
      // for the currentField. But the steeper fields might need it.
      // ++currentField;
      currentField.gotoNext();
    }
    if (currentField.isAtEnd())
    {
//      System.out.println("currentField.isAtEnd()");
      // The square was in case 'above' for all fields. This means that
      // we no longer care about it or any squares in its diagonal rank.
      return;
    }

    // Now we check for other cases.
    if (currentField.getCurrent().shallow.isAboveOrContains(topLeft))
    {
      // case BELOW
      // The shallow line is above the extremity of the square, so that
      // square is ignored.
//      System.out.println("currFld.shallow.isAboveOrContains(topLeft) "
//          + currentField.getCurrent().shallow);
      return;
    }
    // The square is between the lines in some way. This means that we
    // need to visit it and determine whether it is blocked.
    boolean isBlocked = actIsBlocked(state, dest);
    if (!isBlocked)
    {
      // We don't care what case might be left, because this square does
      // not obstruct.
      return;
    }

    if (currentField.getCurrent().shallow.isAbove(bottomRight)
        && currentField.getCurrent().steep.isBelow(topLeft))
    {
      // case BLOCKING
      // Both lines intersect the square. This current field has ended.
      currentField.removeCurrent();
    } else if (currentField.getCurrent().shallow.isAbove(bottomRight))
    {
      // case SHALLOW BUMP
      // The square intersects only the shallow line.
      addShallowBump(topLeft, currentField.getCurrent(), steepBumps,
          shallowBumps);
      checkField(currentField);
    } else if (currentField.getCurrent().steep.isBelow(topLeft))
    {
      // case STEEP BUMP
      // The square intersects only the steep line.
      addSteepBump(bottomRight, currentField.getCurrent(), steepBumps,
          shallowBumps);
      checkField(currentField);
    } else
    {
      // case BETWEEN
      // The square intersects neither line. We need to split into two
      // fields.
      fieldT steeperField = currentField.getCurrent();
      fieldT shallowerField = new fieldT(currentField.getCurrent());
      currentField.insertBeforeCurrent(shallowerField);
      addSteepBump(bottomRight, shallowerField, steepBumps, shallowBumps);
      currentField.gotoPrevious();
      if (!checkField(currentField)) // did not remove
        currentField.gotoNext();// point to the original element
      addShallowBump(topLeft, steeperField, steepBumps, shallowBumps);
      checkField(currentField);
    }
  }

  boolean checkField(CLikeIterator<fieldT> currentField)
  {
    // If the two slopes are colinear, and if they pass through either
    // extremity, remove the field of view.
    fieldT currFld = currentField.getCurrent();
    boolean ret = false;

    if (currFld.shallow.doesContain(currFld.steep.near)
        && currFld.shallow.doesContain(currFld.steep.far)
        && (currFld.shallow.doesContain(new Point2I(0, 1)) || currFld.shallow
            .doesContain(new Point2I(1, 0))))
    {
//      System.out.println("removing "+currentField.getCurrent());
      currentField.removeCurrent();
      ret = true;
    }
    // System.out.println("CheckField "+ret);
    return ret;
  }

  void addShallowBump(final Point2I point, fieldT currFld,
      LinkedList<bumpT> steepBumps, LinkedList<bumpT> shallowBumps)
  {
//    System.out.println("Adding shallow "+point);
    // First, the far point of shallow is set to the new point.
    currFld.shallow.far = point;
    // Second, we need to add the new bump to the shallow bump list for
    // future steep bump handling.
    shallowBumps.addLast(new bumpT());
    shallowBumps.getLast().location = point;
    shallowBumps.getLast().parent = currFld.shallowBump;
    currFld.shallowBump = shallowBumps.getLast();
    // Now we have too look through the list of steep bumps and see if
    // any of them are below the line.
    // If there are, we need to replace near point too.
    bumpT currentBump = currFld.steepBump;
    while (currentBump != null)
    {
      if (currFld.shallow.isAbove(currentBump.location))
      {
        currFld.shallow.near = currentBump.location;
      }
      currentBump = currentBump.parent;
    }
  }

  void addSteepBump(final Point2I point, fieldT currFld,
      LinkedList<bumpT> steepBumps, LinkedList<bumpT> shallowBumps)
  {
//    System.out.println("Adding steep "+point);
    currFld.steep.far = point;
    steepBumps.addLast(new bumpT());
    steepBumps.getLast().location = point;
    steepBumps.getLast().parent = currFld.steepBump;
    currFld.steepBump = steepBumps.getLast();
    // Now look through the list of shallow bumps and see if any of them
    // are below the line.
    bumpT currentBump = currFld.shallowBump;
    while (currentBump != null)
    {
      if (currFld.steep.isBelow(currentBump.location))
      {
        currFld.steep.near = currentBump.location;
      }
      currentBump = currentBump.parent;
    }
  }

  boolean actIsBlocked(final fovStateT state, final Point2I pos)
  {
    Point2I adjustedPos = new Point2I(pos.x * state.quadrant.x
        + state.source.x, pos.y * state.quadrant.y + state.source.y);

    if(!state.board.contains(adjustedPos.x, adjustedPos.y))
      return false;//we are getting outside the board
   
    // System.out.println("actIsBlocked "+adjustedPos.x+" "+adjustedPos.y);

    // if ((state.quadrant.x * state.quadrant.y == 1
    // && pos.x == 0 && pos.y != 0)
    // || (state.quadrant.x * state.quadrant.y == -1
    // && pos.y == 0 && pos.x != 0)
    // || doesPermissiveVisit(state.mask, pos.x*state.quadrant.x,
    // pos.y*state.quadrant.y) == 0)
    // {
    // // return result;
    // }
    // else
    // {
    // board.visit(adjustedPos.x, adjustedPos.y);
    // // return result;
    // }
    /*
     * ^ | 2 | <-3-+-1-> | 4 | v
     *
     * To ensure all squares are visited before checked ( so that we can
     * decide obstacling at visit time, eg walls destroyed by explosion) ,
     * visit axes 1,2 only in Q1, 3 in Q2, 4 in Q3
     */
    if (state.isLos // In LOS calculation all visits allowed
        || state.quadrantIndex == 0 // can visit anything from Q1
        || (state.quadrantIndex == 1 && pos.x != 0) // Q2 : no Y axis
        || (state.quadrantIndex == 2 && pos.y != 0) // Q3 : no X axis
        || (state.quadrantIndex == 3 && pos.x != 0 && pos.y != 0)) // Q4
                                      // no X
                                      // or Y
                                      // axis
      if (doesPermissiveVisit(state.mask, pos.x * state.quadrant.x, pos.y
          * state.quadrant.y) == 1)
      {
        state.board.visit(adjustedPos.x, adjustedPos.y);
      }
    return state.board.isObstacle(adjustedPos.x, adjustedPos.y);
  }

  void permissiveFov(int sourceX, int sourceY, permissiveMaskT mask)
  {
    fovStateT state = new fovStateT();
    state.source = new Point2I(sourceX, sourceY);
    state.mask = mask;
    state.board = mask.board;
    // state.isBlocked = isBlocked;
    // state.visit = visit;
    // state.context = context;

    final int quadrantCount = 4;
    final Point2I quadrants[] = { new Point2I(1, 1), new Point2I(-1, 1),
        new Point2I(-1, -1), new Point2I(1, -1) };

    Point2I extents[] = { new Point2I(mask.east, mask.north),
        new Point2I(mask.west, mask.north),
        new Point2I(mask.west, mask.south),
        new Point2I(mask.east, mask.south) };
    int quadrantIndex = 0;
    for (; quadrantIndex < quadrantCount; ++quadrantIndex)
    {
      state.quadrant = quadrants[quadrantIndex];
      state.extent = extents[quadrantIndex];
      state.quadrantIndex = quadrantIndex;
      calculateFovQuadrant(state);
    }
  }

  int doesPermissiveVisit(permissiveMaskT mask, int x, int y)
  {
    if (mask.fovType == FovType.SQUARE)
      return 1;
    else if (mask.fovType == FovType.CIRCLE)
    {
      if (x * x + y * y < mask.distPlusOneSq)
        return 1;
      else
        return 0;
    }
    return 1;
  }

  public void visitFieldOfView(ILosBoard b, int x, int y, int distance)
  {
    permissiveMaskT mask = new permissiveMaskT();
    mask.east = mask.north = mask.south = mask.west = distance;
    mask.mask = null;
    mask.fovType = FovType.CIRCLE;
    mask.distPlusOneSq = (distance+1) * (distance+1);
    mask.board = b;
    permissiveFov(x, y, mask);
  }

  /**
   * Algorithm inspired by
   * http://groups.google.com/group/rec.games.roguelike.development/browse_thread/thread/f3506215be9d9f9a/2e543127f705a278#2e543127f705a278
   *
   * @see rlforj.los.ILosAlgorithm1#existsLineOfSight(rlforj.los.ILosBoard, int,
   *      int, int, int, boolean)
   */
  public boolean existsLineOfSight(ILosBoard b, int startX, int startY,
      int x1, int y1, boolean calculateProject)
  {
    permissiveMaskT mask = new permissiveMaskT();
    int dx = x1 - startX;
    int adx = dx > 0 ? dx : -dx;
    int dy = y1 - startY;
    int ady = dy > 0 ? dy : -dy;
    RecordQuadrantVisitBoard fb = new RecordQuadrantVisitBoard(b, startX, startY, x1, y1,
        calculateProject);
    mask.east = mask.west = adx;
    mask.north = mask.south = ady;
    mask.mask = null;
    mask.fovType = FovType.SQUARE;
    mask.distPlusOneSq = 0;
    mask.board = fb;

    fovStateT state = new fovStateT();
    state.source = new Point2I(startX, startY);
    state.mask = mask;
    state.board = fb;
    state.isLos = true;
    state.quadrant = new Point2I(dx < 0 ? -1 : 1, dy < 0 ? -1 : 1);
    state.quadrantIndex = 0;

    LinkedList<bumpT> steepBumps = new LinkedList<bumpT>();
    LinkedList<bumpT> shallowBumps = new LinkedList<bumpT>();
    // activeFields is sorted from shallow-to-steep.
    LinkedList<fieldT> activeFields = new LinkedList<fieldT>();
    activeFields.addLast(new fieldT());
    activeFields.getLast().shallow.near = new Point2I(0, 1);
    activeFields.getLast().shallow.far = new Point2I(adx + 1, 0);
    activeFields.getLast().steep.near = new Point2I(1, 0);
    activeFields.getLast().steep.far = new Point2I(0, ady + 1);

    Point2I dest = new Point2I(0, 0);

    Line2I stopLine = new Line2I(new Point2I(0, 1), new Point2I(adx, ady + 1)), startLine = new Line2I(
        new Point2I(1, 0), new Point2I(adx + 1, ady));

    // Visit the source square exactly once (in quadrant 1).
    actIsBlocked(state, dest);

    CLikeIterator<fieldT> currentField = new CLikeIterator<fieldT>(
        activeFields.listIterator());
    int maxI = adx + ady;
    // For each square outline
    int lastStartJ = -1;
    Point2I topLeft = new Point2I(0, 0), bottomRight = new Point2I(0, 0);
    for (int i = 1; i <= maxI && !activeFields.isEmpty(); ++i)
    {
      // System.out.println("i "+i);
      int startJ = max(0, i - adx);
      startJ = max(startJ, lastStartJ - 1);
      int maxJ = min(i, ady);

      // System.out.println("Startj "+startJ+" maxj "+maxJ);
      // Visit the nodes in the outline
      int thisStartJ = -1;
      // System.out.println("startJ "+startJ+" maxJ "+maxJ);
      for (int j = startJ; j <= maxJ && !currentField.isAtEnd(); ++j)
      {
        // System.out.println("i j "+i+" "+j);
        dest.x = i - j;
        dest.y = j;
        topLeft.x = dest.x;
        topLeft.y = dest.y + 1;
        bottomRight.x = dest.x + 1;
        bottomRight.y = dest.y;
        // System.out.println(startLine+" "+topLeft+" "+stopLine+"
        // "+bottomRight);
        // System.out.println("isbelow "+startLine.isBelow(topLeft)+"
        // isabove "+stopLine.isAbove(bottomRight));
        if (startLine.isAboveOrContains(topLeft))
        {
          // not in range, continue
          // System.out.println("below start");
          continue;
        }
        if (stopLine.isBelowOrContains(bottomRight))
        {
          // done
          // System.out.println("Above stop ");
          break;
        }
        // in range
        if (thisStartJ == -1)
          thisStartJ = j;
        visitSquare(state, dest, currentField, steepBumps,
            shallowBumps, activeFields);
      }
      lastStartJ = thisStartJ;
      // System.out.println("Activefields size "+activeFields.size());
      currentField = new CLikeIterator<fieldT>(activeFields
          .listIterator());
    }

    if (calculateProject)
    {
      if(fb.endVisited)
        path = GenericCalculateProjection.calculateProjecton(startX, startY, x1, y1, fb);
      else {
        fallBackLos.existsLineOfSight(b, startX, startY, x1, y1, true);
        path=(Vector<Point2I>)fallBackLos.getProjectPath();
      }
//      calculateProjecton(startX, startY, adx, ady, fb, state);
    }
    return fb.endVisited;
  }

  /*
   * (non-Javadoc)
   *
   * @see sid.los.ILosAlgorithm1#getProjectPath()
   */
  public List<Point2I> getProjectPath()
  {
    return path;
  }

//  public static void main(String[] args)
//  {
//    ILosAlgorithm pp = new PrecisePermissive();
//    TestBoard b = new TestBoard(false);
//    // b.exception.add(new Point2I(10, 11));
//    // b.exception.add(new Point2I(11, 10));
//    // b.exception.add(new Point2I(14, 15));
//    b.exception.add(new Point2I(14, 18));
//    System.out.println(pp.existsLineOfSight(b, -10, -10, -19, -15, true));
//
//    System.out.println(pp.getProjectPathX());
//    System.out.println(pp.getProjectPathY());
//
//    System.out.println(b.visited);
//  }


}
TOP

Related Classes of rlforj.los.PrecisePermissive

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.