/*
* This file is part of LaTeXDraw.
* Copyright (c) 2005-2017 Arnaud BLOUIN
* LaTeXDraw is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later version.
* LaTeXDraw is distributed without any warranty; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package net.sf.latexdraw.actions.shape;
import java.awt.geom.Rectangle2D;
import java.util.Optional;
import net.sf.latexdraw.actions.DrawingAction;
import net.sf.latexdraw.actions.Modifying;
import net.sf.latexdraw.actions.ShapeActionImpl;
import net.sf.latexdraw.models.MathUtils;
import net.sf.latexdraw.models.interfaces.shape.IDrawing;
import net.sf.latexdraw.models.interfaces.shape.IGroup;
import net.sf.latexdraw.models.interfaces.shape.IPoint;
import net.sf.latexdraw.models.interfaces.shape.Position;
import net.sf.latexdraw.util.LangTool;
import org.malai.undo.Undoable;
/**
* This action scales a shape.
* @author Arnaud Blouin
*/
public class ScaleShapes extends ShapeActionImpl<IGroup> implements DrawingAction, Undoable, Modifying {
/** The direction of the scaling. */
Optional<Position> refPosition;
/** The new X position used to compute the scale factor. */
double newX;
/** The new Y position used to compute the scale factor. */
double newY;
/** The bound of the selected shapes used to perform the scaling. */
final Rectangle2D bound;
/** The old width of the selection. */
double oldWidth;
/** The old height of the selection. */
double oldHeight;
boolean doneOnce;
/** The drawing that will be handled by the action. */
protected Optional<IDrawing> drawing;
public ScaleShapes() {
super();
drawing = Optional.empty();
refPosition = Optional.empty();
bound = new Rectangle2D.Double();
doneOnce = false;
}
@Override
public RegistrationPolicy getRegistrationPolicy() {
return hadEffect() ? RegistrationPolicy.LIMITED : RegistrationPolicy.LIMITED;
}
@Override
public boolean hadEffect() {
return isDone() && (!MathUtils.INST.equalsDouble(oldWidth, bound.getWidth()) || !MathUtils.INST.equalsDouble(oldHeight, bound.getHeight()));
}
@Override
public boolean canDo() {
return super.canDo() && drawing.isPresent() && refPosition.isPresent() && isValidScales();
}
private boolean isValidScales() {
if(!refPosition.isPresent()) return false;
switch(refPosition.get()) {
case EAST:
return MathUtils.INST.isValidCoord(newX) && scaledWidth(newX) > 1.0;
case WEST:
return MathUtils.INST.isValidCoord(newX) && scaledWidth(newX) > 1.0;
case NORTH:
return MathUtils.INST.isValidCoord(newY) && scaledHeight(newY) > 1.0;
case SOUTH:
return MathUtils.INST.isValidCoord(newY) && scaledHeight(newY) > 1.0;
default:
return MathUtils.INST.isValidCoord(newX) && MathUtils.INST.isValidCoord(newY) && scaledHeight(newY) > 1.0 && scaledWidth(newX) > 1.0;
}
}
@Override
protected void doActionBody() {
if(Double.isNaN(oldWidth)) {
shape.ifPresent(sh -> {
final IPoint br = sh.getBottomRightPoint();
final IPoint tl = sh.getTopLeftPoint();
oldWidth = br.getX() - tl.getX();
oldHeight = br.getY() - tl.getY();
updateBound(tl, br);
});
}
redo();
}
private void updateBound(final IPoint tl, final IPoint br) {
bound.setFrameFromDiagonal(tl.getX(), tl.getY(), br.getX(), br.getY());
}
@Override
public void undo() {
shape.ifPresent(sh -> refPosition.ifPresent(pos -> drawing.ifPresent(dr -> {
sh.scale(oldWidth, oldHeight, pos, bound);
sh.setModified(true);
dr.setModified(true);
updateBound(sh.getTopLeftPoint(), sh.getBottomRightPoint());
})));
}
@Override
public void setShape(final IGroup sh) {
super.setShape(sh);
if(sh != null) {
updateBound(sh.getTopLeftPoint(), sh.getBottomRightPoint());
}
}
private double scaledHeight(final double y) {
if(!refPosition.isPresent()) return 0.0;
if(refPosition.get().isSouth()) return bound.getHeight() + bound.getY() - y;
if(refPosition.get().isNorth()) return bound.getHeight() + y - bound.getMaxY();
return 0.0;
}
private double scaledWidth(final double x) {
if(!refPosition.isPresent()) return 0.0;
if(refPosition.get().isWest()) return bound.getWidth() + x - bound.getMaxX();
if(refPosition.get().isEast()) return bound.getWidth() + bound.getX() - x;
return 0.0;
}
@Override
public void redo() {
shape.ifPresent(sh -> refPosition.ifPresent(pos -> drawing.ifPresent(dr -> {
sh.scale(scaledWidth(newX), scaledHeight(newY), pos, bound);
sh.setModified(true);
dr.setModified(true);
updateBound(sh.getTopLeftPoint(), sh.getBottomRightPoint());
})));
}
@Override
public String getUndoName() {
return LangTool.INSTANCE.getBundle().getString("Actions.11");
}
public Optional<Position> getRefPosition() {
return refPosition;
}
public void setRefPosition(final Position pos) {
refPosition = Optional.ofNullable(pos);
}
public double getNewX() {
return newX;
}
public void setNewX(final double x) {
if(scaledWidth(x) > 1.0) {
newX = x;
}
}
public double getNewY() {
return newY;
}
public void setNewY(final double y) {
if(scaledHeight(y) > 1.0) {
newY = y;
}
}
@Override
public void setDrawing(final IDrawing dr) {
drawing = Optional.ofNullable(dr);
}
@Override
public Optional<IDrawing> getDrawing() {
return drawing;
}
}