Package org.nlogo.agent

Source Code of org.nlogo.agent.Turtle

// (C) Uri Wilensky. https://github.com/NetLogo/NetLogo

package org.nlogo.agent;

import org.nlogo.api.AgentException;
import org.nlogo.api.AgentVariableNumbers;
import org.nlogo.api.AgentVariables;
import org.nlogo.api.Color;
import org.nlogo.api.Dump;
import org.nlogo.api.I18N;
import org.nlogo.api.LogoException;
import org.nlogo.api.LogoList;

import java.util.Iterator;
import java.util.Set;

// A note on wrapping: normally whether x and y coordinates wrap is a
// product of the topology.  But we also have the old "-nowrap" primitives
// that don't wrap regardless of what the topology is.  So that's why many
// methods like distance() and towards() take a boolean argument "wrap";
// it's true for the normal prims, false for the nowrap prims. - ST 5/24/06

public strictfp class Turtle
    extends Agent
    implements org.nlogo.api.Turtle {
  void id(long id) {
    this.id = id;
    variables[VAR_WHO] = Double.valueOf(id);
  }

  public static final int VAR_WHO = AgentVariableNumbers.VAR_WHO;
  public static final int VAR_COLOR = AgentVariableNumbers.VAR_COLOR;
  public static final int VAR_HEADING = AgentVariableNumbers.VAR_HEADING;
  public static final int VAR_XCOR = AgentVariableNumbers.VAR_XCOR;
  public static final int VAR_YCOR = AgentVariableNumbers.VAR_YCOR;
  static final int VAR_SHAPE = AgentVariableNumbers.VAR_SHAPE;
  public static final int VAR_LABEL = AgentVariableNumbers.VAR_LABEL;
  private static final int VAR_LABELCOLOR = AgentVariableNumbers.VAR_LABELCOLOR;
  static final int VAR_BREED = AgentVariableNumbers.VAR_BREED;
  private static final int VAR_HIDDEN = AgentVariableNumbers.VAR_HIDDEN;
  private static final int VAR_SIZE = AgentVariableNumbers.VAR_SIZE;
  private static final int VAR_PENSIZE = AgentVariableNumbers.VAR_PENSIZE;
  private static final int VAR_PENMODE = AgentVariableNumbers.VAR_PENMODE;

  public int LAST_PREDEFINED_VAR = VAR_PENMODE;
  public int NUMBER_PREDEFINED_VARS = LAST_PREDEFINED_VAR + 1;

  public static final String PEN_UP = "up";
  public static final String PEN_DOWN = "down";
  public static final String PEN_ERASE = "erase";


  void initvars(Double xcor, Double ycor, AgentSet breed) {
    variables[VAR_COLOR] = Color.BoxedBlack();
    heading = 0;
    variables[VAR_HEADING] = World.ZERO;
    this.xcor = xcor.doubleValue();
    variables[VAR_XCOR] = xcor;
    this.ycor = ycor.doubleValue();
    variables[VAR_YCOR] = ycor;
    variables[VAR_SHAPE] = world.turtleBreedShapes.breedShape(breed);
    variables[VAR_LABEL] = "";
    variables[VAR_LABELCOLOR] = Color.BoxedWhite();
    variables[VAR_BREED] = breed;
    variables[VAR_HIDDEN] = Boolean.FALSE;
    variables[VAR_SIZE] = World.ONE;
    variables[VAR_PENSIZE] = World.ONE;
    variables[VAR_PENMODE] = PEN_UP;
  }

  public Turtle(World world, AgentSet breed, Double xcor, Double ycor) {
    this(world, breed, xcor, ycor, true);
  }

  private Turtle(World world, AgentSet breed, Double xcor, Double ycor, boolean getId) {
    super(world);
    variables = new Object[world.getVariablesArraySize(this, breed)];
    if (getId) {
      id(world.newTurtleId());
      world.turtles().add(this);
    }
    initvars(xcor, ycor, breed);

    for (int i = LAST_PREDEFINED_VAR + 1; i < variables.length; i++) {
      variables[i] = World.ZERO;
    }
    if (breed != world.turtles()) {
      breed.add(this);
    }
    getPatchHere().addTurtle(this);
  }

  // creates a turtle that has id id, breed turtle, and is in the turtles agentset in
  // the idth slot in the agents array, if the slot was empty.  it is up to the caller to make sure
  // that the slot is open.  --mas 12/18/01
  Turtle(World world, long id) {
    this(world, world.turtles(), World.ZERO, World.ZERO, false);
    id(id);
    world.turtles().add(this);
  }


  Turtle(World world) {
    super(world);
  }

  public Turtle hatch() {
    Turtle child = new Turtle(world);
    child.heading = heading;
    child.xcor = xcor;
    child.ycor = ycor;
    child.variables = variables.clone();
    child.id(world.newTurtleId());
    world.turtles().add(child);
    if (getBreed() != world.turtles()) {
      getBreed().add(child);
    }
    child.getPatchHere().addTurtle(child);
    return child;
  }

  public void die() {
    if (id == -1) {
      return;
    }
    world.linkManager.cleanup(this);
    Patch patch = getPatchHere();
    patch.removeTurtle(this);
    AgentSet breed = getBreed();
    if (breed != world.turtles()) {
      breed.remove(agentKey());
    }
    world.removeLineThickness(this);
    world.turtles().remove(agentKey());
    id(-1);
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
  }

  public double lineThickness() {
    if (world != null) {
      return world.lineThickness(this);
    }
    return 0.0;
  }

  Patch currentPatch = null;

  @Override
  public Patch getPatchAtOffsets(double dx, double dy)
      throws AgentException {
    Patch target = world.getTopology().getPatchAt(xcor + dx, ycor + dy);
    if (target == null) {
      // Cannot get patch beyond limits of current world.
      throw new AgentException(I18N.errorsJ().get("org.nlogo.agent.Turtle.patchBeyondLimits"));
    }
    return target;
  }

  @Override
  Agent realloc(boolean compiling) {
    return realloc(compiling, null);
  }

  Agent realloc(boolean compiling, AgentSet oldBreed) {
    // first check if we recompiled and our breed disappeared!
    if (compiling && getBreed() != world.turtles() &&
        world.getBreed(getBreed().printName()) == null) {
      return this;
    }

    // stage 0: get ready
    Object[] oldvars = variables;
    variables = new Object[world.getVariablesArraySize(this, getBreed())];
    int turtlesOwnSize = world.getVariablesArraySize((Turtle) null, world.turtles());

    // stage 1: use arraycopy to copy over as many variables as possible
    // (if compiling it's just the predefined ones, if not compiling it's turtles-own too!)
    int numberToCopyDirectly = compiling ? NUMBER_PREDEFINED_VARS : turtlesOwnSize;
    System.arraycopy(oldvars, 0, variables, 0, numberToCopyDirectly);

    // stage 2: shift the turtles-own variables into their new positions
    // (unless we already did turtles-own during stage 1)
    if (compiling) {
      for (int i = NUMBER_PREDEFINED_VARS; i < turtlesOwnSize; i++) {
        String name = world.turtlesOwnNameAt(i);
        int oldpos = world.oldTurtlesOwnIndexOf(name);
        if (oldpos == -1) {
          variables[i] = World.ZERO;
        } else {
          variables[i] = oldvars[oldpos];
          oldvars[oldpos] = null;
        }
      }
    }

    // stage 3: handle the BREED-own variables
    for (int i = turtlesOwnSize; i < variables.length; i++) {
      String name = world.breedsOwnNameAt(getBreed(), i);
      int oldpos = compiling ? world.oldBreedsOwnIndexOf(getBreed(), name)
          : world.breedsOwnIndexOf(oldBreed, name);
      if (oldpos == -1) {
        variables[i] = World.ZERO;
      } else {
        variables[i] = oldvars[oldpos];
        oldvars[oldpos] = null;
      }
    }

    return null;
  }

  // note this is very similar to
  // World.getPatchAtDistanceAndHeading() - ST 9/3/03
  public void jump(double distance)
      throws AgentException {
    if (heading != cachedHeading) {
      cachedHeading = heading;
      int integerHeading = (int) heading;
      if (heading == integerHeading) {
        cachedCosine = TrigTables.cos()[integerHeading];
        cachedSine = TrigTables.sin()[integerHeading];
      } else {
        double headingRadians = StrictMath.toRadians(heading);
        cachedCosine = StrictMath.cos(headingRadians);
        cachedSine = StrictMath.sin(headingRadians);
      }
    }
    xandycor(xcor + (distance * cachedSine),
        ycor + (distance * cachedCosine));
  }

  public Patch getPatchAtHeadingAndDistance(double delta, double distance)
      throws AgentException {
    double h = heading + delta;
    if (h < 0 || h >= 360) {
      h = ((h % 360) + 360) % 360;
    }
    return world.protractor().getPatchAtHeadingAndDistance(this, h, distance);
  }

  public Patch getPatchHere() {
    if (currentPatch == null) {
      //turtles cannot leave the world, so xcor and ycor will always be valid
      //so assume we dont have to access the Topologies
      currentPatch = world.getPatchAtWrap(xcor, ycor);
    }
    return currentPatch;
  }

  private void mustOwn(String name)
      throws AgentException {
    if (name != null && !world.breedOwns(getBreed(), name)) {
      throw new AgentException(I18N.errorsJ().getN("org.nlogo.agent.Agent.breedDoesNotOwnVariable",
          getBreed().printName(), name));
    }
  }


  @Override
  public Object getVariable(int vn) {
    return getTurtleVariable(vn);
  }

  public String variableName(int vn) {
    if (vn < world.program().turtlesOwn().size()) {
      return world.turtlesOwnNameAt(vn);
    } else {
      return world.breedsOwnNameAt(getBreed(), vn);
    }
  }

  @Override
  public void setVariable(int vn, Object value)
      throws AgentException {
    setTurtleVariable(vn, value);
  }

  @Override
  public Object getObserverVariable(int vn) {
    return world.observer().getObserverVariable(vn);
  }

  @Override
  public Object getTurtleOrLinkVariable(String varName) {
    return getTurtleVariable(world.program().turtlesOwn().indexOf(varName));
  }

  @Override
  public Object getTurtleVariable(int vn) {
    if (vn == VAR_WHO) {
      if (variables[VAR_WHO] == null) {
        variables[VAR_WHO] = Double.valueOf(id);
      }
    } else if (vn == VAR_HEADING) {
      if (variables[VAR_HEADING] == null) {
        variables[VAR_HEADING] = Double.valueOf(heading);
      }
    } else if (vn == VAR_XCOR) {
      if (variables[VAR_XCOR] == null) {
        variables[VAR_XCOR] = Double.valueOf(xcor);
      }
    } else if (vn == VAR_YCOR && variables[VAR_YCOR] == null) {
      variables[VAR_YCOR] = Double.valueOf(ycor);
    }

    return variables[vn];
  }

  public double getTurtleVariableDouble(int vn) {
    switch (vn) {
      case VAR_WHO:
        return id;
      case VAR_HEADING:
        return heading;
      case VAR_XCOR:
        return xcor;
      case VAR_YCOR:
        return ycor;
      case VAR_SIZE:
        return size();
      case VAR_PENSIZE:
        return penSize();
      default:
        throw new IllegalArgumentException
            (I18N.errorsJ().getN("org.nlogo.agent.Agent.notADoubleVariable", vn));
    }
  }

  @Override
  public Object getLinkBreedVariable(String name)
      throws AgentException {
    throw new AgentException
        (I18N.errorsJ().get("org.nlogo.agent.Turtle.cantAccessLinkWithoutSpecifyingLink"));
  }

  @Override
  public Object getBreedVariable(String name)
      throws AgentException {
    mustOwn(name);
    int vn = world.breedsOwnIndexOf(getBreed(), name);
    return getTurtleVariable(vn);
  }

  public boolean ownsVariable(String name) {
    int vn = world.breedsOwnIndexOf(getBreed(), name);
    if (vn != -1) {
      return true;
    }
    vn = world.turtlesOwnIndexOf(name);
    if (vn != -1) {
      return true;
    }
    return false;
  }

  public Object getVariable(String name)
      throws AgentException {
    int vn = world.breedsOwnIndexOf(getBreed(), name);
    if (vn == -1) {
      vn = world.turtlesOwnIndexOf(name);
    }
    if (vn != -1) {
      return getTurtleVariable(vn);
    }
    throw new AgentException(I18N.errorsJ().getN("org.nlogo.agent.Agent.breedDoesNotOwnVariable", this.toString(), name));
  }

  @Override
  public Object getLinkVariable(int vn)
      throws AgentException {
    throw new AgentException
        (I18N.errorsJ().get("org.nlogo.agent.Turtle.cantAccessLinkWithoutSpecifyingLink"));
  }

  @Override
  public Object getPatchVariable(int vn) {
    return getPatchHere().getPatchVariable(vn);
  }

  @Override
  public void setObserverVariable(int vn, Object value)
      throws AgentException, LogoException {
    world.observer().setObserverVariable(vn, value);
  }

  @Override
  public void setTurtleOrLinkVariable(String varName, Object value)
      throws AgentException {
    setTurtleVariable(world.program().turtlesOwn().indexOf(varName), value);
  }

  @Override
  public void setTurtleVariable(int vn, double value)
      throws AgentException {
    switch (vn) {
      case VAR_COLOR:
        colorDouble(Double.valueOf(value));
        break;
      case VAR_HEADING:
        heading(value);
        break;
      case VAR_XCOR:
        xcor(value);
        break;
      case VAR_YCOR:
        ycor(value);
        break;
      case VAR_SIZE:
        size(value);
        break;
      case VAR_PENSIZE:
        penSize(value);
        break;
      case VAR_WHO:
        throw new AgentException(I18N.errorsJ().get("org.nlogo.agent.Turtle.cantChangeWho"));
      default:
        throw new IllegalArgumentException(I18N.errorsJ().getN("org.nlogo.agent.Agent.notADoubleVariable", vn));
    }
  }

  @Override
  public void setTurtleVariable(int vn, Object value)
      throws AgentException {
    if (vn > LAST_PREDEFINED_VAR) {
      variables[vn] = value;
    } else {
      switch (vn) {
        case VAR_COLOR:
          if (value instanceof Double) {
            colorDouble((Double) value);
          } else if (value instanceof LogoList) {
            color((LogoList) value, VAR_COLOR);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], Double.class, value);
          }
          break;
        case VAR_HEADING:
          if (value instanceof Double) {
            heading((Double) value);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], Double.class, value);
          }
          break;
        case VAR_XCOR:
          if (value instanceof Double) {
            xcor((Double) value);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], Double.class, value);
          }
          break;
        case VAR_YCOR:
          if (value instanceof Double) {
            ycor((Double) value);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], Double.class, value);
          }
          break;
        case VAR_SHAPE:
          if (value instanceof String) {
            String newShape = world.checkTurtleShapeName((String) value);
            if (newShape == null) {
              throw new AgentException
                  (I18N.errorsJ().getN("org.nlogo.agent.Agent.shapeUndefined", value));
            }
            shape(newShape);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], String.class, value);
          }
          break;
        case VAR_LABEL:
          label(value);
          break;
        case VAR_LABELCOLOR:
          if (value instanceof Double) {
            labelColor(((Double) value).doubleValue());
          } else if (value instanceof LogoList) {
            labelColor((LogoList) value, VAR_LABELCOLOR);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], Double.class, value);
          }
          break;
        case VAR_BREED:
          if (value instanceof AgentSet) {
            AgentSet breed = (AgentSet) value;
            if (breed != world.turtles() && !world.isBreed(breed)) {
              throw new AgentException(I18N.errorsJ().get("org.nlogo.agent.Turtle.cantSetBreedToNonBreedAgentSet"));
            }
            setBreed(breed);
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], AgentSet.class, value);
          }
          break;
        case VAR_HIDDEN:
          if (value instanceof Boolean) {
            hidden(((Boolean) value).booleanValue());
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn], Boolean.class, value);
          }
          break;
        case VAR_SIZE:
          if (value instanceof Double) {
            size(((Double) value).doubleValue());
          } else {
            wrongTypeForVariable
                (AgentVariables.getImplicitTurtleVariables(false)[vn],
                    Double.class, value);
          }
          break;
        case VAR_PENMODE:
          if (value instanceof String) {
            penMode((String) value);
          } else {
            wrongTypeForVariable(AgentVariables.getImplicitTurtleVariables(false)[vn],
                String.class, value);
          }
          break;

        case VAR_PENSIZE:
          if (value instanceof Double) {
            penSize(((Double) value).doubleValue());
          } else {
            wrongTypeForVariable(AgentVariables.getImplicitTurtleVariables(false)[vn],
                Double.class, value);
          }
          break;

        case VAR_WHO:
          throw new AgentException(I18N.errorsJ().get("org.nlogo.agent.Turtle.cantChangeWho"));
        default:
          throw new IllegalStateException(I18N.errorsJ().getN("org.nlogo.agent.Agent.cantSetUnknownVariable", vn));
      }
    }
    world.notifyWatchers(this, vn, value);
  }

  @Override
  public void setBreedVariable(String name, Object value)
      throws AgentException {
    mustOwn(name);
    int vn = world.breedsOwnIndexOf(getBreed(), name);
    setTurtleVariable(vn, value);
  }

  @Override
  public void setPatchVariable(int vn, Object value)
      throws AgentException {
    getPatchHere().setPatchVariable(vn, value);
  }

  @Override
  public void setPatchVariable(int vn, double value)
      throws AgentException {
    getPatchHere().setPatchVariable(vn, value);
  }

  @Override
  public void setLinkVariable(int vn, Object value)
      throws AgentException {
    throw new AgentException
        ("a turtle can't set a link variable without specifying which link");
  }

  @Override
  public void setLinkVariable(int vn, double value)
      throws AgentException {
    throw new AgentException
        ("a turtle can't set a link variable without specifying which link");
  }

  @Override
  public void setLinkBreedVariable(String name, Object value)
      throws AgentException {
    throw new AgentException
        ("a turtle can't set a link variable without specifying which link");
  }

  ///

  public Object color() {
    return variables[VAR_COLOR];
  }

  public void colorDouble(Double boxedColor) {
    double c = boxedColor.doubleValue();
    if (c < 0 || c >= Color.MaxColor()) {
      c = Color.modulateDouble(c);
      boxedColor = Double.valueOf(c);
    }
    variables[VAR_COLOR] = boxedColor;
  }

  public void colorDoubleUnchecked(Double boxedColor) {
    variables[VAR_COLOR] = boxedColor;
  }

  public void color(LogoList rgb, int varIndex)
      throws AgentException {
    validRGBList(rgb, true);
    variables[varIndex] = rgb;
    if(rgb.size() > 3) {
      world.mayHavePartiallyTransparentObjects = true;
    }
  }

  public void turnRight(double delta) {
    heading(heading + delta);
  }

  double heading = 0;
  double cachedHeading = 0;
  double cachedSine = 0;
  double cachedCosine = 1;

  public double heading() {
    return heading;
  }

  public void heading(double heading) {
    double originalHeading = this.heading;
    headingHelper(heading);
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleTurned(this, heading, originalHeading);
    }
  }

  // when we're calculating ties this gets called since we are recursing
  // and we need to pass the seenTurtles around ev 7/24/07
  public void heading(double heading, Set<Turtle> seenTurtles) {
    double originalHeading = this.heading;
    headingHelper(heading);
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleTurned(this, heading, originalHeading, seenTurtles);
    }
  }

  private void headingHelper(double heading) {
    if (heading < 0 || heading >= 360) {
      heading = ((heading % 360) + 360) % 360;
    }
    this.heading = heading;
    variables[VAR_HEADING] = null;
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
  }

  public void heading(Double heading) {
    double originalHeading = this.heading;
    double h = heading.doubleValue();
    double wrapped;
    if (h < 0 || h >= 360) {
      wrapped = ((h % 360) + 360) % 360;
    } else {
      wrapped = h;
    }
    this.heading = wrapped;
    if (h == wrapped) {
      variables[VAR_HEADING] = heading;
    } else {
      variables[VAR_HEADING] = null;
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleTurned(this, h, originalHeading);
    }
  }

  ///

  void drawLine(double x0, double y0, double x1, double y1) {
    if (!penMode().equals(PEN_UP) && (x0 != x1 || y0 != y1)) {
      world.drawLine(x0, y0, x1, y1, variables[VAR_COLOR], penSize(), penMode());
    }
  }

  public void moveTo(Agent otherAgent)
      throws AgentException {
    double x, y;
    if (otherAgent instanceof Turtle) {
      Turtle t = (Turtle) otherAgent;
      x = t.xcor();
      y = t.ycor();
    } else if (otherAgent instanceof Patch) {
      Patch p = (Patch) otherAgent;
      x = p.pxcor;
      y = p.pycor;
    } else {
      throw new AgentException("you can't move-to a link");
    }

    xandycor(shortestPathX(x), shortestPathY(y));
  }

  public double shortestPathY(double y)
      throws AgentException {
    // if the pen is not even down we don't care
    // what the shortest path is so just return.
    if (!penMode().equals(PEN_DOWN)) {
      return y;
    }

    // wrap the coords (we do this first in case we're trying to move outside
    // the world we want to trigger the exception.)
    y = world.wrapY(y);

    double yprime;

    // find the "unwrapped" coordinates
    // could we just be undoing what we just did? yes.
    // but do we really want to try and figure that out?
    if (y > ycor) {
      yprime = y - world.worldHeight();
    } else {
      yprime = y + world.worldHeight();
    }

    // techincally we're not supposed to check these
    // directly but it seems like the clearest thing to
    // do here since we don't want to change the values
    // just make sure that we're obeying the topology if
    // we were to use one of these paths.
    if (!world.wrappingAllowedInY()) {
      yprime = y;
    }

    if (StrictMath.abs(y - ycor) > StrictMath.abs(yprime - ycor)) {
      y = yprime;
    }

    return y;
  }

  public double shortestPathX(double x)
      throws AgentException {
    if (!penMode().equals(PEN_DOWN)) {
      return x;
    }

    x = world.wrapX(x);

    double xprime;

    if (x > xcor) {
      xprime = x - world.worldWidth();
    } else {
      xprime = x + world.worldWidth();
    }

    if (!world.wrappingAllowedInX()) {
      xprime = x;
    }

    if (StrictMath.abs(x - xcor) > StrictMath.abs(xprime - xcor)) {
      x = xprime;
    }

    return x;
  }

  double xcor;

  public double xcor() {
    return xcor;
  }

  public void xcor(double xcor)
      throws AgentException {
    Patch originalPatch = getPatchHere();

    double oldX = this.xcor;

    drawLine(this.xcor, ycor, shortestPathX(xcor), ycor);

    this.xcor = world.wrapX(xcor);

    variables[VAR_XCOR] = null;
    currentPatch = null;
    Patch targetPatch = getPatchHere();
    if (originalPatch != targetPatch) {
      originalPatch.removeTurtle(this);
      targetPatch.addTurtle(this);
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved
          (this, xcor, ycor, oldX, ycor);
    }
  }

  public void xcor(Double xcor)
      throws AgentException {
    Patch originalPatch = getPatchHere();
    double x = xcor.doubleValue();
    double oldX = this.xcor;

    double wrapped = world.wrapX(x);

    drawLine(this.xcor, ycor, shortestPathX(x), ycor);

    this.xcor = wrapped;
    if (x == wrapped) {
      variables[VAR_XCOR] = xcor;
    } else {
      variables[VAR_XCOR] = null;
    }
    currentPatch = null;
    Patch targetPatch = getPatchHere();
    if (originalPatch != targetPatch) {
      originalPatch.removeTurtle(this);
      targetPatch.addTurtle(this);
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved
          (this, x, ycor, oldX, ycor);
    }
  }

  double ycor;

  public double ycor() {
    return ycor;
  }

  public void ycor(double ycor)
      throws AgentException {
    Patch originalPatch = getPatchHere();

    double oldY = this.ycor;

    drawLine(xcor, this.ycor, xcor, shortestPathY(ycor));

    this.ycor = world.wrapY(ycor);

    variables[VAR_YCOR] = null;
    currentPatch = null;
    Patch targetPatch = getPatchHere();
    if (originalPatch != targetPatch) {
      originalPatch.removeTurtle(this);
      targetPatch.addTurtle(this);
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved(this, xcor, ycor, xcor, oldY);
    }
  }

  public void ycor(Double ycor)
      throws AgentException {
    Patch originalPatch = getPatchHere();
    double y = ycor.doubleValue();
    double oldY = this.ycor;
    double wrapped = world.wrapY(y);

    drawLine(xcor, this.ycor, xcor, shortestPathY(y));

    this.ycor = wrapped;
    if (y == wrapped) {
      variables[VAR_YCOR] = ycor;
    } else {
      variables[VAR_YCOR] = null;
    }
    currentPatch = null;
    Patch targetPatch = getPatchHere();
    if (originalPatch != targetPatch) {
      originalPatch.removeTurtle(this);
      targetPatch.addTurtle(this);
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved(this, xcor, y, xcor, oldY);
    }
  }

  public void xandycor(double xcor, double ycor)
      throws AgentException {
    double oldX = this.xcor;
    double oldY = this.ycor;
    xandycorHelper(xcor, ycor);
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved(this, xcor, ycor, oldX, oldY);
    }
  }

  // when we're calculating ties this gets called since we are recursing
  // and we need to pass the seenTurtles around ev 7/24/07
  public void xandycor(double xcor, double ycor, Set<Turtle> seenTurtles)
      throws AgentException {
    double oldX = this.xcor;
    double oldY = this.ycor;
    xandycorHelper(xcor, ycor);
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved(this, xcor, ycor, oldX, oldY, seenTurtles);
    }
  }

  public void xandycorHelper(double xcor, double ycor)
      throws AgentException {
    Patch originalPatch = getPatchHere();

    double newX = world.wrapX(xcor);
    double newY = world.wrapY(ycor);

    drawLine(this.xcor, this.ycor, xcor, ycor);

    this.xcor = newX;
    this.ycor = newY;

    variables[VAR_XCOR] = null;
    variables[VAR_YCOR] = null;
    currentPatch = null;
    Patch targetPatch = getPatchHere();
    if (originalPatch != targetPatch) {
      originalPatch.removeTurtle(this);
      targetPatch.addTurtle(this);
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
  }

  public void xandycor(Double xcor, Double ycor)
      throws AgentException {
    Patch originalPatch = getPatchHere();
    double x = xcor.doubleValue();
    double y = ycor.doubleValue();

    double oldX = this.xcor;
    double oldY = this.ycor;

    double wrappedX = world.wrapX(x);
    double wrappedY = world.wrapY(y);

    drawLine(this.xcor, this.ycor, x, y);

    this.xcor = wrappedX;
    this.ycor = wrappedY;
    variables[VAR_XCOR] = (x == wrappedX) ? xcor : null;
    variables[VAR_YCOR] = (y == wrappedY) ? ycor : null;
    currentPatch = null;
    Patch targetPatch = getPatchHere();
    if (originalPatch != targetPatch) {
      originalPatch.removeTurtle(this);
      targetPatch.addTurtle(this);
    }
    Observer observer = world.observer();
    if (this == observer.targetAgent()) {
      observer.updatePosition();
    }
    if (world.tieManager.tieCount > 0) {
      world.tieManager.turtleMoved(this, x, y, oldX, oldY);
    }
  }

  public void moveToPatchCenter() {
    Patch p = getPatchHere();
    double x = p.pxcor;
    double y = p.pycor;
    double oldX = this.xcor;
    double oldY = this.ycor;
    drawLine(oldX, oldY, x, y);
    if (x != oldX || y != oldY) {
      this.xcor = x;
      this.ycor = y;
      variables[VAR_XCOR] = p.variables[Patch.VAR_PXCOR];
      variables[VAR_YCOR] = p.variables[Patch.VAR_PYCOR];
      Observer observer = world.observer();
      if (this == observer.targetAgent()) {
        observer.updatePosition();
      }
      if (world.tieManager.tieCount > 0) {
        world.tieManager.turtleMoved(this, x, y, oldX, oldY);
      }
    }
  }

  public void face(Agent agent, boolean wrap) {
    try {
      heading(world.protractor().towards(this, agent, wrap));
    } catch (AgentException ex) {
      // AgentException here means we tried to calculate the heading from
      // an agent to itself, or to an agent at the exact same position.
      // Since face is nice, it just ignores the exception and doesn't change
      // the callers heading. - AZS 6/22/05
      org.nlogo.util.Exceptions.ignore(ex);
    }
  }

  public void face(double x, double y, boolean wrap) {
    try {
      heading(world.protractor().towards(this, x, y, wrap));
    } catch (AgentException ex) {
      // AgentException here means we tried to calculate the heading from
      // an agent to itself, or to an agent at the exact same position.
      // Since face is nice, it just ignores the exception and doesn't change
      // the callers heading. - AZS 6/22/05
      org.nlogo.util.Exceptions.ignore(ex);
    }
  }

  public static double subtractHeadings(double h1, double h2) {
    if (h1 < 0 || h1 >= 360) {
      h1 = (h1 % 360 + 360) % 360;
    }
    if (h2 < 0 || h2 >= 360) {
      h2 = (h2 % 360 + 360) % 360;
    }
    double diff = h1 - h2;
    if (diff > -180 && diff <= 180) {
      return diff;
    } else if (diff > 0) {
      return diff - 360;
    } else {
      return diff + 360;
    }
  }

  public void home() {
    try {
      xandycor(World.ZERO, World.ZERO);
    } catch (AgentException e) {
      // this will never happen since we require 0,0 be inside the world.
      throw new IllegalStateException(e);
    }
  }

  public double dx() {
    if (heading != cachedHeading) {
      cachedHeading = heading;
      int integerHeading = (int) heading;
      if (heading == integerHeading) {
        cachedCosine = TrigTables.cos()[integerHeading];
        cachedSine = TrigTables.sin()[integerHeading];
      } else {
        double headingRadians = StrictMath.toRadians(heading);
        cachedCosine = StrictMath.cos(headingRadians);
        cachedSine = StrictMath.sin(headingRadians);
      }
    }
    return cachedSine;
  }

  public double dy() {
    if (heading != cachedHeading) {
      cachedHeading = heading;
      int integerHeading = (int) heading;
      if (heading == integerHeading) {
        cachedCosine = TrigTables.cos()[integerHeading];
        cachedSine = TrigTables.sin()[integerHeading];
      } else {
        double headingRadians = StrictMath.toRadians(heading);
        cachedCosine = StrictMath.cos(headingRadians);
        cachedSine = StrictMath.sin(headingRadians);
      }
    }
    return cachedCosine;
  }

  public String shape() {
    return (String) variables[VAR_SHAPE];
  }

  public void shape(String shape) {
    variables[VAR_SHAPE] = shape;
  }

  public Object label() {
    return variables[VAR_LABEL];
  }

  public boolean hasLabel() {
    return !(label() instanceof String &&
        ((String) label()).length() == 0);
  }

  public String labelString() {
    return Dump.logoObject(variables[VAR_LABEL]);
  }

  public void label(Object label) {
    variables[VAR_LABEL] = label;
  }

  public Object labelColor() {
    return variables[VAR_LABELCOLOR];
  }

  public void labelColor(double labelColor) {
    variables[VAR_LABELCOLOR] = Double.valueOf(Color.modulateDouble(labelColor));
  }

  public void labelColor(LogoList rgb, int valueIndex)
      throws AgentException {
    validRGBList(rgb, true);
    variables[valueIndex] = rgb;
  }

  public AgentSet getBreed() {
    return (AgentSet) variables[VAR_BREED];
  }

  // returns the index of the breed of this turtle, 0 means just a turtle
  // this is super kludge. is there a better way? -AZS 10/28/04
  public int getBreedIndex() {
    AgentSet mybreed = getBreed();
    if (mybreed == world.turtles()) {
      return 0;
    }
    int j = 0;
    for (Iterator<Object> iter = world.program().breeds().values().iterator(); iter.hasNext(); j++) {
      if (mybreed == ((AgentSet) iter.next())) {
        return j;
      }
    }
    // we might get here if the program fails to compile ev 9/2/08
    return 0;
  }

  /**
   * This version of setBreed properly resets the global breed AgentSets
   * Caller should ensure that the turtle isn't a link (links aren't
   * allowed to change breed).
   */
  public void setBreed(AgentSet breed) {
    AgentSet oldBreed = null;
    if (variables[VAR_BREED] instanceof AgentSet) {
      oldBreed = (AgentSet) variables[VAR_BREED];
      if (breed == oldBreed) {
        return;
      }
      if (oldBreed != world.turtles()) {
        ((AgentSet) variables[VAR_BREED]).remove(agentKey());
      }
    }
    if (breed != world.turtles()) {
      breed.add(this);
    }
    variables[VAR_BREED] = breed;
    shape(world.turtleBreedShapes.breedShape(breed));
    realloc(false, oldBreed);
  }

  public boolean hidden() {
    return ((Boolean) variables[VAR_HIDDEN]).booleanValue();
  }

  public void hidden(boolean hidden) {
    variables[VAR_HIDDEN] = hidden ? Boolean.TRUE : Boolean.FALSE;
  }

  public double size() {
    return ((Double) variables[VAR_SIZE]).doubleValue();
  }

  public void size(double size) {
    variables[VAR_SIZE] = Double.valueOf(size);
  }

  public double penSize() {
    return ((Double) variables[VAR_PENSIZE]).doubleValue();
  }

  public void penSize(double penSize) {
    variables[VAR_PENSIZE] = Double.valueOf(penSize);
  }

  public String penMode() {
    return (String) variables[VAR_PENMODE];
  }

  public void penMode(String penMode) {
    variables[VAR_PENMODE] = penMode;
  }

  @Override
  public String toString() {
    return world.getBreedSingular(getBreed()).toLowerCase() + " " + id;
  }

  @Override
  public String classDisplayName() {
    return world.getBreedSingular(getBreed()).toLowerCase();
  }

  @Override
  public Class<Turtle> getAgentClass() {
    return Turtle.class;
  }

  public static final int BIT = 2;

  @Override
  public int getAgentBit() {
    return BIT;
  }

  public int alpha() {
    return org.nlogo.api.Color.getColor(color()).getAlpha();
  }


}
TOP

Related Classes of org.nlogo.agent.Turtle

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.