package org.openstreetmap.josm.plugins.piclayer.transform;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.openstreetmap.josm.data.coor.EastNorth;
public class PictureTransform {
private AffineTransform cachedTransform;
private EastNorth imagePosition;
public EastNorth getImagePosition() {
return imagePosition;
}
public void setImagePosition(EastNorth imagePosition) {
this.imagePosition = imagePosition;
}
private boolean modified = false;
private List<Point2D> originPoints;
public PictureTransform() {
cachedTransform = new AffineTransform();
originPoints = new ArrayList<>(3);
}
public AffineTransform getTransform() {
return cachedTransform;
}
private AffineTransform solveEquation(List<Point2D> desiredPoints) throws NoSolutionException {
Matrix3D X = new Matrix3D(originPoints);
Matrix3D Y = new Matrix3D(desiredPoints);
Matrix3D result = Y.multiply(X.inverse());
return result.toAffineTransform();
}
public void addOriginPoint(Point2D originPoint) {
if (originPoints.size() < 3)
originPoints.add(originPoint);
}
public void resetCalibration() {
originPoints.clear();
modified = false;
cachedTransform = new AffineTransform();
}
/**
* updates pair of points (suppose that other pairs are (origin=>origin) points are the same),
* solves equation,
* applies transform matrix to the existing cachedTransform
*
* @param originPoint - should be one of origin points, otherwise - no transform applied
* @param desiredPoint - new place for the point
*/
public void updatePair(Point2D originPoint, Point2D desiredPoint) {
if (originPoint == null)
return;
switch (originPoints.size()) {
case 1: {
cachedTransform.concatenate(AffineTransform.getTranslateInstance(desiredPoint.getX()-originPoint.getX(),
desiredPoint.getY()-originPoint.getY()));
break;
}
case 2: {
// find triangle and move it
List<Point2D> desiredPoints = new ArrayList<>(3);
Point2D o1 = originPoints.get(0);
Point2D o2 = originPoints.get(1);
Point2D d1, d2;
if (o2 == originPoint) {
d2 = desiredPoint;
d1 = (Point2D) o1.clone();
} else {
d1 = desiredPoint;
d2 = (Point2D) o2.clone();
}
Point2D o3 = calculateTrianglePoint(o1, o2);
Point2D d3 = calculateTrianglePoint(d1, d2);
originPoints.add(o3);
desiredPoints.add(d1); desiredPoints.add(d2); desiredPoints.add(d3);
trySolve(desiredPoints);
originPoints.remove(2);
break;
}
case 3: {
List<Point2D> desiredPoints = new ArrayList<>(3);
for (Point2D origin : originPoints) {
if (origin.equals(originPoint))
desiredPoints.add(desiredPoint);
else
desiredPoints.add(origin);
}
trySolve(desiredPoints);
break;
}
default:
}
}
private Point2D calculateTrianglePoint(Point2D d1, Point2D d2) {
Point2D result;
if (d1 instanceof Point2D.Double) {
result = new Point2D.Double();
} else {
result = new Point2D.Float();
}
result.setLocation((d1.getX()+d2.getX()-d2.getY()+d1.getY())/2, (d1.getY()+d2.getY()+d2.getX()-d1.getX())/2);
return result;
}
private void trySolve(List<Point2D> desiredPoints) {
if (desiredPoints.size() == 3 && originPoints.size() == 3) {
try {
cachedTransform.concatenate(solveEquation(desiredPoints));
modified = true;
desiredPoints.clear();
} catch (NoSolutionException e) {
System.err.println(e.getMessage());
}
}
}
public void replaceOriginPoint(Point2D originPoint, Point2D newOriginPoint) {
if (originPoint == null || newOriginPoint == null)
return;
int index = originPoints.indexOf(originPoint);
if (index < 0)
return;
originPoints.set(index, newOriginPoint);
}
public void concatenateTransformPoint(AffineTransform transform, Point2D trans) {
if (trans != null) {
AffineTransform centered = AffineTransform.getTranslateInstance(trans.getX(), trans.getY());
centered.concatenate(transform);
centered.translate(-trans.getX(), -trans.getY());
cachedTransform.concatenate(centered);
} else {
cachedTransform.concatenate(transform);
}
for (int i = 0; i < originPoints.size(); i++) {
Point2D point = originPoints.get(i);
transform.transform(point, point);
}
modified = true;
}
public boolean isModified() {
return modified;
}
public void setModified() {
modified = true;
}
public void resetModified() {
modified = false;
}
public void setTransform(AffineTransform newTransform) {
cachedTransform = new AffineTransform(newTransform);
}
public List<Point2D> getOriginPoints() {
return originPoints;
}
public void setOriginPoints(List<Point2D> list) {
this.originPoints = new ArrayList<>(list);
}
public void removeOriginPoint(Point2D selectedPoint) {
originPoints.remove(selectedPoint);
}
}