package minizoo.c;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.PriorityQueue;
import minizoo.c.action.Action;
import minizoo.c.core.Vector2d;
import minizoo.i.Collider;
import minizoo.i.Drawable;
import minizoo.i.Task;
@SuppressWarnings("UnusedDeclaration")
public abstract class Entity implements Comparable<Entity>, Collider, Drawable, Task {
public Entity(String name) {
this.name = name;
}
public String toString() {
return "Entity:" + name + " " + getPosition();
}
@Override
public Rectangle2D getBoundingBox() {
Vector2d origPos = getPosition();
Vector2d origAnchor = getAnchor();
setPosition(0f, 0f);
setAnchor(0f, 0f);
Rectangle2D result = getBoundingBoxInternal(null);
setPosition(origPos);
setAnchor(origAnchor);
return result;
}
private Rectangle2D getBoundingBoxInternal(AffineTransform base) {
Vector2d min = new Vector2d();
Vector2d max = new Vector2d();
AffineTransform affineTransform;
if (base == null) {
affineTransform = getTransform();
} else {
affineTransform = (AffineTransform)base.clone();
affineTransform.concatenate(getTransform());
}
Rectangle2D boxCollider = new Rectangle2D.Double(0, 0, getContentSize().x, getContentSize().y);
boxCollider = affineTransform.createTransformedShape(boxCollider).getBounds2D();
min.x = boxCollider.getMinX();
min.y = boxCollider.getMinY();
max.x = boxCollider.getMaxX();
max.y = boxCollider.getMaxY();
for (Entity child : getChildren()) {
Rectangle2D childCollider = child.getBoundingBoxInternal(affineTransform);
min.x = Math.min(childCollider.getMinX(), min.x);
min.y = Math.min(childCollider.getMinY(), min.y);
max.x = Math.max(childCollider.getMaxX(), max.x);
max.y = Math.max(childCollider.getMaxY(), max.y);
}
return new Rectangle2D.Double(min.x, min.y, max.x - min.x, max.y - min.y);
}
@Override
public boolean intersect(Point2D point, AffineTransform base) {
AffineTransform affineTransform;
if (base == null) {
affineTransform = getTransform();
} else {
affineTransform = (AffineTransform)base.clone();
affineTransform.concatenate(getTransform());
}
Rectangle2D boxCollider = new Rectangle2D.Double(0, 0, getContentSize().x, getContentSize().y);
Shape transShaped = affineTransform.createTransformedShape(boxCollider);
if (transShaped.contains(point)) {
return true;
} else {
for (Entity child : getChildren()) {
if (child.intersect(point, affineTransform)) {
return true;
}
}
}
return false;
}
@Override
public void draw (Graphics g) {
Graphics2D g2 = (Graphics2D)g;
beforeDraw(g2);
visit(g2);
afterDraw(g2);
}
protected void beforeDraw(Graphics2D g2) {
// Push Matrix
lastAffineTransform = g2.getTransform();
g2.transform(getTransform());
}
protected void visit(Graphics2D g2) {
}
protected void afterDraw(Graphics2D g2) {
drawChildren(g2);
// drawing bounding box
/*
if (this instanceof Animal) {
Vector2d origScale = getScale();
setScale(1f, 1f);
g2.setTransform(lastAffineTransform);
g2.transform(getTransform());
setScale(origScale);
g2.setColor(Color.green);
g2.draw(getBoundingBox());
g2.setTransform(lastAffineTransform);
g2.transform(getTransform());
}
*/
// Pop Matrix
g2.setTransform(lastAffineTransform);
}
protected void drawChildren(Graphics2D g2) {
for (Entity child : getChildren()) {
if (child!=null) {
child.draw(g2);
}
}
}
@Override
@SuppressWarnings("NullableProblems")
public int compareTo(Entity entity) {
return this.zOrder <= entity.zOrder ? -1 : 1;
}
public void addChild(Entity entity) {
addChild(entity, -1);
}
public void addChild(Entity entity, int zOrder) {
entity.zOrder = zOrder;
entity.parent = this;
children.add(entity);
}
public void removeChild(Entity entity) {
children.remove(entity);
entity.parent = null;
}
public PriorityQueue<Entity> getChildren() {
return children;
}
@SuppressWarnings("UnusedDeclaration")
public void setParent(Entity parent) {
if (this.parent != null) {
this.parent.removeChild(this);
}
if (parent != null) {
parent.addChild(this);
}
this.parent = parent;
}
public Entity getParent() {
return parent;
}
@Override
public void update(float elapsed) {
updatedTime += elapsed;
updateAction(elapsed);
for (Entity child : children) {
child.update(elapsed);
}
}
public void setContentSize(float sx, float sy) {
setContentSize(new Vector2d(sx, sy));
}
public void setContentSize(Vector2d contentSize) {
this.contentSize = contentSize;
isDirty = true;
}
public Vector2d getContentSize() {
return contentSize;
}
public void setPosition(float px, float py) {
setPosition(new Vector2d(px, py));
}
public void setPosition(Vector2d position) {
this.position = position;
isDirty = true;
}
public Vector2d getPosition() {
return position;
}
public void setAnchor(float x, float y) {
setAnchor(new Vector2d(x, y));
}
public void setAnchor(Vector2d anchor) {
this.anchor = anchor;
isDirty = true;
}
@SuppressWarnings("UnusedDeclaration")
public Vector2d getAnchor() {
return anchor;
}
public void setScale(float sx, float sy) {
setScale(new Vector2d(sx, sy));
}
public void setScale(Vector2d scale) {
this.scale = scale;
isDirty = true;
}
public Vector2d getScale() {
return scale;
}
public void setRotation(double rotation) {
this.rotation = rotation;
isDirty = true;
}
public double getRotation() {
return rotation;
}
@Override
public void setTint(Color tint) {
this.tint = tint;
}
@Override
public Color getTint() {
return tint;
}
public Color getTintedColor(Color color) {
if (getParent() != null) {
return getParent().getTintedColor(color);
} else {
return new Color(
(color.getRed() * getTint().getRed()) / 255,
(color.getGreen() * getTint().getGreen()) / 255,
(color.getBlue() * getTint().getBlue()) / 255,
(color.getAlpha() * getTint().getAlpha()) / 255);
}
}
public void setZOrder(int zOrder) {
this.zOrder = zOrder;
}
public int getZOrder() {
return zOrder;
}
public AffineTransform getTransform() {
if (isDirty) {
transform.setToIdentity();
// Translation
transform.translate(position.x, position.y);
// Rotation
transform.rotate(rotation);
// Scale
transform.scale(scale.x, scale.y);
// Anchor Apply
transform.translate(-contentSize.x * anchor.x, -contentSize.y * anchor.y);
}
return transform;
}
// Action
private void updateAction(float elapsed) {
class ActionControl {
public ActionControl(ArrayList<Action> actions, float time) {
this.actions = actions;
this.time = time;
}
public void removeExpiredAction(float elapsed) {
for (Iterator<Action> it = actions.iterator(); it.hasNext(); ) {
Action action = it.next();
if (action.getDuration() <= (action.getTime()+elapsed)) {
it.remove();
}
}
}
public void processAction(float elapsed) {
for (Action action : actions) {
action.update(elapsed);
}
}
ArrayList<Action> actions;
float time;
}
ActionControl control = new ActionControl(actions, updatedTime-elapsed);
control.removeExpiredAction(0);
control.processAction(elapsed);
control.removeExpiredAction(elapsed);
}
public void runAction(Action action) {
action.setTarget(this);
action.clear();
actions.add(action);
}
public void runAction(Action action, String id) {
action.setIdentifier(id);
runAction(action);
}
public void stopAction(String id) {
for (Iterator<Action> it = actions.iterator(); it.hasNext(); ) {
if (id.equals(it.next().getIdentifier())) {
it.remove();
}
}
}
public void stopAllAction() {
actions.clear();
}
ArrayList<Action> actions = new ArrayList<Action>();
// Entity property
String name;
Vector2d contentSize = new Vector2d(0, 0);
Vector2d position = new Vector2d(0, 0);
Vector2d anchor = new Vector2d(0.5, 0.5);
Vector2d scale = new Vector2d(1, 1);
double rotation = 0.0;
Color tint = Color.white;
int zOrder = -1;
AffineTransform transform = new AffineTransform();
boolean isDirty = true;
public float getUpdatedTime() {
return updatedTime;
}
float updatedTime;
AffineTransform lastAffineTransform;
// Entity hierarchy
Entity parent = null;
PriorityQueue<Entity> children = new PriorityQueue<Entity>();
public static PriorityQueue<Entity> list = new PriorityQueue<Entity>();
public static void add(Entity entity) {
Entity.add(entity, -1);
}
public static void add(Entity entity, int zOrder) {
entity.zOrder = zOrder;
Entity.list.add(entity);
}
}