package loon.action;
import loon.geom.Vector2f;
import loon.utils.Easing.EasingMode;
import loon.utils.MathUtils;
import loon.utils.timer.EaseTimer;
/**
* 让指定对象环绕运动(与MoveRoundTo差异在于此类可以控制环绕的width和height范围大小,也就是走不规则椭圆),
* 需要偏移或矫正显示位置时请设置setOffset参数
*/
public class MoveOvalTo extends ActionEvent {
private final float angle;
private final float startAngle;
private final float width;
private final float height;
private final float rotateScale;
private float currentPosX;
private final float per;
private Vector2f startPoint, oldStartPoint;
private Vector2f centerPoint, oldCenterPoint;
private int directionX = 1;
private int directionY = -1;
private EaseTimer easeTimer;
public MoveOvalTo(float startAngle, float angle, float width, float height,
Vector2f centerPoint, float duration) {
this(startAngle, angle, width, height, centerPoint, null, duration,
1f / 60f, EasingMode.Linear);
}
public MoveOvalTo(float startAngle, float angle, float width, float height,
Vector2f centerPoint, float duration, EasingMode easing) {
this(startAngle, angle, width, height, centerPoint, null, duration,
1f / 60f, easing);
}
public MoveOvalTo(float startAngle, float angle, float width, float height,
Vector2f centerPoint, float duration, float delay, EasingMode easing) {
this(startAngle, angle, width, height, centerPoint, null, duration,
delay, easing);
}
public MoveOvalTo(float startAngle, float angle, float width, float height,
Vector2f centerPoint, Vector2f startPoint, float duration,
float delay, EasingMode easing) {
if (angle > 360) {
angle = 360;
}
this.angle = angle;
this.width = width;
this.startAngle = (startAngle % 360f);
if (startPoint == null) {
startPoint = new Vector2f();
}
initDirection();
this.height = height;
this.rotateScale = (MathUtils.abs(angle) / 90f);
this.per = (this.rotateScale / duration);
float radian = MathUtils.toRadians(this.startAngle);
float oneDivrr = MathUtils.pow(MathUtils.cos(radian) / width, 2f)
+ MathUtils.pow(MathUtils.sin(radian) / height, 2f);
float radius = MathUtils.sqrt(1f / oneDivrr);
float startY = this.startAngle == 0.0F ? height
: (float) (radius * MathUtils.sin(radian));
this.centerPoint = centerPoint;
this.centerPoint.y += (this.directionY == 1 ? -startY : startY);
this.startPoint = startPoint;
this.easeTimer = new EaseTimer(duration, delay, easing);
this.oldStartPoint = startPoint;
this.oldCenterPoint = centerPoint;
}
private void initDirection() {
int dx;
int dy;
if (this.startAngle > 90f) {
dx = 1;
dy = 1;
} else {
if (this.startAngle > 180f) {
dx = -1;
dy = 1;
} else {
if (this.startAngle > 270f) {
dx = -1;
dy = -1;
} else {
dx = 1;
dy = -1;
}
}
}
this.directionX = dx;
this.directionY = dy;
}
@Override
public void update(long elapsedTime) {
easeTimer.update(elapsedTime);
if (easeTimer.isCompleted()) {
_isCompleted = true;
float radian = MathUtils
.toRadians((this.startAngle + this.angle - 90f) % 360f);
float oneDivrr = MathUtils.pow(MathUtils.cos(radian) / this.width,
2f)
+ MathUtils.pow(MathUtils.sin(radian) / this.height, 2f);
float radius = MathUtils.sqrt(1f / oneDivrr);
float x = this.centerPoint.x + (radius * MathUtils.cos(radian));
float y = this.centerPoint.y + (radius * MathUtils.sin(radian));
this.original.setLocation(x + offsetX, y + offsetY);
return;
}
this.currentPosX = ((float) (this.per * easeTimer.getProgress()));
if (this.currentPosX > 1f) {
initDirection();
int cnt = (int) (this.startAngle / 90f);
while (this.currentPosX > 1f) {
this.currentPosX -= 1f;
cnt++;
if (cnt >= 4) {
cnt = 0;
}
int backupX = this.directionX;
if ((cnt == 0) || (cnt == 2))
this.directionX = (-this.directionX);
else {
this.directionY = (-this.directionY);
}
if ((backupX != this.directionX) && (this.currentPosX <= 1f)) {
if (this.directionX == 1)
this.currentPosX = (1f - this.currentPosX);
} else if (this.currentPosX <= 1f)
this.currentPosX = (1f - this.currentPosX);
}
}
float addX = (this.angle > 0f ? this.currentPosX : -this.currentPosX)
* this.width;
this.startPoint.x = (this.centerPoint.x + addX * this.directionX);
float addY = MathUtils.sqrt(this.height * this.height - this.height
* this.height * addX * addX / (this.width * this.width));
this.startPoint.y = (this.centerPoint.y + addY * this.directionY);
this.original.setLocation(this.startPoint.x + offsetX,
this.startPoint.y + offsetY);
}
@Override
public void onLoad() {
if (startPoint == null || startPoint.getX() == -1
|| startPoint.getY() == -1) {
this.startPoint = new Vector2f(original.getX(), original.getY());
}
this.oldStartPoint.set(startPoint);
this.oldCenterPoint.set(centerPoint);
}
@Override
public boolean isComplete() {
return _isCompleted;
}
@Override
public ActionEvent cpy() {
MoveOvalTo moveoval = new MoveOvalTo(startAngle, angle, width, height,
oldCenterPoint, oldStartPoint, easeTimer.getDuration(),
easeTimer.getDelay(), easeTimer.getEasingMode());
moveoval.set(this);
return moveoval;
}
@Override
public ActionEvent reverse() {
return cpy();
}
@Override
public String getName() {
return "moveoval";
}
}