package oripa.paint.creasepattern.tool; import java.util.Collection; import java.util.LinkedList; import javax.vecmath.Vector2d; import oripa.geom.GeomUtil; import oripa.geom.Ray; import oripa.paint.core.PaintConfig; import oripa.value.CalculationResource; import oripa.value.OriLine; public class SymmetricLineFactory { private class BestPair { private OriLine bestLine = null; private Vector2d bestPoint = null; /** * @return bestLine */ public OriLine getBestLine() { return bestLine; } /** * @param bestLine bestLineを登録する */ public void setBestLine(OriLine bestLine) { this.bestLine = bestLine; } /** * @return bestPoint */ public Vector2d getBestPoint() { return bestPoint; } /** * @param bestPoint bestPointを登録する */ public void setBestPoint(Vector2d bestPoint) { this.bestPoint = bestPoint; } } /** * v1-v2 is the symmetry line, v0-v1 is the subject to be copied. * @param v0 * @param v1 * @param v2 * @param creasePattern * @throws PainterCommandFailedException */ public OriLine createSymmetricLine( Vector2d v0, Vector2d v1, Vector2d v2, Collection<OriLine> creasePattern) throws PainterCommandFailedException { BestPair pair = findBestPair(v0, v1, v2, creasePattern); Vector2d bestPoint = pair.getBestPoint(); return new OriLine(v1, bestPoint, PaintConfig.inputLineType); } /** * Core logic for creating symmetric line * @param v0 * @param v1 * @param v2 * @param creasePattern * @return * @throws PainterCommandFailedException */ private BestPair findBestPair( Vector2d v0, Vector2d v1, Vector2d v2, Collection<OriLine> creasePattern) throws PainterCommandFailedException { BestPair bestPair = new BestPair(); Vector2d v3 = GeomUtil.getSymmetricPoint(v0, v1, v2); Ray ray = new Ray(v1, new Vector2d(v3.x - v1.x, v3.y - v1.y)); double minDist = Double.MAX_VALUE; for (OriLine l : creasePattern) { Vector2d crossPoint = GeomUtil.getCrossPoint(ray, l.getSegment()); if (crossPoint == null) { continue; } double distance = GeomUtil.Distance(crossPoint, v1); if (distance < CalculationResource.POINT_EPS) { continue; } if (distance < minDist) { minDist = distance; bestPair.setBestPoint(crossPoint); bestPair.setBestLine(l); } } if (bestPair.getBestPoint() == null) { throw new PainterCommandFailedException("failed to find the terminal of symmetric line"); } return bestPair; } /** * This method generates possible rebouncing of the fold. * * @param v0 terminal point of the line to be copied * @param v1 connecting point of symmetry line and the line to be copied. * @param v2 terminal point of symmetry line * @param startV * @param creasePattern * * @return a collection of auto walk line * @throws PainterCommandFailedException */ public Collection<OriLine> createSymmetricLineAutoWalk( Vector2d v0, Vector2d v1, Vector2d v2, Vector2d startV, Collection<OriLine> creasePattern) throws PainterCommandFailedException { LinkedList<OriLine> autoWalkLines = new LinkedList<>(); addSymmetricLineAutoWalk(v0, v1, v2, 0, startV, creasePattern, autoWalkLines); return autoWalkLines; } /** * add new symmetric line to {@code autoWalkLines} recursively. * @param v0 * @param v1 * @param v2 * @param stepCount * @param startV * @param creasePattern * @param autoWalkLines * @throws PainterCommandFailedException */ private void addSymmetricLineAutoWalk( Vector2d v0, Vector2d v1, Vector2d v2, int stepCount, Vector2d startV, Collection<OriLine> creasePattern, Collection<OriLine> autoWalkLines) throws PainterCommandFailedException { //FIXME this method does not detect loop path. it causes meaningless recursion. stepCount++; if (stepCount > 36) { return; } BestPair pair = findBestPair(v0, v1, v2, creasePattern); Vector2d bestPoint = pair.getBestPoint(); OriLine bestLine = pair.getBestLine(); OriLine autoWalk = new OriLine( v1, bestPoint, PaintConfig.inputLineType); autoWalkLines.add(autoWalk); if (GeomUtil.Distance(bestPoint, startV) < CalculationResource.POINT_EPS) { return; } addSymmetricLineAutoWalk( v1, bestPoint, bestLine.p0, stepCount, startV, creasePattern, autoWalkLines); } }