Пример #1
0
  /**
   * Applies a touch up event to the stage and returns true if an actor in the scene {@link
   * Event#handle() handled} the event. Only {@link InputListener listeners} that returned true for
   * touchDown will receive this event.
   */
  public boolean touchUp(int screenX, int screenY, int pointer, int button) {
    pointerTouched[pointer] = false;
    pointerScreenX[pointer] = screenX;
    pointerScreenY[pointer] = screenY;

    if (touchFocuses.size == 0) return false;

    screenToStageCoordinates(tempCoords.set(screenX, screenY));

    InputEvent event = Pools.obtain(InputEvent.class);
    event.setType(Type.touchUp);
    event.setStage(this);
    event.setStageX(tempCoords.x);
    event.setStageY(tempCoords.y);
    event.setPointer(pointer);
    event.setButton(button);

    SnapshotArray<TouchFocus> touchFocuses = this.touchFocuses;
    TouchFocus[] focuses = touchFocuses.begin();
    for (int i = 0, n = touchFocuses.size; i < n; i++) {
      TouchFocus focus = focuses[i];
      if (focus.pointer != pointer || focus.button != button) continue;
      if (!touchFocuses.removeValue(focus, true)) continue; // Touch focus already gone.
      event.setTarget(focus.target);
      event.setListenerActor(focus.listenerActor);
      if (focus.listener.handle(event)) event.handle();
      Pools.free(focus);
    }
    touchFocuses.end();

    boolean handled = event.isHandled();
    Pools.free(event);
    return handled;
  }
Пример #2
0
  private Actor fireEnterAndExit(Actor overLast, int screenX, int screenY, int pointer) {
    // Find the actor under the point.
    screenToStageCoordinates(tempCoords.set(screenX, screenY));
    Actor over = hit(tempCoords.x, tempCoords.y, true);
    if (over == overLast) return overLast;

    // Exit overLast.
    if (overLast != null) {
      InputEvent event = Pools.obtain(InputEvent.class);
      event.setStage(this);
      event.setStageX(tempCoords.x);
      event.setStageY(tempCoords.y);
      event.setPointer(pointer);
      event.setType(InputEvent.Type.exit);
      event.setRelatedActor(over);
      overLast.fire(event);
      Pools.free(event);
    }
    // Enter over.
    if (over != null) {
      InputEvent event = Pools.obtain(InputEvent.class);
      event.setStage(this);
      event.setStageX(tempCoords.x);
      event.setStageY(tempCoords.y);
      event.setPointer(pointer);
      event.setType(InputEvent.Type.enter);
      event.setRelatedActor(overLast);
      over.fire(event);
      Pools.free(event);
    }
    return over;
  }
Пример #3
0
  /**
   * Cancels touch focus for all listeners except the specified listener.
   *
   * @see #cancelTouchFocus()
   */
  public void cancelTouchFocusExcept(EventListener exceptListener, Actor exceptActor) {
    InputEvent event = Pools.obtain(InputEvent.class);
    event.setStage(this);
    event.setType(InputEvent.Type.touchUp);
    event.setStageX(Integer.MIN_VALUE);
    event.setStageY(Integer.MIN_VALUE);

    // Cancel all current touch focuses except for the specified listener, allowing for concurrent
    // modification, and never
    // cancel the same focus twice.
    SnapshotArray<TouchFocus> touchFocuses = this.touchFocuses;
    TouchFocus[] items = touchFocuses.begin();
    for (int i = 0, n = touchFocuses.size; i < n; i++) {
      TouchFocus focus = items[i];
      if (focus.listener == exceptListener && focus.listenerActor == exceptActor) continue;
      if (!touchFocuses.removeValue(focus, true)) continue; // Touch focus already gone.
      event.setTarget(focus.target);
      event.setListenerActor(focus.listenerActor);
      event.setPointer(focus.pointer);
      event.setButton(focus.button);
      focus.listener.handle(event);
      // Cannot return TouchFocus to pool, as it may still be in use (eg if cancelTouchFocus is
      // called from touchDragged).
    }
    touchFocuses.end();

    Pools.free(event);
  }
Пример #4
0
  /**
   * Applies a touch down event to the stage and returns true if an actor in the scene {@link
   * Event#handle() handled} the event.
   */
  public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    if (screenX < viewport.getScreenX()
        || screenX >= viewport.getScreenX() + viewport.getScreenWidth()) return false;
    if (Gdx.graphics.getHeight() - screenY < viewport.getScreenY()
        || Gdx.graphics.getHeight() - screenY >= viewport.getScreenY() + viewport.getScreenHeight())
      return false;

    pointerTouched[pointer] = true;
    pointerScreenX[pointer] = screenX;
    pointerScreenY[pointer] = screenY;

    screenToStageCoordinates(tempCoords.set(screenX, screenY));

    InputEvent event = Pools.obtain(InputEvent.class);
    event.setType(Type.touchDown);
    event.setStage(this);
    event.setStageX(tempCoords.x);
    event.setStageY(tempCoords.y);
    event.setPointer(pointer);
    event.setButton(button);

    Actor target = hit(tempCoords.x, tempCoords.y, true);
    if (target == null) {
      if (root.getTouchable() == Touchable.enabled) root.fire(event);
    } else {
      target.fire(event);
    }

    boolean handled = event.isHandled();
    Pools.free(event);
    return handled;
  }
Пример #5
0
 private void freeAll(TrackEntry entry) {
   while (entry != null) {
     TrackEntry next = entry.next;
     Pools.free(entry);
     entry = next;
   }
 }
Пример #6
0
  /**
   * Applies a mouse moved event to the stage and returns true if an actor in the scene {@link
   * Event#handle() handled} the event. This event only occurs on the desktop.
   */
  public boolean mouseMoved(int screenX, int screenY) {
    if (screenX < viewport.getScreenX()
        || screenX >= viewport.getScreenX() + viewport.getScreenWidth()) return false;
    if (Gdx.graphics.getHeight() - screenY < viewport.getScreenY()
        || Gdx.graphics.getHeight() - screenY >= viewport.getScreenY() + viewport.getScreenHeight())
      return false;

    mouseScreenX = screenX;
    mouseScreenY = screenY;

    screenToStageCoordinates(tempCoords.set(screenX, screenY));

    InputEvent event = Pools.obtain(InputEvent.class);
    event.setStage(this);
    event.setType(Type.mouseMoved);
    event.setStageX(tempCoords.x);
    event.setStageY(tempCoords.y);

    Actor target = hit(tempCoords.x, tempCoords.y, true);
    if (target == null) target = root;

    target.fire(event);
    boolean handled = event.isHandled();
    Pools.free(event);
    return handled;
  }
Пример #7
0
 /**
  * Recalculates the bounding area for the given {@code entity}, which most frequently will turn
  * out this component's parent. Should be invoked each time entity's position, size or shape
  * changes.
  */
 public void update(EngineEntity entity) {
   if (area != null) {
     Pools.free(area);
   }
   area =
       rect
           ? BoundingAreaBuilder.getBoundingRectangle(entity)
           : BoundingAreaBuilder.getBoundingCircle(entity);
 }
Пример #8
0
 /**
  * @param oldText May be null.
  * @return True if the text was changed.
  */
 boolean changeText(String oldText, String newText) {
   if (newText.equals(oldText)) return false;
   text = newText;
   ChangeEvent changeEvent = Pools.obtain(ChangeEvent.class);
   boolean cancelled = fire(changeEvent);
   text = cancelled ? oldText : newText;
   Pools.free(changeEvent);
   return !cancelled;
 }
Пример #9
0
 public void layoutCards() {
   Array<Card> cards = Pools.obtain(Array.class);
   for (int i = 0; i < 4; i++) {
     cards.clear();
     for (int c : players[i].getCards()) cards.add(getCardController().getCard(c));
     screen.playDealingAnimation(i, cards);
   }
   cards.clear();
   Pools.free(cards);
 }
Пример #10
0
  private void updateValueFromTouch(float touchX, float touchY) {
    int newV = (int) (touchX / BasicColorPicker.PALETTE_SIZE * maxValue / sizes.scaleFactor);
    int newS = (int) (touchY / BasicColorPicker.PALETTE_SIZE * maxValue / sizes.scaleFactor);

    setValue(newS, newV);

    ChangeEvent changeEvent = Pools.obtain(ChangeEvent.class);
    fire(changeEvent);
    Pools.free(changeEvent);
  }
Пример #11
0
  /**
   * Calculate the bounds of the given actors as a group
   *
   * @param actors the actors
   * @param resultOrigin result origin of the bounds
   * @param resultSize result size of the bounds
   */
  public static void calculateBounds(
      Array<Actor> actors, Vector2 resultOrigin, Vector2 resultSize) {
    resultOrigin.set(0, 0);
    resultSize.set(0, 0);
    if (actors.size == 0) {
      return;
    }

    Vector2 origin = Pools.obtain(Vector2.class);
    Vector2 size = Pools.obtain(Vector2.class);
    Vector2 leftTop = Pools.obtain(Vector2.class);
    Vector2 rightBottom = Pools.obtain(Vector2.class);
    float minX = Float.POSITIVE_INFINITY;
    float minY = Float.POSITIVE_INFINITY;
    float maxX = Float.NEGATIVE_INFINITY;
    float maxY = Float.NEGATIVE_INFINITY;
    for (Actor actor : actors) {
      calculateBounds(actor, origin, size);
      size.add(origin);
      leftTop.set(origin.x, size.y);
      rightBottom.set(size.x, origin.y);
      actor.localToParentCoordinates(origin);
      actor.localToParentCoordinates(size);
      actor.localToParentCoordinates(leftTop);
      actor.localToParentCoordinates(rightBottom);

      minX =
          Math.min(minX, Math.min(origin.x, Math.min(size.x, Math.min(leftTop.x, rightBottom.x))));
      minY =
          Math.min(minY, Math.min(origin.y, Math.min(size.y, Math.min(leftTop.y, rightBottom.y))));
      maxX =
          Math.max(maxX, Math.max(origin.x, Math.max(size.x, Math.max(leftTop.x, rightBottom.x))));
      maxY =
          Math.max(maxY, Math.max(origin.y, Math.max(size.y, Math.max(leftTop.y, rightBottom.y))));
    }
    Pools.free(origin);
    Pools.free(size);
    Pools.free(leftTop);
    Pools.free(rightBottom);
    resultOrigin.set(minX, minY);
    resultSize.set(maxX - minX, maxY - minY);
  }
Пример #12
0
 /**
  * Applies a key typed event to the actor that has {@link Stage#setKeyboardFocus(Actor) keyboard
  * focus}, if any, and returns true if the event was {@link Event#handle() handled}.
  */
 public boolean keyTyped(char character) {
   Actor target = keyboardFocus == null ? root : keyboardFocus;
   InputEvent event = Pools.obtain(InputEvent.class);
   event.setStage(this);
   event.setType(InputEvent.Type.keyTyped);
   event.setCharacter(character);
   target.fire(event);
   boolean handled = event.isHandled();
   Pools.free(event);
   return handled;
 }
Пример #13
0
 /**
  * Applies a key up event to the actor that has {@link Stage#setKeyboardFocus(Actor) keyboard
  * focus}, if any, and returns true if the event was {@link Event#handle() handled}.
  */
 public boolean keyUp(int keyCode) {
   Actor target = keyboardFocus == null ? root : keyboardFocus;
   InputEvent event = Pools.obtain(InputEvent.class);
   event.setStage(this);
   event.setType(InputEvent.Type.keyUp);
   event.setKeyCode(keyCode);
   target.fire(event);
   boolean handled = event.isHandled();
   Pools.free(event);
   return handled;
 }
Пример #14
0
  public void clearTrack(int trackIndex) {
    if (trackIndex >= tracks.size) return;
    TrackEntry current = tracks.get(trackIndex);
    if (current == null) return;

    if (current.listener != null) current.listener.end(trackIndex);
    for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).end(trackIndex);

    tracks.set(trackIndex, null);
    freeAll(current);
    if (current.previous != null) Pools.free(current.previous);
  }
Пример #15
0
  /** Adjusts the position and size of the given group to its children */
  public static void adjustGroup(Actor root) {
    if (!(root instanceof Group)) {
      return;
    }

    Group group = (Group) root;
    if (group.getChildren().size == 0) {
      return;
    }

    Vector2 origin = Pools.obtain(Vector2.class);
    Vector2 size = Pools.obtain(Vector2.class);
    Vector2 tmp3 = Pools.obtain(Vector2.class);
    Vector2 tmp4 = Pools.obtain(Vector2.class);

    calculateBounds(group.getChildren(), origin, size);

    /*
     * minX and minY are the new origin (new 0, 0), so everything inside the
     * group must be translated that much.
     */
    for (Actor actor : group.getChildren()) {
      actor.setPosition(actor.getX() - origin.x, actor.getY() - origin.y);
    }

    /*
     * Now, we calculate the current origin (0, 0) and the new origin (minX,
     * minY), and group is translated by that difference.
     */
    group.localToParentCoordinates(tmp3.set(0, 0));
    group.localToParentCoordinates(tmp4.set(origin.x, origin.y));
    tmp4.sub(tmp3);
    group.setBounds(group.getX() + tmp4.x, group.getY() + tmp4.y, size.x, size.y);
    group.setOrigin(size.x / 2.0f, size.y / 2.0f);

    Pools.free(origin);
    Pools.free(size);
    Pools.free(tmp3);
    Pools.free(tmp4);
  }
Пример #16
0
  private void dealCards(int round) {
    Array<Integer> cards = Pools.obtain(Array.class);
    for (int i = 0; i < 4; i++) {
      cards.clear();
      getDealer().deal(Math.min(round, 13), cards);
      players[i].receiveCards(cards);
    }
    cards.clear();
    Pools.free(cards);
    layoutCards();

    getSounds().playDealSound();
  }
Пример #17
0
  /**
   * For a given actor computes and applies the transformation to keep the same screen
   * transformation in a new group
   *
   * @param actor
   * @param parent
   */
  public static void computeTransformFor(Actor actor, Group parent) {
    Vector2 tmp1 = Pools.obtain(Vector2.class);
    Vector2 tmp2 = Pools.obtain(Vector2.class);
    Vector2 tmp3 = Pools.obtain(Vector2.class);
    Vector2 tmp4 = Pools.obtain(Vector2.class);
    Vector2 tmp5 = Pools.obtain(Vector2.class);

    calculateBounds(actor, tmp4, tmp5);
    Vector2 o = tmp1.set(tmp4.x, tmp4.y);
    Vector2 t = tmp2.set(tmp4.x + tmp5.x, tmp4.y);
    Vector2 n = tmp3.set(tmp4.x, tmp4.y + tmp5.y);
    actor.localToAscendantCoordinates(parent, o);
    actor.localToAscendantCoordinates(parent, t);
    actor.localToAscendantCoordinates(parent, n);
    actor.setRotation(actor.getRotation() + actor.getParent().getRotation());
    applyTransformation(actor, o, t, n);

    Pools.free(tmp1);
    Pools.free(tmp2);
    Pools.free(tmp3);
    Pools.free(tmp4);
    Pools.free(tmp5);
  }
Пример #18
0
  private void setCurrent(int index, TrackEntry entry) {
    TrackEntry current = expandToIndex(index);
    if (current != null) {
      if (current.previous != null) {
        Pools.free(current.previous);
        current.previous = null;
      }

      if (current.listener != null) current.listener.end(index);
      for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).end(index);

      entry.mixDuration = data.getMix(current.animation, entry.animation);
      if (entry.mixDuration > 0) {
        entry.mixTime = 0;
        entry.previous = current;
      } else Pools.free(current);
    }

    tracks.set(index, entry);

    if (entry.listener != null) entry.listener.start(index);
    for (int i = 0, n = listeners.size; i < n; i++) listeners.get(i).start(index);
  }
Пример #19
0
 /**
  * Removes the listener from being notified for all touchDragged and touchUp events for the
  * specified pointer and button. Note the listener may never receive a touchUp event if this
  * method is used.
  */
 public void removeTouchFocus(
     EventListener listener, Actor listenerActor, Actor target, int pointer, int button) {
   SnapshotArray<TouchFocus> touchFocuses = this.touchFocuses;
   for (int i = touchFocuses.size - 1; i >= 0; i--) {
     TouchFocus focus = touchFocuses.get(i);
     if (focus.listener == listener
         && focus.listenerActor == listenerActor
         && focus.target == target
         && focus.pointer == pointer
         && focus.button == button) {
       touchFocuses.removeIndex(i);
       Pools.free(focus);
     }
   }
 }
Пример #20
0
  /**
   * Applies a mouse scroll event to the stage and returns true if an actor in the scene {@link
   * Event#handle() handled} the event. This event only occurs on the desktop.
   */
  public boolean scrolled(int amount) {
    Actor target = scrollFocus == null ? root : scrollFocus;

    screenToStageCoordinates(tempCoords.set(mouseScreenX, mouseScreenY));

    InputEvent event = Pools.obtain(InputEvent.class);
    event.setStage(this);
    event.setType(InputEvent.Type.scrolled);
    event.setScrollAmount(amount);
    event.setStageX(tempCoords.x);
    event.setStageY(tempCoords.y);
    target.fire(event);
    boolean handled = event.isHandled();
    Pools.free(event);
    return handled;
  }
Пример #21
0
 public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
   if (pointer == 0 && button != 0) return false;
   stageToLocalCoordinates(Vector2.tmp);
   x = Vector2.tmp.x;
   y = Vector2.tmp.y;
   if (x > 0 && x < getWidth() && y > 0 && y < getHeight()) {
     listSelectedIndex = (int) ((getHeight() - y) / itemHeight);
     listSelectedIndex = Math.max(0, listSelectedIndex);
     listSelectedIndex = Math.min(items.length - 1, listSelectedIndex);
     selectedIndex = listSelectedIndex;
     if (items.length > 0) {
       ChangeEvent changeEvent = Pools.obtain(ChangeEvent.class);
       SelectBox.this.fire(changeEvent);
       Pools.free(changeEvent);
     }
   }
   return true;
 }
Пример #22
0
  /**
   * Sets this actor as the event {@link Event#setTarget(Actor) target} and propagates the event to
   * this actor and ancestor actors as necessary. If this actor is not in the stage, the stage must
   * be set before calling this method.
   *
   * <p>Events are fired in 2 phases. The first phase notifies listeners on each actor starting at
   * the root and propagating downward to (and including) this actor. The second phase notifes
   * listeners on each actor starting at this actor and, if {@link Event#getBubbles()} is true,
   * propagating upward to the root. If the event is {@link Event#stop() stopped} at any time, it
   * will not propagate to the next actor.
   *
   * @return true of the event was {@link Event#cancel() cancelled}.
   */
  public boolean fire(Event event) {
    if (event.getStage() == null) event.setStage(getStage());
    event.setTarget(this);

    // Collect ancestors so event propagation is unaffected by hierarchy changes.
    Array<Group> ancestors = Pools.obtain(Array.class);
    Group parent = getParent();
    while (parent != null) {
      ancestors.add(parent);
      parent = parent.getParent();
    }

    try {
      // Notify all parent capture listeners, starting at the root. Ancestors may stop an event
      // before children receive it.
      for (int i = ancestors.size - 1; i >= 0; i--) {
        Group currentTarget = ancestors.get(i);
        currentTarget.notify(event, true);
        if (event.isStopped()) return event.isCancelled();
      }

      // Notify the target capture listeners.
      notify(event, true);
      if (event.isStopped()) return event.isCancelled();

      // Notify the target listeners.
      notify(event, false);
      if (!event.getBubbles()) return event.isCancelled();
      if (event.isStopped()) return event.isCancelled();

      // Notify all parent listeners, starting at the target. Children may stop an event before
      // ancestors receive it.
      for (int i = 0, n = ancestors.size; i < n; i++) {
        ancestors.get(i).notify(event, false);
        if (event.isStopped()) return event.isCancelled();
      }

      return event.isCancelled();
    } finally {
      ancestors.clear();
      Pools.free(ancestors);
    }
  }
Пример #23
0
  public void apply(Skeleton skeleton) {
    Array<Event> events = this.events;
    int listenerCount = listeners.size;

    for (int i = 0; i < tracks.size; i++) {
      TrackEntry current = tracks.get(i);
      if (current == null) continue;

      events.size = 0;

      float time = current.time;
      boolean loop = current.loop;
      if (!loop && time > current.endTime) time = current.endTime;

      TrackEntry previous = current.previous;
      if (previous == null) current.animation.apply(skeleton, current.lastTime, time, loop, events);
      else {
        float previousTime = previous.time;
        if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
        previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null);

        float alpha = current.mixTime / current.mixDuration;
        if (alpha >= 1) {
          alpha = 1;
          Pools.free(previous);
          current.previous = null;
        }
        current.animation.mix(skeleton, current.lastTime, time, loop, events, alpha);
      }

      for (int ii = 0, nn = events.size; ii < nn; ii++) {
        Event event = events.get(ii);
        if (current.listener != null) current.listener.event(i, event);
        for (int iii = 0; iii < listenerCount; iii++) listeners.get(iii).event(i, event);
      }

      current.lastTime = current.time;
    }
  }
Пример #24
0
  /**
   * Calls the {@link Actor#act(float)} method on each actor in the stage. Typically called each
   * frame. This method also fires enter and exit events.
   *
   * @param delta Time in seconds since the last frame.
   */
  public void act(float delta) {
    // Update over actors. Done in act() because actors may change position, which can fire
    // enter/exit without an input event.
    for (int pointer = 0, n = pointerOverActors.length; pointer < n; pointer++) {
      Actor overLast = pointerOverActors[pointer];
      // Check if pointer is gone.
      if (!pointerTouched[pointer]) {
        if (overLast != null) {
          pointerOverActors[pointer] = null;
          screenToStageCoordinates(
              tempCoords.set(pointerScreenX[pointer], pointerScreenY[pointer]));
          // Exit over last.
          InputEvent event = Pools.obtain(InputEvent.class);
          event.setType(InputEvent.Type.exit);
          event.setStage(this);
          event.setStageX(tempCoords.x);
          event.setStageY(tempCoords.y);
          event.setRelatedActor(overLast);
          event.setPointer(pointer);
          overLast.fire(event);
          Pools.free(event);
        }
        continue;
      }
      // Update over actor for the pointer.
      pointerOverActors[pointer] =
          fireEnterAndExit(overLast, pointerScreenX[pointer], pointerScreenY[pointer], pointer);
    }
    // Update over actor for the mouse on the desktop.
    ApplicationType type = Gdx.app.getType();
    if (type == ApplicationType.Desktop
        || type == ApplicationType.Applet
        || type == ApplicationType.WebGL)
      mouseOverActor = fireEnterAndExit(mouseOverActor, mouseScreenX, mouseScreenY, -1);

    // Run actions and determine whether to request rendering (for when setContinuousRendering is
    // off)
    root.act(delta);
  }
Пример #25
0
 /**
  * Sets the actor that will receive scroll events.
  *
  * @param actor May be null.
  */
 public void setScrollFocus(Actor actor) {
   if (scrollFocus == actor) return;
   FocusEvent event = Pools.obtain(FocusEvent.class);
   event.setStage(this);
   event.setType(FocusEvent.Type.scroll);
   Actor oldScrollFocus = scrollFocus;
   if (oldScrollFocus != null) {
     event.setFocused(false);
     event.setRelatedActor(actor);
     oldScrollFocus.fire(event);
   }
   if (!event.isCancelled()) {
     scrollFocus = actor;
     if (actor != null) {
       event.setFocused(true);
       event.setRelatedActor(oldScrollFocus);
       actor.fire(event);
       if (event.isCancelled()) setScrollFocus(oldScrollFocus);
     }
   }
   Pools.free(event);
 }
 public void free() {
   Pools.free(position);
   position = null;
   object = null;
   PlayerContactPool.this.free(this);
 }
Пример #27
0
  /** Sets position, rotation, scale and origin in actor to meet the 3 given points */
  public static void applyTransformation(
      Actor actor, Vector2 origin, Vector2 tangent, Vector2 normal) {
    /*
     * We are going to calculate the affine transformation for the actor to
     * fit the bounds represented by the handles. The affine transformation
     * is defined as follows:
     */
    // |a b tx|
    // |c d ty|=|Translation Matrix| x |Scale Matrix| x |Rotation
    // Matrix|
    // |0 0 1 |
    /*
     * More info about affine transformations:
     * https://people.gnome.org/~mathieu
     * /libart/libart-affine-transformation-matrices.html, To obtain the
     * matrix, we want to resolve the following equation system:
     */
    // | a b tx| |0| |o.x|
    // | c d ty|*|0|=|o.y|
    // | 0 0 1 | |1| | 1 |
    //
    // | a b tx| |w| |t.x|
    // | c d ty|*|0|=|t.y|
    // | 0 0 1 | |1| | 1 |
    //
    // | a b tx| |0| |n.x|
    // | c d ty|*|h|=|n.y|
    // | 0 0 1 | |1| | 1 |
    /*
     * where o is handles[0] (origin), t is handles[2] (tangent) and n is
     * handles[6] (normal), w is actor.getWidth() and h is
     * actor.getHeight().
     *
     * This matrix defines that the 3 points defining actor bounds are
     * transformed to the 3 points defining modifier bounds. E.g., we want
     * that actor origin (0,0) is transformed to (handles[0].x,
     * handles[0].y), and that is expressed in the first equation.
     *
     * Resolving these equations is obtained:
     */
    // a = (t.x - o.y) / w
    // b = (t.y - o.y) / w
    // c = (n.x - o.x) / h
    // d = (n.y - o.y) / h
    /*
     * Values for translation, scale and rotation contained by the matrix
     * can be obtained directly making operations over a, b, c and d:
     */
    // tx = o.x
    // ty = o.y
    // sx = sqrt(a^2+b^2)
    // sy = sqrt(c^2+d^2)
    // rotation = atan(c/d)
    // or
    // rotation = atan(-b/a)
    /*
     * Rotation can give two different values (this happens when there is
     * more than one way of obtaining the same transformation). To avoid
     * that, we ignore the rotation to obtain the final values.
     */

    Vector2 tmp1 = Pools.obtain(Vector2.class);
    Vector2 tmp2 = Pools.obtain(Vector2.class);
    Vector2 tmp3 = Pools.obtain(Vector2.class);
    Vector2 tmp4 = Pools.obtain(Vector2.class);
    Vector2 tmp5 = Pools.obtain(Vector2.class);

    Vector2 o = tmp1.set(origin.x, origin.y);
    Vector2 t = tmp2.set(tangent.x, tangent.y);
    Vector2 n = tmp3.set(normal.x, normal.y);

    Vector2 vt = tmp4.set(t).sub(o);
    Vector2 vn = tmp5.set(n).sub(o);

    // Ignore rotation
    float rotation = actor.getRotation();
    vt.rotate(-rotation);
    vn.rotate(-rotation);

    t.set(vt).add(o);
    n.set(vn).add(o);

    Vector2 bottomLeft = Pools.obtain(Vector2.class);
    Vector2 size = Pools.obtain(Vector2.class);

    calculateBounds(actor, bottomLeft, size);

    float a = (t.x - o.x) / size.x;
    float c = (t.y - o.y) / size.x;
    float b = (n.x - o.x) / size.y;
    float d = (n.y - o.y) / size.y;

    Pools.free(tmp1);
    Pools.free(tmp2);
    Pools.free(tmp3);
    Pools.free(tmp4);
    Pools.free(tmp5);
    Pools.free(bottomLeft);
    Pools.free(size);

    // Math.sqrt gives a positive value, but it also have a negatives.
    // The
    // signum is calculated computing the current rotation
    float signumX = vt.angle() > 90.0f && vt.angle() < 270.0f ? -1.0f : 1.0f;
    float signumY = vn.angle() > 180.0f ? -1.0f : 1.0f;

    float scaleX = (float) Math.sqrt(a * a + b * b) * signumX;
    float scaleY = (float) Math.sqrt(c * c + d * d) * signumY;

    actor.setScale(scaleX, scaleY);

    /*
     * To obtain the correct translation value we need to subtract the
     * amount of translation due to the origin.
     */
    tmpMatrix.setToTranslation(actor.getOriginX(), actor.getOriginY());
    tmpMatrix.rotate(actor.getRotation());
    tmpMatrix.scale(actor.getScaleX(), actor.getScaleY());
    tmpMatrix.translate(-actor.getOriginX(), -actor.getOriginY());

    /*
     * Now, the matrix has how much translation is due to the origin
     * involved in the rotation and scaling operations
     */
    float x = o.x - tmpMatrix.getValues()[Matrix3.M02];
    float y = o.y - tmpMatrix.getValues()[Matrix3.M12];
    actor.setPosition(x, y);
  }
Пример #28
0
 protected void sendEvent(Event _event) {
   handleEvent(_event);
   _event.setManager(null);
   Pools.free(_event);
 }
Пример #29
0
 /** Fires that this {@link #focusActor} has gained focus */
 private void fireFocus() {
   FocusEvent dropEvent = Pools.obtain(FocusEvent.class);
   dropEvent.setActor(focusActor);
   focusActor.fire(dropEvent);
   Pools.free(dropEvent);
 }
Пример #30
0
 /**
  * Calculates the {@code float} distance to another bounding area.
  *
  * @param anotherArea Another bounding area
  * @param fromBorder If true, distance is calculated between the borders, otherwise they are
  *     calculated between their centers.
  * @return A positive or zero float representing the distance between this bounding area and the
  *     given one, or a negative float if {@code fromBorder} is true and one area contains the
  *     other.
  */
 public float distanceTo(BoundingAreaComponent anotherArea, boolean fromBorder) {
   Vector2 intersection = Pools.obtain(Vector2.class);
   float d = distanceTo(anotherArea, intersection, fromBorder);
   Pools.free(intersection);
   return d;
 }