AnimationState(final State startState, final long milliseconds, boolean isForwardAndReverse) {
      assert startState != null && milliseconds > 0;
      assert SwingUtilities.isEventDispatchThread();

      this.startState = startState;
      this.duration = milliseconds * 1000000;
      this.startTime = System.nanoTime();
      this.isForwardAndReverse = isForwardAndReverse;
      progress = 0f;
    }
    private void updateProgress() {
      assert SwingUtilities.isEventDispatchThread();

      if (isDone()) {
        return;
      }
      long currentTime = System.nanoTime();

      progress = ((float) (currentTime - startTime)) / duration;
      progress = Math.max(progress, 0); // in case time was reset
      if (progress >= 1) {
        progress = 1;
        if (isForwardAndReverse) {
          startTime = currentTime;
          progress = 0;
          isForward = !isForward;
        }
      }
    }
    void paintSkin(Skin skin, Graphics _g, int dx, int dy, int dw, int dh, State state) {
      assert SwingUtilities.isEventDispatchThread();

      updateProgress();
      if (!isDone()) {
        Graphics2D g = (Graphics2D) _g.create();
        skin.paintSkinRaw(g, dx, dy, dw, dh, startState);
        float alpha;
        if (isForward) {
          alpha = progress;
        } else {
          alpha = 1 - progress;
        }
        g.setComposite(AlphaComposite.SrcOver.derive(alpha));
        skin.paintSkinRaw(g, dx, dy, dw, dh, state);
        g.dispose();
      } else {
        skin.paintSkinRaw(_g, dx, dy, dw, dh, state);
      }
    }
    boolean isDone() {
      assert SwingUtilities.isEventDispatchThread();

      return progress >= 1;
    }