/** * */ package cz.cuni.mff.peckam.java.origamist.model; import static cz.cuni.mff.peckam.java.origamist.math.MathHelper.EPSILON; import java.text.MessageFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.vecmath.Point2d; import javax.vecmath.Vector2d; import javax.vecmath.Vector3d; import cz.cuni.mff.peckam.java.origamist.exceptions.InvalidOperationException; import cz.cuni.mff.peckam.java.origamist.math.AngleUnit; import cz.cuni.mff.peckam.java.origamist.math.Line2d; import cz.cuni.mff.peckam.java.origamist.math.Segment2d; import cz.cuni.mff.peckam.java.origamist.math.Segment3d; import cz.cuni.mff.peckam.java.origamist.modelstate.Direction; import cz.cuni.mff.peckam.java.origamist.modelstate.Layer; import cz.cuni.mff.peckam.java.origamist.modelstate.LayerFilter; import cz.cuni.mff.peckam.java.origamist.modelstate.ModelPoint; import cz.cuni.mff.peckam.java.origamist.modelstate.ModelState; import cz.cuni.mff.peckam.java.origamist.modelstate.arguments.AngleArgument; import cz.cuni.mff.peckam.java.origamist.modelstate.arguments.LayersArgument; import cz.cuni.mff.peckam.java.origamist.modelstate.arguments.LineArgument; import cz.cuni.mff.peckam.java.origamist.modelstate.arguments.OperationArgument; import cz.cuni.mff.peckam.java.origamist.services.ServiceLocator; import cz.cuni.mff.peckam.java.origamist.services.interfaces.ConfigurationManager; /** * A thunderbolt fold. * * This fold consists of two basic folds - one mountain and one valley. * * @author Martin Pecka */ public class ThunderboltFoldOperation extends cz.cuni.mff.peckam.java.origamist.model.jaxb.ThunderboltFoldOperation implements HasSymmetricOperation { /** P1 is the center of rotation segment, P2 is the furthest rotated point in the last getModelState() call. */ protected Segment3d markerPosition = null; @Override public ModelState getModelState(ModelState previousState) throws InvalidOperationException { Direction dir = Direction.MOUNTAIN; if (invert != null && invert == true) dir = dir.getOpposite(); if (secondAngle == null) secondAngle = angle; Segment2d line = getLine().toSegment2d(); Segment2d secondLine = getSecondLine().toSegment2d(); Point2d refPoint = secondLine.getP1(); if (line.isBorderPoint(refPoint)) refPoint = secondLine.getP2(); Segment2d intPoint = line.getIntersection(secondLine); // check if line and secondLine have no "inside" common point if (intPoint != null) { Point2d point = intPoint.getPoint(); if (!intPoint.getVector().epsilonEquals(new Vector2d(), EPSILON) || !line.isBorderPoint(point) || !secondLine.isBorderPoint(point)) throw new InvalidOperationException( "<line> and <secondLine> must have either no intersection point or exactly one (and then it" + " has to be a border point of both of them), but their intersection is: " + intPoint.toStringAsIntersection(), this); } previousState.setFurthestRotationSegment(getLine().toSegment2d()); Map<Layer, Layer> layersToBend = previousState.makeFold(dir, line.getP1(), line.getP2(), refPoint, new LayerFilter(layer), angle); {// if we rotated the layers so that they face the screen by their opposite sides, we don't want to change the // fold direction because the change will be made simply by the rotation of the layers Entry<Layer, Layer> anyLayer = layersToBend.entrySet().iterator().next(); Vector3d normalBeforeRotation = anyLayer.getValue().getNormal(); Vector3d normalAfterRotation = anyLayer.getKey().getNormal(); double angleToScreenBefore = previousState.getScreenNormal().angle(normalBeforeRotation); double angleToScreenAfter = previousState.getScreenNormal().angle(normalAfterRotation); if ((angleToScreenBefore >= Math.PI / 2d + EPSILON) == (angleToScreenAfter >= Math.PI / 2d + EPSILON)) dir = dir.getOpposite(); } {// find the second refPoint - try to prolong the line created by some of border points of line and secondLine refPoint = null; out: for (Point2d p1 : line.getPoints()) { for (Point2d p2 : secondLine.getPoints()) { Segment2d seg = new Segment2d(p1, p2); if (!seg.getVector().epsilonEquals(new Vector2d(), EPSILON)) { Point2d newRefPoint = seg.getPointForParameter(1 + 10000d * EPSILON); if (previousState.getOrigami().getModel().getPaper().containsRelative(newRefPoint)) { refPoint = newRefPoint; break out; } } } } } previousState.makeFold(dir, secondLine.getP1(), secondLine.getP2(), refPoint, new LayerFilter(layersToBend.keySet()), secondAngle); ModelPoint furthest = previousState.getFurthestRotatedPointAroundSegment(); if (furthest != null) markerPosition = new Segment3d(furthest, previousState.getFurthestRotationSegment().getNearestPoint( furthest)); previousState.clearFurthestRotationSegment(); return previousState; } @Override protected List<OperationArgument> initArguments() { List<OperationArgument> result = new ArrayList<OperationArgument>(5); LineArgument line; result.add(line = new LineArgument(true, "operation.argument.select.line")); result.add(new LayersArgument(line, true, "operation.argument.select.layers")); result.add(new AngleArgument(true, "operation.argument.angle", 0d, Math.PI, AngleUnit.RAD)); result.add(line = new LineArgument(true, "operation.argument.select.second.line")); result.add(new AngleArgument(false, "operation.argument.second.angle", 0d, Math.PI, AngleUnit.RAD)); return result; } @Override public void fillFromArguments() throws IllegalStateException { super.fillFromArguments(); this.line = ((LineArgument) arguments.get(0)).getLine2D(); this.layer = ((LayersArgument) arguments.get(1)).getLayers(); this.angle = ((AngleArgument) arguments.get(2)).getAngle(); this.secondLine = ((LineArgument) arguments.get(3)).getLine2D(); if (arguments.get(4).isComplete()) this.secondAngle = ((AngleArgument) arguments.get(4)).getAngle(); } @Override public Segment3d getMarkerPosition() { return markerPosition; } @Override public String getL7dUserTip(OperationArgument argument) { String bundleKey = null; if (argument == getArguments().get(0)) { bundleKey = "thunder.line.user.tip"; } else if (argument == getArguments().get(1)) { bundleKey = "thunder.layers.user.tip"; } else if (argument == getArguments().get(2)) { bundleKey = "thunder.angle.user.tip"; } else if (argument == getArguments().get(3)) { bundleKey = "thunder.second.line.user.tip"; } else if (argument == getArguments().get(4)) { bundleKey = "thunder.second.angle.user.tip"; } if (bundleKey != null) return messages.getString(bundleKey); else return null; } @Override public String getDefaultDescription() { String prefix = type.toString(); StringBuilder text = new StringBuilder("<html><body><dl style=\"margin:0px;\"><dt>").append(getL7dName()) .append("</dt>"); AngleUnit prefUnit = ServiceLocator.get(ConfigurationManager.class).get().getPreferredAngleUnit(); text.append("<dd style=\"margin-left: 10px;\">"); if (invert == null || !invert) text.append(messages.getString(prefix + ".firstMountain")); else text.append(messages.getString(prefix + ".firstValley")); text.append("</dd>"); text.append("<dd style=\"margin-left: 10px;\">"); String angleText = prefUnit.formatValue(AngleUnit.RAD.convertTo(angle, prefUnit)); text.append(MessageFormat.format(messages.getString(prefix + ".angleFormat"), new Object[] { angleText })); text.append("</dd>"); text.append("<dd style=\"margin-left: 10px;\">"); if (invert == null || !invert) text.append(messages.getString(prefix + ".secondValley")); else text.append(messages.getString(prefix + ".secondMountain")); text.append("</dd>"); text.append("<dd style=\"margin-left: 10px;\">"); angleText = prefUnit.formatValue(AngleUnit.RAD.convertTo(secondAngle != null ? secondAngle : angle, prefUnit)); text.append(MessageFormat.format(messages.getString(prefix + ".secondAngleFormat"), new Object[] { angleText })); text.append("</dd>"); text.append("</dl></body></html>"); return text.toString(); } @Override public String toString() { return "ThunderboltFoldOperation [angle=" + angle + ", line=" + line + ", layer=" + layer + ", secondAngle=" + secondAngle + ", secondLine=" + secondLine + "]"; } @Override public Operation getSymmetricOperation(Line2d symmetryAxis) { ThunderboltFoldOperation result = new ThunderboltFoldOperation(); result.type = this.type; result.line = new Line2D(symmetryAxis.mirror(this.line.toLine2d())); result.layer = new LinkedList<Integer>(this.layer); result.angle = this.angle; result.secondLine = new Line2D(symmetryAxis.mirror(this.secondLine.toLine2d())); result.secondAngle = this.secondAngle; result.invert = this.invert; return result; } }