package es.iiia.shapegrammar.utils;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;
import org.eclipse.core.runtime.IProgressMonitor;
import Jama.Matrix;
import es.iiia.shapegrammar.execution.ExecutionReport;
import es.iiia.shapegrammar.model.ShapeGrammarModel;
import es.iiia.shapegrammar.shape.CarrierModel;
import es.iiia.shapegrammar.shape.IntersectionModel;
import es.iiia.shapegrammar.shape.LineModel;
import es.iiia.shapegrammar.shape.MarkerModel;
import es.iiia.shapegrammar.shape.ShapeModel;
import es.iiia.shapegrammar.shape.guides.IntersectionTriplet;
import es.iiia.shapegrammar.shape.guides.LineDefinition;
/**
* Created by Tomas Trescak, @ IIIA, UAB Barcelona.
* Date: Oct 22, 2008
* Time: 1:59:47 PM
*/
public class ShapeTools {
// private static ArrayList<String> pairs;
// private static Random random = new Random();
public static void createMaximalLines(ArrayList<LineModel> currentLines) throws Exception {
LineModel leftLine = null;
LineModel rightLine = null;
int rightIndex;
for (int i = 0; i < currentLines.size(); i++) {
for (int j = 0; j < currentLines.size(); j++) {
// avoid duplicate testing
if (i >= j) continue;
// sort lines left to right
rightIndex = (
MathUtils.compare(currentLines.get(i).getP1(),
currentLines.get(j).getP1()) == 1) ? i : j;
leftLine = currentLines.get(rightIndex == i ? j : i);
rightLine = currentLines.get(rightIndex);
// proceed to test the lines
if (MathUtils.compare(leftLine.getP2(), rightLine.getP1()) != -1 &&
MathUtils.vectorsEqual(leftLine, rightLine) &&
MathUtils.vectorsEqual(
leftLine.getP1(), leftLine.getP2(),
leftLine.getP1(), rightLine.getP2()
)) {
// set the line to the longest possible
if (MathUtils.compare(leftLine.getP2(), rightLine.getP2()) == -1) {
flipPoints(leftLine.getP2(), rightLine.getP2());
}
Debugger.getInstance().addMessage("REMOVING: " + currentLines.get(rightIndex).toString());
currentLines.remove(rightIndex);
//decrease iterator
j--;
}
}
}
}
/**
* Finds instences of subshape in shape. Returns number of results depending on randomization fator
*
* @param subShape Subshape to be found
* @param shape Shape where we're finding subshape
* @param randomization (0: all instances, 1: first found, >1 modulo randomization)
* @param useMarkers checks if markers are used
* @return
*/
public static ArrayList<ShapeModel> findSubShape
(ShapeModel subShape,
ShapeModel shape,
ArrayList<ShapeModel> excludeShapeList,
int randomization,
boolean useMarkers,
int maximumSubshapes,
IProgressMonitor monitor) throws Exception {
// we limit the smallest possible shape
if (subShape.getIntersections().size() == 0) {
String message = "Shape size exption! The smallest shape has to have at least 3 points";
Debugger.getInstance().addMessage(message);
throw new Exception(message);
}
// init return list
ArrayList<ShapeModel> subShapes = new ArrayList<ShapeModel>();
if (shape.getIntersections().size() < subShape.getIntersections().size()) {
Debugger.getInstance().addMessage("ERROR: Shape has smaller ammount of intersections");
return subShapes;
}
ExecutionReport.shapeTested();
//monitor.beginTask("Detecting intersections", 1);
// Get the intersection we will use to find a transformation
IntersectionModel from = subShape.getIntersections().get(0);
// Get the list of intersection in the subshape
ArrayList<IntersectionModel> fromIntersections = subShape.getIntersections();
// Get the list of intersection in the shape
ArrayList<IntersectionModel> toIntersections = shape.getIntersections();
//monitor.worked(1);
//monitor.beginTask("Checking " + shape.getIntersections().size() + " intersections", shape.getIntersections().size());
ExecutionReport.setIntersections(toIntersections.size());
ExecutionReport.setCombinations(fromIntersections.size(), toIntersections.size());
// Point pairing tester allows detection of unpaired poins upon transformation
PointPairingTester pairTester = new PointPairingTester(subShape, shape);
ShapeModel lastFound = null;
boolean markerFound, exclusionFound;
/* create all non identical permutations of points
1. Take first transformation from subShape (has smallest angle)
2. Create second triplets
3. For each combination of points create transformation and test
*/
Matrix A = new Matrix(6, 6);
Matrix b = new Matrix(6, 1);
Matrix solution;
Matrix subsolution = new Matrix(2, 2);
Point2D transformedPoint = new Point2D.Double();
// output transform
AffineTransform outTransform = new AffineTransform();
int[] fromIndexes = new int[3];
// TODO: REDO
fromIndexes[0] = 0;
fromIndexes[1] = 1;
fromIndexes[2] = 2;
// create left matrix
// 1st row
IntersectionTriplet fromTriplet = from.getTriplet();
A.set(0, 0, from.getIntersection().getX());
A.set(0, 1, from.getIntersection().getY());
A.set(0, 2, 1);
// 2nd
A.set(1, 3, from.getIntersection().getX());
A.set(1, 4, from.getIntersection().getY());
A.set(1, 5, 1);
// 3rd
A.set(2, 0, fromTriplet.getPointA().getX());
A.set(2, 1, fromTriplet.getPointA().getY());
A.set(2, 2, 1);
// 4th
A.set(3, 3, fromTriplet.getPointA().getX());
A.set(3, 4, fromTriplet.getPointA().getY());
A.set(3, 5, 1);
// 5th
A.set(4, 0, fromTriplet.getPointB().getX());
A.set(4, 1, fromTriplet.getPointB().getY());
A.set(4, 2, 1);
// 6th
A.set(5, 3, fromTriplet.getPointB().getX());
A.set(5, 4, fromTriplet.getPointB().getY());
A.set(5, 5, 1);
// now find all intersection points in shape with same angle and ratio as original intersection
IntersectionModel to;
ArrayList<IntersectionTriplet> intersectionPairs;
IntersectionTriplet currentPair;
//Debugger.getInstance().addMessage("Init pair: " + from.getIntersection().toString() + fromTriplet.toString());
// show markers
//System.out.println("MARKERS");
for (MarkerModel model : shape.getMarkers()) {
Debugger.getInstance().addMessage(model.getLocation().toString());
}
// get itersections with the same angle
toIntersections = shape.getIntersections(from.getAngle());
ExecutionReport.setGoodIntersections(toIntersections.size());
long triplets = 0;
for (IntersectionModel intr : toIntersections) {
ArrayList<IntersectionTriplet> arr = intr.getTriplets(fromTriplet.getRatio());
if (arr != null) {
triplets += arr.size();
}
}
ExecutionReport.setTriplets(triplets);
// intersections are ordered by the angle
// we start with the smallest angle and continue to the top limit
for (int tos = 0; tos < toIntersections.size(); tos++) {
ExecutionReport.intersectionTested();
//monitor.setTaskName("Checking " + (tos + 1) + ". intersection");
if (monitor.isCanceled()) {
return subShapes;
}
// initial validation
to = toIntersections.get(tos);
Debugger.getInstance().addMessage("=================================");
Debugger.getInstance().addMessage("Intersection: " + to.getIntersection());
// // we stop if we've reached bigger angle
// if (to.getAngle() > from.getAngle()) {
// break;
// }
//
// // we continue to search for the exact angle and ratio
// if (to.getAngle() < from.getAngle()) {
// continue;
// }
// now we have an intersection with the good angle
// we will search for all points on carriers that have the same ratio as the
// initial intersection triplet
// intersection is defined by point and two carriers
intersectionPairs = to.getTriplets(fromTriplet.getRatio());
// there exist no triplets on carriers with given ratio
if (intersectionPairs == null)
continue;
// iterate for all found pairs
for (int pair = 0; pair < intersectionPairs.size(); pair++) {
ExecutionReport.tripletTested();
//monitor.subTask("Checking intersection triplets");
if (monitor.isCanceled()) {
return subShapes;
}
// get current pair
currentPair = intersectionPairs.get(pair);
Debugger.getInstance().addMessage("Pair: " + currentPair);
Debugger.getInstance().addMessage("TESTING: " + to.getIntersection().toString() + currentPair.toString());
// we test this 2 times (once when we flip pointA and pointB (symmetry))
// TODO: Test ratio .. if by flip does not change continue
for (int j = 0; j < 1; j++) {
// if (monitor.isCanceled()) {
// return subShapes;
// }
// this counter is to see how much transformations were tested
b.set(0, 0, to.getIntersection().getX());
b.set(1, 0, to.getIntersection().getY());
b.set(2, 0, j == 0 ? currentPair.getPointA().getX() : currentPair.getPointB().getX());
b.set(3, 0, j == 0 ? currentPair.getPointA().getY() : currentPair.getPointB().getY());
b.set(4, 0, j == 0 ? currentPair.getPointB().getX() : currentPair.getPointA().getX());
b.set(5, 0, j == 0 ? currentPair.getPointB().getY() : currentPair.getPointA().getY());
// create transformation by solving equation of 6 unknowns
try {
solution = A.solve(b);
}
catch (Exception ex) {
Debugger.getInstance().addMessage("Solution exception");
continue;
}
// check if it's symmetry
// first check determinant .. must be -1
subsolution.set(0, 0, solution.get(0, 0));
subsolution.set(0, 1, solution.get(1, 0));
subsolution.set(1, 0, solution.get(3, 0));
subsolution.set(1, 1, solution.get(4, 0));
// Symmetry: MathUtils.round(subsolution.det()) == -1)
// Inverse: MathUtils.isIdentity(subsolution.times(subsolution.transpose()))
if (MathUtils.round(subsolution.det()) == 0)
continue;
// we round transformations to 3 decimal places
outTransform.setTransform(
solution.get(0, 0),
solution.get(3, 0),
solution.get(1, 0),
solution.get(4, 0),
solution.get(2, 0),
solution.get(5, 0)
// MathUtils.round(solution.get(0, 0), 6),
// MathUtils.round(solution.get(3, 0), 6),
// MathUtils.round(solution.get(1, 0), 6),
// MathUtils.round(solution.get(4, 0), 6),
// MathUtils.round(solution.get(2, 0), 6),
// MathUtils.round(solution.get(5, 0), 6)
);
//Debugger.getInstance().addMessage(outTransform.toString());
// TODO : Think how can i know which other intersection points are paired
// that means .. how to pair info about information point of:
// from.pointA with to.PointA and get index in original intersection collection
// We can decide if to use to use not markers
if (useMarkers) {
//monitor.subTask("Checking marker images");
markerFound = true;
if (subShape.getMarkers().size() > 0) {
markerFound = false;
for (MarkerModel subMarker : subShape.getMarkers()) {
// first transform this marker
MarkerModel transformedMarker = new MarkerModel(subMarker.getLocation());
transformedMarker.transform(outTransform);
Debugger.getInstance().addMessage("M:" + transformedMarker.getLocation());
// now try to find this marker
for (MarkerModel marker : shape.getMarkers()) {
if (marker.compareTo(transformedMarker) == 0) {
markerFound = true;
}
}
}
}
if (!markerFound) {
Debugger.getInstance().addMessage("Marker not found");
break;
}
}
// Test remaining points
pairTester.reset();
pairTester.setPair(0, tos);
Point2D testPoint;
int testPointIndex;
boolean imageMissing = false;
// now start pairing all other intersection points to see if their images exist
//monitor.subTask("Checking point images");
while ((testPointIndex = pairTester.getFirstUnpaired()) != -1) {
// get image of point according to tranformation
testPoint = fromIntersections.get(testPointIndex).getIntersection();
// transform point and try to set pair
transformedPoint.setLocation(testPoint.getX(), testPoint.getY());
outTransform.transform(transformedPoint, transformedPoint);
if (!pairTester.setPair(testPointIndex, transformedPoint.getX(), transformedPoint.getY())) {
Debugger.getInstance().addMessage("Image not found");
imageMissing = true;
break;
}
}
if (imageMissing || !pairTester.isPaired()) {
Debugger.getInstance().addMessage("Continuing");
continue;
}
// now check this transform
//monitor.subTask("Checking line images");
ShapeModel testShape = checkTransformation(shape, subShape, new AffineTransform(outTransform));
// check if this shape can be returned and return this shape
//monitor.subTask("Checking duplicate subshape");
if (testShape == null) {
Debugger.getInstance().addMessage("Boundary not found: " + testShape);
}
if (testShape != null) {
// check if we want to exclude this transformation
if (excludeShapeList != null) {
exclusionFound = false;
for (ShapeModel excludedShape : excludeShapeList) {
if (ShapeTools.compareTransformations(excludedShape.getInitialTransform(), testShape.getInitialTransform())) {
exclusionFound = true;
break;
}
}
if (exclusionFound) {
ExecutionReport.shapeDuplicated();
Debugger.getInstance().addMessage("Already found: " + testShape);
continue;
}
}
Debugger.getInstance().addMessage("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
lastFound = testShape;
// returns all shapes
if (randomization == 0) {
//monitor.subTask("Adding sub-shape");
subShapes.add(testShape);
Debugger.getInstance().addMessage("Adding: " + testShape);
ExecutionReport.shapeFound();
} else if (randomization == 1 || (new Random()).nextInt(1000) % randomization == 0) {
//monitor.subTask("Adding sub-shape");
subShapes.add(testShape);
Debugger.getInstance().addMessage("Adding: " + testShape);
ExecutionReport.shapeFound();
//return subShapes;
} else {
Debugger.getInstance().addMessage("Skipping");
ExecutionReport.skipShape();
//monitor.subTask("Skipping sub-shape");
//subShapes.clear();
}
if (subShapes.size() >= maximumSubshapes) {
return subShapes;
}
}
}
}
//monitor.subTask("Starting new sub-shape");
//monitor.worked(tos);
}
if (lastFound != null) {
//subShapes.add(lastFound);
return subShapes;
}
return null;
}
static LineDefinition carrier;
static ArrayList<LineModel> originalCarrierLines;
static ArrayList<LineModel> imageCarrierLines;
static LineModel imageLine;
static CarrierModel imageCarrier;
private static ShapeModel getShapeInstance(ShapeModel subShape) {
// init help test shape
ShapeModel shape = new ShapeModel((ShapeGrammarModel) subShape.getParent());
shape.setParent(subShape.getParent());
shape.setId(subShape.getId());
shape.setName(subShape.getName() + "_subshape");
return shape;
}
private static ShapeModel checkTransformation(ShapeModel shape, ShapeModel subShape, AffineTransform transformation) {
// 2. check transformation of carriers
boolean boundaryFound = false;
// we'll scan transformation by transformation and we'll try to find
// transformed carrier and later if transformed line is within carrier
// init searching subshape
ShapeModel foundSubShape = getShapeInstance(subShape);
// test all carriers from left shape
// System.out.println(pairs.get(i).toString());
for (CarrierModel originalCarrierDefinition : subShape.getCarriers()) {
// transform carrier
carrier = originalCarrierDefinition.getDefinition().transform(transformation);
// try to find carrier in right shape
imageCarrier = shape.findCarrier(carrier);
// test if carrier exists
if (imageCarrier == null) {
// System.out.println("CARRIER DOES NOT EXISTS");
return null;
}
// 3. find if end points of current lines are within maximal lines of the image
// retreive all lines of current carrier
imageCarrierLines = imageCarrier.getLines();
originalCarrierLines = originalCarrierDefinition.getLines();
// scan all lines and find boundaries
for (LineModel line : originalCarrierLines) {
imageLine = line.transformTo(transformation);
boundaryFound = false;
for (LineModel image : imageCarrierLines) {
// TEST Ys
if (image.contains(
MathUtils.round(imageLine.getP1().getX()),
MathUtils.round(imageLine.getP1().getY()),
MathUtils.round(imageLine.getP2().getX()),
MathUtils.round(imageLine.getP2().getY()))) {
boundaryFound = true;
// add this line to resulting shape and recreate shape
imageLine.setParent(image);
// add line to subshape
foundSubShape.getChildrenArray().add(imageLine);
break;
}
}
if (!boundaryFound) break;
}
if (!boundaryFound) {
// System.out.println("BOUNDARY DOES NOT EXISTS");
return null;
}
// System.out.println("Carrier OK");
}
foundSubShape.setInitialTransform(transformation);
return foundSubShape.clone(true);
}
// // TODO: Study how to flip refernces instead
//
// public static boolean orderLines(LineModel expectedLeft, LineModel expectedRight) {
// if (expectedLeft.getP1().getX() > expectedRight.getP1().getX()) {
// return false;
// } else if (expectedLeft.getP1().getX() == expectedRight.getP1().getX()) {
// if (expectedLeft.getP1().getY() > expectedRight.getP1().getY()) {
// return false;
// }
// }
// return true;
// }
public static boolean compareTransformations(AffineTransform trans1, AffineTransform trans2) {
return trans1.getScaleX() == trans2.getScaleX() &&
trans1.getScaleY() == trans2.getScaleY() &&
trans1.getShearX() == trans2.getShearX() &&
trans1.getShearY() == trans2.getShearY() &&
trans1.getTranslateX() == trans2.getTranslateX() &&
trans1.getTranslateY() == trans2.getTranslateY();
}
public static void orderPoints(Point2D expectedLeft, Point2D expectedRight) {
if (MathUtils.compare(expectedLeft, expectedRight) == 1) {
flipPoints(expectedLeft, expectedRight);
}
}
public static void flipPoints(Point2D pt1, Point2D pt2) {
double x = pt2.getX();
double y = pt2.getY();
pt2.setLocation(pt1.getX(), pt1.getY());
pt1.setLocation(x, y);
}
public static boolean orderPoints(double x1, double y1, double x2, double y2) {
if (x1 > x2) {
return false;
} else if (x1 == x2) {
if (y1 > y2) {
return false;
}
}
return true;
}
public static void createRectangle(Rectangle2D rectangle, int x1, int y1, int x2, int y2) {
if (x1 < x2) {
if (y1 < y2) {
rectangle.setFrame(x1, y1, x2 - x1, y2 - y1);
} else {
rectangle.setFrame(x1, y2, x2 - x1, y1 - y2);
}
} else {
if (y1 < y2) {
rectangle.setFrame(x2, y1, x1 - x2, y2 - y1);
} else {
rectangle.setFrame(x2, y2, x1 - x2, y1 - y2);
}
}
}
}