package es.iiia.shapegrammar.shape;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Hashtable;
import es.iiia.shapegrammar.shape.guides.IntersectionTriplet;
import es.iiia.shapegrammar.utils.MathUtils;
import es.iiia.shapegrammar.utils.Vector;
public class IntersectionModel implements Comparable<IntersectionModel> {
private CarrierModel carrier1;
private CarrierModel carrier2;
private Point2D intersection;
private double angle;
private Hashtable<Double, ArrayList<IntersectionTriplet>> triplets;
// constructor
public IntersectionModel(CarrierModel carrier1, CarrierModel carrier2, boolean includeOuter) {
this.carrier1 = carrier1;
this.carrier2 = carrier2;
// first find an intersetion point
this.findIntersection();
// this is done if lines have an intersection point
if (this.intersection != null) {
// now check if this is outer or inner intersection
int found = 0;
if (!includeOuter) {
for (LineModel line : carrier1.getLines()) {
if (line.contains(this.getIntersection())) {
found += 1;
break;
}
}
if (found < 1) {
this.intersection = null;
return;
}
for (LineModel line : carrier2.getLines()) {
if (line.contains(this.getIntersection())) {
found += 1;
break;
}
}
if (found < 2) {
this.intersection = null;
return;
}
}
// add this interscetion to carriers it is used in pointA,B calculations
this.carrier1.addPoint(this.intersection);
this.carrier2.addPoint(this.intersection);
// find angles and 2 closest points to intersetion
this.findAngle();
//this.intersection.setLabel(SGShape.getName(naming++) + ":" + this.angle + " " + this.intersection);
//this.intersection.setLabel(this.intersection.toString());
//this.intersection.setLabel(SGShape.getName(naming++));
//this.intersection.setVisible(true);
}
// point have no intersection
else {
// TODO: handle nonintesecting shapes
// we'll predefine angle to maximal value 360 if lines are paralel
this.intersection = null;
return;
// this.angle = 360;
// this.intersection = carrier1.getDefinition().getP1();
// this.pointA = carrier1.getDefinition().getP2();
// this.pointB = carrier2.getDefinition().getP1();
//
// this.intersection.setLabel(SGShape.getName(naming++) + " : " + this.pointA + this.pointB);
//
// // create raio of these two sides (smaller/bigger)
// this.createRatio();
}
}
/**
* Gets first found triplet
*/
public IntersectionTriplet getTriplet() {
// gets first found pair
Point2D pointA = null;
for (int i = 0; i < this.carrier1.getPoints().size(); i++) {
pointA = this.carrier1.getPoints().get(i);
if (MathUtils.compare(pointA, this.intersection) != 0)
break;
}
Point2D pointB = null;
for (int i = 0; i < this.carrier2.getPoints().size(); i++) {
pointB = this.carrier2.getPoints().get(i);
if (MathUtils.compare(pointB, this.intersection) != 0 &&
MathUtils.compare(pointB, pointA) != 0)
break;
}
double ratio = getRatio(pointA, pointB);
return new IntersectionTriplet(pointA, pointB, ratio);
}
/**
* Finds all triplets with the given ratio
*
* @param ratio
* @return collection of triplets with the given ratio
*/
public ArrayList<IntersectionTriplet> getTriplets(double ratio) {
// check if collection exists
if (this.triplets == null) {
this.triplets = new Hashtable<Double, ArrayList<IntersectionTriplet>>();
this.createTriplets(this.carrier1, this.carrier2);
this.createTriplets(this.carrier2, this.carrier1);
}
// return initialized collection
return this.triplets.get(ratio);
}
private void createTriplets(CarrierModel car1, CarrierModel car2) {
// we'll create collection for each ratio
Point2D pointA, pointB;
double currentRatio;
// TODO: maybe the problem with indexes of type double
for (int i = 0; i < car1.getPoints().size(); i++) {
pointA = car1.getPoints().get(i);
// exclude intersecion
if (MathUtils.compare(pointA, this.intersection) == 0)
continue;
for (int j = 0; j < car2.getPoints().size(); j++) {
// init point
pointB = car2.getPoints().get(j);
// avoid same points
if (MathUtils.compare(pointA, pointB) == 0)
continue;
// exclude intersecion
if (MathUtils.compare(pointB, this.intersection) == 0)
continue;
// count current ratio
currentRatio = getRatio(pointA, pointB);
// check if we have current value in collection
if (!this.triplets.containsKey(currentRatio)) {
this.triplets.put(currentRatio, new ArrayList<IntersectionTriplet>());
}
// now add this triplet to this ratio
this.triplets.get(currentRatio).add(new IntersectionTriplet(pointA, pointB, currentRatio));
}
}
}
// private methods
private double getRatio(Point2D pointA, Point2D pointB) {
return getRatio(this.intersection, pointA, pointB);
}
private static double getRatio(Point2D intersection, Point2D pointA, Point2D pointB) {
try {
Vector t1 = new Vector(pointA, intersection);
Vector t2 = new Vector(pointB, intersection);
double norm1 = Math.sqrt(t1.x * t1.x + t1.y * t1.y);
double norm2 = Math.sqrt(t2.x * t2.x + t2.y * t2.y);
return MathUtils.round(norm1 / norm2);
// if (norm1 < norm2) {
// return MathUtils.round(norm1 / norm2);
// } else {
// return MathUtils.round(norm2 / norm1);
// }
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
private void findIntersection() {
// PROBLEM: ZLE NACHADZA BODY
double m1, m2, c1, c2, det, x, y;
// y = mx + c
// m = (yB-yA / xB - xA) ... slope
// mX - Y - m*xB + yB = 0
m1 = carrier1.getDefinition().getSlope();
m2 = carrier2.getDefinition().getSlope();
// for finding intersection we'll be using Cramers rule
// first check if lines are paralel [determinant of coeficients == 0]
//System.out.println("DET: " + MathUtils.getDeterminant(m1, -1, m2, -1));
//System.out.println("DET: " + (det = MathUtils.getDeterminant(m1, -1, m2, -1)));
// check if one of the carrier is non-function (e.g. x=4)
if (m1 == Double.MAX_VALUE || m2 == Double.MAX_VALUE) {
// System.out.println("NON FUNCTION");
if (m1 != m2) {
if (m1 == Double.MAX_VALUE) {
x = carrier1.getDefinition().getP1().getX();
y = m2 * x + carrier2.getDefinition().getC();
this.intersection = new Point2D.Double(x, y);
} else {
x = carrier2.getDefinition().getP1().getX();
y = m1 * x + carrier1.getDefinition().getC();
this.intersection = new Point2D.Double(x, y);
}
}
// carriers cannot be are parallel
} else if (MathUtils.round((det = MathUtils.getDeterminant(m1, -1, m2, -1))) != 0) {
// now we know there exists intersection so we'll count values for X and Y.
c1 = -carrier1.getDefinition().getC();
c2 = -carrier2.getDefinition().getC();
x = MathUtils.getDeterminant(c1, -1, c2, -1) / det;
y = MathUtils.getDeterminant(m1, c1, m2, c2) / det;
this.intersection = new Point2D.Double(x, y);
}
// System.out.println("INTERSECTION: " + new Point2D(x, y).toString());
}
@SuppressWarnings("restriction")
private void findAngle() {
// carriers can be parallel
if (this.intersection == null) {
this.angle = 0;
}
// otherwise count parameters
else {
// first order points
try {
// create vectors
Vector t1 = new Vector(
carrier1.getDefinition().getP1(),
carrier1.getDefinition().getP2());
Vector t2 = new Vector(
carrier2.getDefinition().getP1(),
carrier2.getDefinition().getP2());
double dot = t1.x * t2.x + t1.y * t2.y;
double norm1 = Math.sqrt(t1.x * t1.x + t1.y * t1.y);
double norm2 = Math.sqrt(t2.x * t2.x + t2.y * t2.y);
this.angle = MathUtils.round(Math.acos(dot / (norm1 * norm2)) * (180 / Math.PI));
} catch (Exception e) {
e.printStackTrace();
}
}
}
// private Point2D findClosestPoint(CarrierModel carrier) {
// // finds closest points
//
// // if both points are on the left or both on the right of the intersection point
// // we'll keep the angle. Otherwise it's 180
// Point2D intersection;
//
// for (int i = 0; i < carrier.getIntersections().size(); i++) {
// intersection = carrier.getIntersections().get(i);
//
// // else try to find point to the right
// if (intersection.getX() > this.intersection.getX() ||
// (intersection.getX() == this.intersection.getX() && intersection.getY() > this.intersection.getY())) {
// return intersection;
// }
//
// // we've reached the last element
// if (i == carrier.getIntersections().size() - 1) {
// // flip angle, because point is in oposite direction as expected
// this.angle = 180 - this.angle;
//
// if (intersection.getX() == this.intersection.getX() &&
// intersection.getY() == this.intersection.getY()) {
// // don't return the same point
// return carrier.getIntersections().get(i - 1);
// } else {
// // get the point to the left
// return carrier.getIntersections().get(i);
// }
// }
// }
// // this should never happen
// return null;
// }
// Gs/Ss
public Point2D getIntersection() {
return intersection;
}
public double getAngle() {
return angle;
}
// temp
public static int naming;
public int compareTo(IntersectionModel sgIntersection) {
if (this.angle < sgIntersection.angle) {
return -1;
} else if (this.angle > sgIntersection.angle) {
return 1;
}
return 0;
}
}