341 lines
11 KiB
Java
341 lines
11 KiB
Java
package ru.delkom07.geometry;
|
|
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.opencv.core.MatOfPoint;
|
|
import org.opencv.core.Point;
|
|
|
|
import ru.delkom07.geometry.obj.LineFunction;
|
|
|
|
public class SpecificGeometry {
|
|
public static Point[] getTopLeftAndBottomRight(List<Point> points) {
|
|
double minX = Integer.MAX_VALUE;
|
|
double minY = Integer.MAX_VALUE;
|
|
|
|
double maxX = -1;
|
|
double maxY = -1;
|
|
for(Point point : points) {
|
|
if(point.x < minX) {
|
|
minX = point.x;
|
|
}
|
|
|
|
if(point.y < minY) {
|
|
minY = point.y;
|
|
}
|
|
|
|
if(point.x > maxX) {
|
|
maxX = point.x;
|
|
}
|
|
|
|
if(point.y > maxY) {
|
|
maxY = point.y;
|
|
}
|
|
}
|
|
|
|
return new Point[] {new Point(minX, minY), new Point(maxX, maxY)};
|
|
}
|
|
|
|
|
|
/**
|
|
* Разделение контура на два по линии представленной двумя точками данного контура.
|
|
* @param contour - исходный контур.
|
|
* @param point1 - первая точка линии деления.
|
|
* @param point2 - вторая точка линии деления.
|
|
* @return - два контура, замкнутые по линии деления.
|
|
*/
|
|
private static MatOfPoint[] devideCountour(MatOfPoint contour, Point point1, Point point2) {
|
|
MatOfPoint contour1 = new MatOfPoint();
|
|
MatOfPoint contour2 = new MatOfPoint();
|
|
|
|
List<Point> pointsOrdered = contour.toList();
|
|
|
|
List<Point> pointsOrdered1 = new LinkedList<Point>();
|
|
List<Point> pointsOrdered2 = new LinkedList<Point>();
|
|
|
|
// переключатель контура
|
|
boolean contourRelay = false;
|
|
for(Point point : pointsOrdered) {
|
|
// дошли до точки линии деления
|
|
if( point.equals(point1) || point.equals(point2)) {
|
|
contourRelay = !contourRelay;
|
|
|
|
// добавить все точки по линии деления для замыкания контура
|
|
if(point.equals(point1)) { // from point1 to point2
|
|
addMiddlePoints(contourRelay ? pointsOrdered1 : pointsOrdered2, point1, point2);
|
|
} else {
|
|
addMiddlePoints(contourRelay ? pointsOrdered1 : pointsOrdered2, point2, point1);
|
|
}
|
|
}
|
|
|
|
// добавить текущую точку исходного контура в соответствующий новый контур
|
|
if(contourRelay) {
|
|
pointsOrdered1.add(point);
|
|
} else {
|
|
pointsOrdered2.add(point);
|
|
}
|
|
}
|
|
|
|
contour1.fromList(pointsOrdered1);
|
|
contour2.fromList(pointsOrdered2);
|
|
|
|
return new MatOfPoint[]{contour1, contour2};
|
|
}
|
|
|
|
|
|
private static void addMiddlePoints(List<Point> pointsOrdered, Point point1, Point point2) {
|
|
double alfa;
|
|
|
|
int stepCount = (int) SimpleGeometry.distance(point1, point2) * 2;
|
|
double step = 1.0 / stepCount;
|
|
|
|
for(int i=0; i<stepCount; i++) {
|
|
alfa = step*i;
|
|
Point newPoint = new Point(point1.x*alfa + point2.x*(1.0-alfa), point1.y*alfa + point2.y*(1.0-alfa));
|
|
|
|
if(!pointsOrdered.contains(newPoint) && !newPoint.equals(point1) && !newPoint.equals(point2)) {
|
|
pointsOrdered.add(newPoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Method finds two points on middle normal with maximum distance.
|
|
* @param contour - mat of points on contour.
|
|
* @param p1 - top point.
|
|
* @param p2 - bottom point.
|
|
* @return two points on middle normal with maximum distance.
|
|
*/
|
|
private static double[][] findMaxDistancePointsOnMiddleNormal(MatOfPoint contour, final double[] p1, final double[] p2) {
|
|
double bigDistance = SimpleGeometry.distance(p1, p2)/3; // That distance is big for this contour
|
|
|
|
// Point : distance from point to middle normal
|
|
final Map<Double, double[]> distancesWithPoints = new HashMap <Double, double[]>();
|
|
|
|
// Find all distance for all point to middle normal
|
|
for(Point p: contour.toList()) {
|
|
double distance = distanceFromPointToMiddleNormal(new double[] {p.x, p.y}, new double[]{p1[0], p1[1], p2[0], p2[1]});
|
|
distancesWithPoints.put(distance, new double[] {p.x, p.y});
|
|
}
|
|
|
|
// Utils.forEach(contour,
|
|
// (double[] point) -> {
|
|
// double distance = distanceFromPointToMiddleNormal(point, new double[]{p1[0], p1[1], p2[0], p2[1]});
|
|
// distancesWithPoints.put(distance, point);
|
|
// }
|
|
// );
|
|
|
|
// new pair of points
|
|
double[][] pair = new double[2][2];
|
|
|
|
Set<Double> distances = distancesWithPoints.keySet();
|
|
pair[0] = distancesWithPoints.remove(Collections.min(distances));
|
|
while(!distancesWithPoints.isEmpty()) {
|
|
Double minDistance = Collections.min(distances);
|
|
|
|
double[] next = distancesWithPoints.get(minDistance);
|
|
if(SimpleGeometry.distance(pair[0], next) > bigDistance) {
|
|
pair[1] = next;
|
|
return pair;
|
|
}
|
|
|
|
distancesWithPoints.remove(minDistance);
|
|
}
|
|
|
|
return pair;
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculate distance from point to middle normal of segment.
|
|
* @param p - point of reference.
|
|
* @return distance from point to middle normal of segment.
|
|
* @tested
|
|
*/
|
|
private static double distanceFromPointToMiddleNormal(double[] p, double[] line) {
|
|
LineFunction func = LineFunctions.getMiddleNormalLineFunction(
|
|
new double[]{line[0], line[1]}, new double[]{line[2], line[3]});
|
|
|
|
double A = func.getA();
|
|
double B = func.getB();
|
|
double C = func.getC();
|
|
|
|
double tmp = Math.sqrt(A*A + B*B);
|
|
|
|
if(0!=tmp) return Math.abs(A*p[0] + B*p[1] + C) / tmp;
|
|
else return SimpleGeometry.distance(p, new double[]{line[0], line[1]});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Вернить две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
|
|
* Функция сначала находит список точек претендентов: наиболее близкие к линии точки по pointBufferSize справа и слева.
|
|
* После этого для каждой стороны выбирается наиболее близкая точка к нормали.
|
|
* @param listOfPoints - списко точек.
|
|
* @param line - данная линия.
|
|
* @param normal - нормаль.
|
|
* @return две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
|
|
*/
|
|
//private static Point[] getTwoPointThatLayOnGivenLine(List<Point> listOfPoints, Vector line, Vector normal) {
|
|
//short pointBufferSize = 5;
|
|
//
|
|
//List<Pair<Point, Double>> leftPoints = new LinkedList<Pair<Point, Double>>();
|
|
//List<Pair<Point, Double>> rightPoints = new LinkedList<Pair<Point, Double>>();
|
|
//
|
|
//for(Point point : listOfPoints) {
|
|
// double xDiff = (line.x2() - line.x1());
|
|
// double yDiff = (line.y2() -line.y1());
|
|
//
|
|
// xDiff = xDiff == 0 ? 0.0001 : xDiff;
|
|
// yDiff = yDiff == 0 ? 0.0001 : yDiff;
|
|
//
|
|
// double closenessToLine = Math.abs((point.x - line.x1())/xDiff - (point.y - line.y1())/yDiff);
|
|
// //double closenessToLine = Math.abs((point.x - line.x1())/(line.x2() - line.x1()) - (point.y - line.y1())/(line.y2()-line.y1()));
|
|
//
|
|
// // position relative to the normal
|
|
// // (y1-y2)*x+(x2-x1)*y+(x1*y2-x2*y1) > or < 0
|
|
// double position = (normal.y1()-normal.y2())*point.x + (normal.x2()-normal.x1()) * point.y + (normal.x1()*normal.y2()-normal.x2()*normal.y1());
|
|
//
|
|
// Comparator<Pair<Point, Double>> comparator = new Comparator<Pair<Point, Double>>() {
|
|
//
|
|
// @Override
|
|
// public int compare(Pair<Point, Double> arg0, Pair<Point, Double> arg1) {
|
|
// if(arg0.getRight() > arg1.getRight()) {
|
|
// return 1;
|
|
// }
|
|
// if(arg0.getRight() < arg1.getRight()) {
|
|
// return -1;
|
|
// }
|
|
// return 0;
|
|
// }
|
|
// };
|
|
//
|
|
// if(position <= 0) { // right position
|
|
// if(rightPoints.size() < pointBufferSize && !Double.isNaN(closenessToLine) && Double.isFinite(closenessToLine)) {
|
|
// rightPoints.add(new Pair<Point, Double>(point, closenessToLine));
|
|
//
|
|
// if(rightPoints.size() == pointBufferSize) {
|
|
// rightPoints.sort(comparator);
|
|
// }
|
|
//
|
|
// } else {
|
|
// for(int j=0; j<rightPoints.size(); j++) {
|
|
// Pair<Point, Double> rightPoint = rightPoints.get(j);
|
|
//
|
|
// if(closenessToLine < rightPoint.getRight()) {
|
|
// rightPoints.add(rightPoints.indexOf(rightPoint), new Pair<Point, Double>(point, closenessToLine));
|
|
// rightPoints.remove(rightPoints.size()-1);
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// }
|
|
//
|
|
// } else { // left position
|
|
// if(leftPoints.size() < pointBufferSize && !Double.isNaN(closenessToLine) && Double.isFinite(closenessToLine)) {
|
|
// leftPoints.add(new Pair<Point, Double>(point, closenessToLine));
|
|
//
|
|
// if(leftPoints.size() == pointBufferSize) {
|
|
// leftPoints.sort(comparator);
|
|
// }
|
|
// } else {
|
|
// for(int j=0; j<leftPoints.size(); j++) {
|
|
// Pair<Point, Double> leftPoint = leftPoints.get(j);
|
|
//
|
|
// if(closenessToLine < leftPoint.getRight()) {
|
|
// leftPoints.add(leftPoints.indexOf(leftPoint), new Pair<Point, Double>(point, closenessToLine));
|
|
// leftPoints.remove(leftPoints.size()-1);
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// }
|
|
// }
|
|
//}
|
|
//
|
|
//
|
|
//Point rightPoint = null;
|
|
//double rightBestDist = Double.MAX_VALUE;
|
|
//for(Pair<Point, Double> rightPointPair : rightPoints) {
|
|
// Point point = rightPointPair.getLeft();
|
|
//
|
|
// double xDiff = (normal.x2() - normal.x1());
|
|
// double yDiff = (normal.y2() - normal.y1());
|
|
//
|
|
// xDiff = xDiff == 0 ? 0.0001 : xDiff;
|
|
// yDiff = yDiff == 0 ? 0.0001 : yDiff;
|
|
//
|
|
// double closenessToNormal = Math.abs((point.x - normal.x1())/xDiff - (point.y - normal.y1())/yDiff);
|
|
// //double closenessToNormal = Math.abs((point.x - normal.x1())/(normal.x2() - normal.x1()) - (point.y - normal.y1())/(normal.y2()-normal.y1()));
|
|
//
|
|
// if(closenessToNormal < rightBestDist) {
|
|
// rightPoint = point;
|
|
// rightBestDist = closenessToNormal;
|
|
// }
|
|
//
|
|
//}
|
|
//
|
|
//Point leftPoint = null;
|
|
//double leftBestDist = Double.MAX_VALUE;
|
|
//for(Pair<Point, Double> leftPointPair : leftPoints) {
|
|
// Point point = leftPointPair.getLeft();
|
|
//
|
|
// double xDiff = (normal.x2() - normal.x1());
|
|
// double yDiff = (normal.y2()- normal.y1());
|
|
//
|
|
// xDiff = xDiff == 0 ? 0.0001 : xDiff;
|
|
// yDiff = yDiff == 0 ? 0.0001 : yDiff;
|
|
//
|
|
// double closenessToNormal = Math.abs((point.x - normal.x1())/xDiff - (point.y - normal.y1())/yDiff);
|
|
// //double closenessToNormal = Math.abs((point.x - normal.x1())/(normal.x2() - normal.x1()) - (point.y - normal.y1())/(normal.y2()-normal.y1()));
|
|
//
|
|
// if(closenessToNormal < leftBestDist) {
|
|
// leftPoint = point;
|
|
// leftBestDist = closenessToNormal;
|
|
// }
|
|
//}
|
|
//
|
|
//return new Point[]{leftPoint, rightPoint};
|
|
//}
|
|
|
|
|
|
/*
|
|
*
|
|
* public static Point[] getTwoPointThatLayOnGivenLine_(List<Point> listOfPoints, Vector line) {
|
|
Point point1 = null;
|
|
Point point2 = null;
|
|
Point prevPoint = listOfPoints.get(0);
|
|
|
|
boolean rele = false;
|
|
|
|
double minEstimation = Double.MAX_VALUE;
|
|
for(Point point : listOfPoints) {
|
|
double estimation = Math.abs((point.x - line.x1())/(line.x2() - line.x1()) - (point.y - line.y1())/(line.y2()-line.y1()));
|
|
|
|
if(estimation < minEstimation && distance(prevPoint, point)>10) { // ?????? distance it is not good decision
|
|
minEstimation= estimation;
|
|
|
|
if(rele) {
|
|
point1 = point;
|
|
} else {
|
|
point2 = point;
|
|
}
|
|
rele = !rele;
|
|
prevPoint = point;
|
|
}
|
|
|
|
}
|
|
|
|
return new Point[]{point1, point2};
|
|
}
|
|
*/ |