package org.archstudio.bna.utils;
import static org.archstudio.sysutils.SystemUtils.simpleName;
import java.util.List;
import org.archstudio.bna.CoordinateMapperEvent;
import org.archstudio.bna.CoordinateMapperEvent.EventType;
import org.archstudio.bna.ICoordinateMapperListener;
import org.archstudio.bna.IMutableCoordinateMapper;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import com.google.common.collect.Lists;
public abstract class AbstractCoordinateMapper implements IMutableCoordinateMapper, Cloneable {
public static final double MIN_SCALE = Math.pow(Math.sqrt(2), -10);
public static final double MAX_SCALE = Math.pow(Math.sqrt(2), 10);
public static final Rectangle getDefaultBounds() {
return new Rectangle(-100000, -100000, 200000, 200000);
}
protected Rectangle worldBounds = getDefaultBounds();
protected double localScale = 1.0d;
protected Point localOrigin = new Point(worldBounds.x + worldBounds.width / 2, worldBounds.y + worldBounds.height
/ 2);
protected List<ICoordinateMapperListener> listeners = Lists.newCopyOnWriteArrayList();
public AbstractCoordinateMapper() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
AbstractCoordinateMapper t = (AbstractCoordinateMapper) super.clone();
t.worldBounds = new Rectangle(t.worldBounds.x, t.worldBounds.y, t.worldBounds.width, t.worldBounds.height);
t.localOrigin = new Point(t.localOrigin.x, t.localOrigin.y);
t.listeners = Lists.newCopyOnWriteArrayList();
return t;
}
@Override
public void insertCoordinateMapperListener(ICoordinateMapperListener l) {
listeners.add(0, l);
}
@Override
public void addCoordinateMapperListener(ICoordinateMapperListener l) {
listeners.add(l);
}
@Override
public void removeCoordinateMapperListener(ICoordinateMapperListener l) {
listeners.remove(l);
}
protected void fireCoordinateMapperEvent(EventType eventtType) {
fireCoordinateMapperEvent(new CoordinateMapperEvent(this, eventtType, worldBounds, getLocalOrigin(),
getLocalScale()));
}
protected void fireCoordinateMapperEvent(CoordinateMapperEvent evt) {
for (ICoordinateMapperListener l : listeners) {
l.coordinateMappingsChanged(evt);
}
}
@Override
public Rectangle getWorldBounds() {
return BNAUtils.clone(worldBounds);
}
@Override
public void setWorldBounds(Rectangle worldBounds) {
if (!this.worldBounds.equals(worldBounds)) {
this.worldBounds = BNAUtils.clone(worldBounds);
fireCoordinateMapperEvent(EventType.WORLD_BOUNDS);
}
}
@Override
public Point getLocalOrigin() {
return BNAUtils.clone(localOrigin);
}
@Override
public void setLocalOrigin(Point localOrigin) {
if (!this.localOrigin.equals(localOrigin)) {
this.localOrigin = BNAUtils.clone(localOrigin);
fireCoordinateMapperEvent(EventType.LOCAL_ORIGIN);
}
}
@Override
public double getLocalScale() {
return localScale;
}
@Override
public void setLocalScale(double localScale) {
//localScale = SystemUtils.bound(MIN_SCALE, localScale, MAX_SCALE);
if (Math.abs(this.localScale - localScale) > Double.MIN_VALUE) {
this.localScale = localScale;
fireCoordinateMapperEvent(EventType.LOCAL_SCALE);
}
}
@Override
public void align(Point localPoint, Point worldPoint) {
Point oldLocalPoint = worldToLocal(new Point(worldPoint.x, worldPoint.y));
Point localDelta = new Point(localPoint.x - oldLocalPoint.x, localPoint.y - oldLocalPoint.y);
if (localDelta.x != 0 || localDelta.y != 0) {
this.localOrigin.x -= localDelta.x;
this.localOrigin.y -= localDelta.y;
fireCoordinateMapperEvent(EventType.LOCAL_ORIGIN);
}
}
@Override
public void setLocalScaleAndAlign(double localScale, Point localPoint, Point worldPoint) {
//localScale = SystemUtils.bound(MIN_SCALE, localScale, MAX_SCALE);
if (Math.abs(this.localScale - localScale) > Double.MIN_VALUE) {
this.localScale = localScale;
Point oldLocalPoint = worldToLocal(new Point(worldPoint.x, worldPoint.y));
Point localDelta = new Point(localPoint.x - oldLocalPoint.x, localPoint.y - oldLocalPoint.y);
if (localDelta.x != 0 || localDelta.y != 0) {
this.localOrigin.x -= localDelta.x;
this.localOrigin.y -= localDelta.y;
fireCoordinateMapperEvent(EventType.LOCAL_SCALE_AND_ORIGIN);
}
else {
fireCoordinateMapperEvent(EventType.LOCAL_SCALE);
}
}
else {
align(localPoint, worldPoint);
}
}
@Override
public String toString() {
return simpleName(this.getClass()) + "["//
+ "worldBounds=" + worldBounds + ","//
+ "localScale=" + localScale + ","//
+ "localOrigin=" + localOrigin + "]";
}
@Override
public Rectangle localToWorld(Rectangle localRectangle) {
Point topLeft = localToWorld(new Point(localRectangle.x, localRectangle.y));
Point bottomRight = localToWorld(new Point(//
localRectangle.x + localRectangle.width, //
localRectangle.y + localRectangle.height));
return new Rectangle(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
}
@Override
public Rectangle worldToLocal(Rectangle worldRectangle) {
Point topLeft = worldToLocal(new Point(worldRectangle.x, worldRectangle.y));
Point bottomRight = worldToLocal(new Point(//
worldRectangle.x + worldRectangle.width, //
worldRectangle.y + worldRectangle.height));
return new Rectangle(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
}
}