package oripa.paint.creasepattern.tool; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.vecmath.Vector2d; import oripa.geom.GeomUtil; import oripa.value.CalculationResource; import oripa.value.OriLine; public class LineAdder { private class PointComparatorX implements Comparator<Vector2d> { @Override public int compare(Vector2d v1, Vector2d v2) { if(v1.x == v2.x){ return 0; } return v1.x > v2.x ? 1 : -1; } } private class PointComparatorY implements Comparator<Vector2d> { @Override public int compare(Vector2d v1, Vector2d v2) { if(v1.y == v2.y){ return 0; } return ((Vector2d) v1).y > ((Vector2d) v2).y ? 1 : -1; } } /** * * @param inputLine * @param currentLines * @return true. */ private boolean divideCurrentLines(OriLine inputLine, Collection<OriLine> currentLines){ LinkedList<OriLine> toBeAdded = new LinkedList<>(); // intersection of (aux input, other type lines) are rejected for (Iterator<OriLine> iterator = currentLines.iterator(); iterator.hasNext();) { OriLine line = iterator.next(); if (inputLine.typeVal == OriLine.TYPE_NONE && line.typeVal != OriLine.TYPE_NONE) { continue; } Vector2d crossPoint = GeomUtil.getCrossPoint(inputLine, line); if (crossPoint == null) { continue; } iterator.remove(); if (GeomUtil.Distance(line.p0, crossPoint) > CalculationResource.POINT_EPS) { toBeAdded.add(new OriLine(line.p0, crossPoint, line.typeVal)); } if (GeomUtil.Distance(line.p1, crossPoint) > CalculationResource.POINT_EPS) { toBeAdded.add(new OriLine(line.p1, crossPoint, line.typeVal)); } //crossingLines.add(line); } for(OriLine line : toBeAdded){ currentLines.add(line); } return true; } /** * Input line should be divided by other lines. This function returns end points of such new small lines. * @param inputLine * @param currentLines * @return points on input line divided by currentLines */ private List<Vector2d> createInputLinePoints(OriLine inputLine, Collection<OriLine> currentLines){ ArrayList<Vector2d> points = new ArrayList<Vector2d>(); points.add(inputLine.p0); points.add(inputLine.p1); // divide input line by existing lines for (OriLine line : currentLines) { // reject (M/V input, aux lines) // It is by MITANI jun, I don't know why. if (inputLine.typeVal != OriLine.TYPE_NONE && line.typeVal == OriLine.TYPE_NONE) { continue; } // If the intersection is on the end of the line, skip if (GeomUtil.Distance(inputLine.p0, line.p0) < CalculationResource.POINT_EPS || GeomUtil.Distance(inputLine.p0, line.p1) < CalculationResource.POINT_EPS|| GeomUtil.Distance(inputLine.p1, line.p0) < CalculationResource.POINT_EPS|| GeomUtil.Distance(inputLine.p1, line.p1) < CalculationResource.POINT_EPS) { continue; } if (GeomUtil.DistancePointToSegment(line.p0, inputLine.p0, inputLine.p1) < CalculationResource.POINT_EPS) { points.add(line.p0); } if (GeomUtil.DistancePointToSegment(line.p1, inputLine.p0, inputLine.p1) < CalculationResource.POINT_EPS) { points.add(line.p1); } // Calculates the intersection Vector2d crossPoint = GeomUtil.getCrossPoint(inputLine, line); if (crossPoint != null) { points.add(crossPoint); } } return points; } /** * Adds a new OriLine, also searching for intersections with others * that would cause their mutual division * * @param inputLine * @param currentLines current line list. it will be affected as * new lines are added and unnecessary lines are removed. */ public void addLine(OriLine inputLine, Collection<OriLine> currentLines) { //ArrayList<OriLine> crossingLines = new ArrayList<OriLine>(); // for debug? // If it already exists, do nothing for (OriLine line : currentLines) { if (GeomUtil.isSameLineSegment(line, inputLine)) { return; } } divideCurrentLines(inputLine, currentLines); List<Vector2d> points = createInputLinePoints(inputLine, currentLines); // sort in order to make points sequential boolean sortByX = Math.abs(inputLine.p0.x - inputLine.p1.x) > Math.abs(inputLine.p0.y - inputLine.p1.y); if (sortByX) { Collections.sort(points, new PointComparatorX()); } else { Collections.sort(points, new PointComparatorY()); } Vector2d prePoint = points.get(0); // add new lines sequentially for (int i = 1; i < points.size(); i++) { Vector2d p = points.get(i); // remove very short line if (GeomUtil.Distance(prePoint, p) < CalculationResource.POINT_EPS) { continue; } currentLines.add(new OriLine(prePoint, p, inputLine.typeVal)); prePoint = p; } } /** * * @param lines lines to be added * @param destination collection as a destination */ public void addAll(Collection<OriLine> lines, Collection<OriLine> destination) { for (OriLine line : lines) { addLine(line, destination); } } }