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 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 pointsOrdered = contour.toList(); List pointsOrdered1 = new LinkedList(); List pointsOrdered2 = new LinkedList(); // переключатель контура 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 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 distancesWithPoints = new HashMap (); // 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 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 listOfPoints, Vector line, Vector normal) { //short pointBufferSize = 5; // //List> leftPoints = new LinkedList>(); //List> rightPoints = new LinkedList>(); // //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> comparator = new Comparator>() { // // @Override // public int compare(Pair arg0, Pair 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, closenessToLine)); // // if(rightPoints.size() == pointBufferSize) { // rightPoints.sort(comparator); // } // // } else { // for(int j=0; j rightPoint = rightPoints.get(j); // // if(closenessToLine < rightPoint.getRight()) { // rightPoints.add(rightPoints.indexOf(rightPoint), new Pair(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, closenessToLine)); // // if(leftPoints.size() == pointBufferSize) { // leftPoints.sort(comparator); // } // } else { // for(int j=0; j leftPoint = leftPoints.get(j); // // if(closenessToLine < leftPoint.getRight()) { // leftPoints.add(leftPoints.indexOf(leftPoint), new Pair(point, closenessToLine)); // leftPoints.remove(leftPoints.size()-1); // break; // } // } // // } // } //} // // //Point rightPoint = null; //double rightBestDist = Double.MAX_VALUE; //for(Pair 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 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 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}; } */