public void removeCell(Cell cell) {
   if (stringToCellID.containsKey(cell.getName())) {
     stringToCellID.remove(cell.getName());
   } else {
     return; // return gracefully
   }
 }
 public void addCell(Cell cell) {
   if (!stringToCellID.containsKey(cell.getName())) {
     stringToCellID.put(cell.getName(), cell.getCellID());
   } else {
     return; // return gracefully.
   }
 }
 protected MovableComponent getMovable(Cell cell) {
   MovableComponent mc = cell.getComponent(MovableComponent.class);
   if (mc != null) {
     return mc;
   } else {
     addMovableComponent(cell);
     return cell.getComponent(MovableComponent.class);
   }
 }
  protected void applyDelta(Vector3f deltaTranslation, Quaternion deltaRotation) {
    LOGGER.warning("Applying delta: " + deltaTranslation + " " + deltaRotation);

    boolean startedDrag = false;
    if (!dragging) {
      // no drag in progress. Start one now and end it after the drag
      // operation
      startDrag();
      startedDrag = true;
    }

    for (Cell cell : selected.getSelectedCells()) {
      CellTransform transform = cell.getLocalTransform();
      Vector3f translate = transform.getTranslation(null);
      Quaternion rotation = transform.getRotation(null);

      // if the cell has a parent, make sure to take the parent's
      // rotation and scale into account when applying the delta
      Vector3f localDeltaTranslation = deltaTranslation.clone();
      Cell parent = cell.getParent();
      if (parent != null) {
        CellTransform parentWorld = parent.getWorldTransform();
        Quaternion parentRotation = parentWorld.getRotation(null);
        float parentScale = parentWorld.getScaling();

        LOGGER.warning("Parent transform: " + parentWorld);

        // invert the rotation to get the child rotation
        parentRotation.inverseLocal();
        localDeltaTranslation = parentRotation.mult(deltaTranslation);
        localDeltaTranslation.multLocal(parentScale);

        LOGGER.warning("Local delta translation: " + localDeltaTranslation);
      }

      translate.addLocal(localDeltaTranslation);
      rotation.multLocal(deltaRotation);
      transform.setTranslation(translate);
      transform.setRotation(rotation);

      MovableComponent mc = getMovable(cell);
      if (mc != null) {
        mc.localMoveRequest(transform);
      }
    }
    lastTranslation.addLocal(deltaTranslation);
    lastRotation.multLocal(deltaRotation);
    // if we started a drag, remember to end it
    if (startedDrag) {
      endDrag();
    }
  }
  /** Adds the movable component, assumes it does not already exist. */
  private void addMovableComponent(Cell cell) {

    // Go ahead and try to add the affordance. If we cannot, then log an
    // error and return.
    CellID cellID = cell.getCellID();
    String className = "org.jdesktop.wonderland.server.cell.MovableComponentMO";
    CellServerComponentMessage cscm = CellServerComponentMessage.newAddMessage(cellID, className);
    ResponseMessage response = cell.sendCellMessageAndWait(cscm);
    if (response instanceof ErrorMessage) {
      LOGGER.warning(
          "Unable to add movable component for Cell"
              + cell.getName()
              + " with ID "
              + cell.getCellID());
    }
  }
  public AvatarImiJME(Cell cell) {
    super(cell);
    assert (cell != null);
    final Cell c = cell;

    // Listen for avatar configuration changes.
    AvatarConfigComponent comp = cell.getComponent(AvatarConfigComponent.class);
    comp.addAvatarConfigChangeListener(new AvatarChangeListener());

    // XXX NPC HACK XXX
    if (cell instanceof AvatarCell) username = ((AvatarCell) cell).getIdentity().getUsername();
    else username = "******"; // HACK !

    characterMotionListener =
        new CharacterMotionListener() {
          Vector3f prevTrans;
          PMatrix prevRot;
          float prevHeight;
          boolean prevCollision;

          public void transformUpdate(Vector3f translation, PMatrix rotation) {
            if (logger.isLoggable(Level.FINEST)) {
              logger.finest(
                  "Transform update: translation: prev: "
                      + prevTrans
                      + " cur: "
                      + translation
                      + " rotation: prev: "
                      + prevRot
                      + " cur: "
                      + rotation);
            }

            float height = avatarCharacter.getController().getHeight();
            boolean collision = avatarCharacter.getController().isColliding();

            if (prevTrans == null
                || !Math3DUtils.epsilonEquals(prevTrans, translation, 0.001f)
                || prevRot == null
                || !prevRot.epsilonEquals(rotation, 0.001f)
                || !Math3DUtils.epsilonEquals(prevHeight, height, 0.001f)
                || prevCollision != collision) {
              MovableAvatarComponent mac =
                  ((MovableAvatarComponent) c.getComponent(MovableComponent.class));
              mac.localMoveRequest(
                  new CellTransform(rotation.getRotation(), translation), height, collision);

              prevTrans = translation.clone();
              prevRot = new PMatrix(rotation);
              prevHeight = height;
              prevCollision = collision;
            }
          };
        };

    // This info will be sent to the other clients to animate the avatar
    gameContextListener =
        new GameContextListener() {

          public void trigger(
              boolean pressed, int trigger, Vector3f translation, Quaternion rotation) {
            synchronized (this) {
              currentTrigger = trigger;
              currentPressed = pressed;
            }

            String animationName = null;

            // OWL issue #237 - regardless of the current state, send the
            // animation that is currently set in CycleActionState. This
            // is consistent with the behavior of
            // WlAvatarContext.setMiscAnimation() used in the trigger()
            // method below
            CycleActionState cas =
                (CycleActionState) avatarCharacter.getContext().getState(CycleActionState.class);
            if (cas != null) {
              animationName = cas.getAnimationName();
            }

            float height = avatarCharacter.getController().getHeight();
            boolean collision = avatarCharacter.getController().isColliding();

            if (c.getComponent(MovableComponent.class) == null) {
              logger.warning("!!!! NULL MovableComponent");
            } else {
              MovableAvatarComponent mac =
                  ((MovableAvatarComponent) c.getComponent(MovableComponent.class));
              mac.localMoveRequest(
                  new CellTransform(rotation, translation),
                  trigger,
                  pressed,
                  animationName,
                  height,
                  collision,
                  null);
            }
          }
        };
  }