package com.github.czyzby.kiwi.util.gdx.scene2d.range;
import com.badlogic.gdx.graphics.Color;
/** Utility object, meant to be used internally by components that change color over time. Uses simplified
* interpolation. Useful for specific cases (like custom widgets), for most actors - UI actions are usually less awkward
* to use.
*
* @author MJ */
public class ColorRange {
private static final float DEFAULT_TRANSITION_LENGTH = 0.25f;
private final Color initialColor;
private final Color currentColor;
private final Color targetColor;
private float transitionLength;
// Control variables.
private boolean transitionInProgress;
private boolean initialRedIsGreater, initialGreenIsGreater, initialBlueIsGreater, initialAlphaIsGreater;
private boolean redNeedsUpdate, greenNeedsUpdate, blueNeedsUpdate, alphaNeedsUpdate;
public ColorRange() {
this(Color.WHITE, DEFAULT_TRANSITION_LENGTH);
}
public ColorRange(final float transitionLength) {
this(Color.WHITE, transitionLength);
}
public ColorRange(final Color initialColor) {
this(initialColor, DEFAULT_TRANSITION_LENGTH);
}
public ColorRange(final Color initialColor, final float transitionLength) {
this.transitionLength = transitionLength;
this.initialColor = new Color().set(initialColor);
currentColor = new Color().set(initialColor);
targetColor = new Color().set(initialColor);
}
public void update(final float delta) {
if (transitionInProgress) {
updateRed(delta);
updateGreen(delta);
updateBlue(delta);
updateAlpha(delta);
updateTransitionStatus();
}
}
private void updateTransitionStatus() {
transitionInProgress = redNeedsUpdate || greenNeedsUpdate || blueNeedsUpdate || alphaNeedsUpdate;
}
private void updateRed(final float delta) {
if (redNeedsUpdate) {
currentColor.r += (targetColor.r - initialColor.r) * delta / transitionLength;
if (initialRedIsGreater) {
if (currentColor.r <= targetColor.r) {
finalizeRedUpdate();
}
} else if (currentColor.r >= targetColor.r) {
finalizeRedUpdate();
}
}
}
private void finalizeRedUpdate() {
currentColor.r = targetColor.r;
redNeedsUpdate = false;
}
private void updateGreen(final float delta) {
if (greenNeedsUpdate) {
currentColor.g += (targetColor.g - initialColor.g) * delta / transitionLength;
if (initialGreenIsGreater) {
if (currentColor.g <= targetColor.g) {
finalizeGreenUpdate();
}
} else if (currentColor.g >= targetColor.g) {
finalizeGreenUpdate();
}
}
}
private void finalizeGreenUpdate() {
currentColor.g = targetColor.g;
greenNeedsUpdate = false;
}
private void updateBlue(final float delta) {
if (blueNeedsUpdate) {
currentColor.b += (targetColor.b - initialColor.b) * delta / transitionLength;
if (initialBlueIsGreater) {
if (currentColor.b <= targetColor.b) {
finalizeBlueUpdate();
}
} else if (currentColor.b >= targetColor.b) {
finalizeBlueUpdate();
}
}
}
private void finalizeBlueUpdate() {
currentColor.b = targetColor.b;
blueNeedsUpdate = false;
}
private void updateAlpha(final float delta) {
if (alphaNeedsUpdate) {
currentColor.a += (targetColor.a - initialColor.a) * delta / transitionLength;
if (initialAlphaIsGreater) {
if (currentColor.a <= targetColor.a) {
finalizeAlphaUpdate();
}
} else if (currentColor.a >= targetColor.a) {
finalizeAlphaUpdate();
}
}
}
private void finalizeAlphaUpdate() {
currentColor.a = targetColor.a;
alphaNeedsUpdate = false;
}
/** @return color that matches current color at the beginning of current (or last) transition. */
public Color getInitialColor() {
return initialColor;
}
/** @return current color with values between initialColor and targetColor. */
public Color getCurrentColor() {
return currentColor;
}
/** @return color than will eventually match current color if range is constantly updated. */
public Color getTargetColor() {
return targetColor;
}
/** Sets initial color to current color, begins transition to passed targetColor. */
public void setTargetColor(final Color targetColor) {
if (!targetColor.equals(this.targetColor)) {
initialColor.set(currentColor);
this.targetColor.set(targetColor);
invalidate();
}
}
/** Reschedules color updates. Should be called upon manual color modifications. */
public void invalidate() {
if (transitionLength <= 0f) {
currentColor.set(targetColor);
redNeedsUpdate = greenNeedsUpdate = blueNeedsUpdate = alphaNeedsUpdate = false;
transitionInProgress = false;
} else {
redNeedsUpdate = initialColor.r != targetColor.r;
greenNeedsUpdate = initialColor.g != targetColor.g;
blueNeedsUpdate = initialColor.b != targetColor.b;
alphaNeedsUpdate = initialColor.a != targetColor.a;
updateTransitionStatus();
if (transitionInProgress) {
initialRedIsGreater = initialColor.r >= targetColor.r;
initialGreenIsGreater = initialColor.g >= targetColor.g;
initialBlueIsGreater = initialColor.b >= targetColor.b;
initialAlphaIsGreater = initialColor.a >= targetColor.a;
}
}
}
/** @return time that has to pass before current color reaches target color's values. */
public float getTransitionLength() {
return transitionLength;
}
/** @param transitionLength time that has to pass before current color reaches target color's values. */
public void setTransitionLength(final float transitionLength) {
this.transitionLength = transitionLength;
}
/** @return true if current color does not match target color. */
public boolean isTransitionInProgress() {
return transitionInProgress;
}
}