/** * */ package cz.cuni.mff.peckam.java.origamist.model; import static cz.cuni.mff.peckam.java.origamist.math.MathHelper.EPSILON; 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.Vector3d; import cz.cuni.mff.peckam.java.origamist.exceptions.InvalidOperationException; 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.model.jaxb.Operations; 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.ExistingLineArgument; 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; /** * Inside or outside crimp fold operation. * * This fold basically consists of two reverse folds - one outside and one inside. * * @author Martin Pecka */ public class CrimpFoldOperation extends cz.cuni.mff.peckam.java.origamist.model.jaxb.CrimpFoldOperation 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 (this.type == Operations.OUTSIDE_CRIMP_FOLD) dir = dir.getOpposite(); Segment2d newRefLine = new Segment2d(new Point2d(), new Point2d()); previousState.setFurthestRotationSegment(getLine().toSegment2d()); List<Map<Layer, Layer>> layersToBend = previousState.makeReverseFold(dir, getLine().toSegment2d(), getOppositeLine() != null ? getOppositeLine().toSegment2d() : null, getRefLine().toSegment2d(), new LayerFilter(layer), oppositeLayer.size() > 0 ? new LayerFilter(oppositeLayer) : null, newRefLine); if (layersToBend.size() == 0 || layersToBend.get(0).size() == 0) throw new InvalidOperationException("crimp.fold.no.second.layers.found"); {// 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.get(0).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(); } previousState.makeReverseFold(dir, getSecondLine().toSegment2d(), getSecondOppositeLine() != null ? getSecondOppositeLine().toSegment2d() : null, newRefLine, new LayerFilter(layersToBend.get(0).keySet()), new LayerFilter(layersToBend.get(1).keySet())); ModelPoint furthest = previousState.getFurthestRotatedPointAroundSegment(); if (furthest != null) markerPosition = new Segment3d(furthest, previousState.getFurthestRotationSegment().getNearestPoint( furthest)); previousState.clearFurthestRotationSegment(); return previousState; } @Override public boolean areAgrumentsComplete() { return super.areAgrumentsComplete() && (arguments.get(3).isComplete() == arguments.get(4).isComplete()); } @Override protected List<OperationArgument> initArguments() { List<OperationArgument> result = new ArrayList<OperationArgument>(4); LineArgument line; result.add(line = new LineArgument(true, "operation.argument.select.line")); result.add(new ExistingLineArgument(true, "operation.argument.select.existing.line")); result.add(new LayersArgument(line, true, "operation.argument.select.layers")); result.add(line = new LineArgument(false, "operation.argument.select.opposite.line")); result.add(new LayersArgument(line, false, "operation.argument.select.opposite.layers")); result.add(line = new LineArgument(true, "operation.argument.select.second.line")); result.add(line = new LineArgument(false, "operation.argument.select.second.opposite.line")); return result; } @Override public void fillFromArguments() throws IllegalStateException { super.fillFromArguments(); this.line = ((LineArgument) arguments.get(0)).getLine2D(); this.refLine = ((ExistingLineArgument) arguments.get(1)).getLine2D(); this.layer = ((LayersArgument) arguments.get(2)).getLayers(); if (arguments.get(3).isComplete() && arguments.get(4).isComplete()) { this.oppositeLine = ((LineArgument) arguments.get(3)).getLine2D(); this.oppositeLayer = ((LayersArgument) arguments.get(4)).getLayers(); } this.secondLine = ((LineArgument) arguments.get(5)).getLine2D(); if (arguments.get(6).isComplete()) { this.secondOppositeLine = ((LineArgument) arguments.get(6)).getLine2D(); } } @Override public Segment3d getMarkerPosition() { return markerPosition; } @Override public String getL7dUserTip(OperationArgument argument) { String bundleKey = null; if (argument == getArguments().get(0)) { bundleKey = "crimp.line.user.tip"; } else if (argument == getArguments().get(1)) { bundleKey = "reverse.refLine.user.tip"; } else if (argument == getArguments().get(2)) { bundleKey = "crimp.layers.user.tip"; } else if (argument == getArguments().get(3)) { bundleKey = "crimp.opposite.line.user.tip"; } else if (argument == getArguments().get(4)) { bundleKey = "crimp.opposite.layers.user.tip"; } else if (argument == getArguments().get(5)) { bundleKey = "crimp.second.line.user.tip"; } else if (argument == getArguments().get(6)) { bundleKey = "crimp.second.opposite.line.user.tip"; } if (bundleKey != null) return messages.getString(bundleKey); else return null; } @Override public String toString() { return "CrimpFoldOperation [type=" + type + ", line=" + line + ", refLine=" + refLine + ", layer=" + layer + ", oppositeLine=" + oppositeLine + ", oppositeLayer=" + oppositeLayer + ", secondLine=" + secondLine + ", secondOppositeLine=" + secondOppositeLine + "]"; } @Override public Operation getSymmetricOperation(Line2d symmetryAxis) { CrimpFoldOperation result = new CrimpFoldOperation(); result.type = this.type; result.line = new Line2D(symmetryAxis.mirror(this.line.toLine2d())); result.layer = new LinkedList<Integer>(this.layer); // TODO the mirrored refline doesn't work; need to take a shortened refline from the main vertex of this fold result.refLine = new Line2D(symmetryAxis.mirror(this.refLine.toLine2d())); if (oppositeLine != null) result.oppositeLine = new Line2D(symmetryAxis.mirror(this.oppositeLine.toLine2d())); if (oppositeLayer != null) result.oppositeLayer = new LinkedList<Integer>(this.oppositeLayer); result.secondLine = new Line2D(symmetryAxis.mirror(this.secondLine.toLine2d())); if (secondOppositeLine != null) result.secondOppositeLine = new Line2D(symmetryAxis.mirror(this.secondOppositeLine.toLine2d())); if (secondOppositeLayer != null) result.secondOppositeLayer = new LinkedList<Integer>(this.secondOppositeLayer); return result; } }