package ij.plugin.filter;
import ij.*;
import ij.gui.*;
import ij.process.*;
import java.awt.*;
import java.awt.geom.*;
public class Rotator implements ExtendedPlugInFilter, DialogListener {
public static final String GRID = "|GRID|";
private int flags = DOES_ALL|SUPPORTS_MASKING|PARALLELIZE_STACKS;
private static double angle = 15.0;
private static boolean fillWithBackground;
private static boolean enlarge;
private static int gridLines = 1;
private ImagePlus imp;
private int bitDepth;
private boolean canEnlarge;
private boolean isEnlarged;
private GenericDialog gd;
private PlugInFilterRunner pfr;
private String[] methods = ImageProcessor.getInterpolationMethods();
private static int interpolationMethod = ImageProcessor.BILINEAR;
private Overlay overlay;
private boolean done;
public int setup(String arg, ImagePlus imp) {
this.imp = imp;
if (imp!=null) {
bitDepth = imp.getBitDepth();
Roi roi = imp.getRoi();
if (roi!=null && roi.isLine())
roi = null;
overlay = imp.getOverlay();
if (roi!=null && overlay!=null && Macro.getOptions()==null) {
String msg = "This image has an overlay so the\nselection will be removed.";
if (!IJ.showMessageWithCancel("Rotator", msg))
return DONE;
imp.deleteRoi();
}
Rectangle r = roi!=null?roi.getBounds():null;
canEnlarge = r==null || (r.x==0&&r.y==0&&r.width==imp.getWidth()&&r.height==imp.getHeight());
if (imp.getDisplayMode()==IJ.COMPOSITE) { Undo.setup(Undo.TRANSFORM, imp);
flags = flags | NO_UNDO_RESET;
}
Undo.saveOverlay(imp);
if (overlay==null)
overlay = new Overlay();
}
return flags;
}
public void run(ImageProcessor ip) {
if (enlarge && gd.wasOKed()) synchronized(this) {
if (!isEnlarged) {
enlargeCanvas();
isEnlarged=true;
}
}
if (isEnlarged) { int slice = pfr.getSliceNumber();
if (imp.getStackSize()==1)
ip = imp.getProcessor();
else
ip = imp.getStack().getProcessor(slice);
}
ip.setInterpolationMethod(interpolationMethod);
if (fillWithBackground)
ip.setBackgroundColor(Toolbar.getBackgroundColor());
else
ip.setBackgroundValue(0);
ip.rotate(angle);
if (!gd.wasOKed())
drawGridLines(gridLines);
if (overlay!=null && !imp.getHideOverlay()) {
Overlay overlay2 = overlay.rotate(angle, ip.getWidth()/2, ip.getHeight()/2);
if (overlay2!=null && overlay2.size()>0)
imp.setOverlay(overlay2);
}
if (isEnlarged && imp.getStackSize()==1) {
imp.changes = true;
imp.updateAndDraw();
Undo.setup(Undo.COMPOUND_FILTER_DONE, imp);
}
if (done) { Overlay ovly = imp.getOverlay();
if (ovly!=null) {
ovly.remove(GRID);
if (ovly.size()==0) imp.setOverlay(null);
}
}
}
void enlargeCanvas() {
imp.unlock();
IJ.run(imp, "Select All", "");
IJ.run(imp, "Rotate...", "angle="+angle);
Roi roi = imp.getRoi();
imp.deleteRoi();
Rectangle2D.Double fb = roi.getFloatBounds();
Rectangle r = new Rectangle((int)Math.round(fb.x),(int)Math.round(fb.y),(int)Math.round(fb.width),(int)Math.round(fb.height));
if (r.width<imp.getWidth()) r.width = imp.getWidth();
if (r.height<imp.getHeight()) r.height = imp.getHeight();
IJ.showStatus("Rotate: Enlarging...");
if (imp.getStackSize()==1)
Undo.setup(Undo.COMPOUND_FILTER, imp);
IJ.run(imp, "Canvas Size...", "width="+r.width+" height="+r.height+" position=Center "+(fillWithBackground?"":"zero"));
IJ.showStatus("Rotating...");
}
void drawGridLines(int lines) {
if (overlay==null)
return;
overlay.remove(GRID);
if (lines==0)
return;
GeneralPath path = new GeneralPath();
float width = imp.getWidth();
float height = imp.getHeight();
float xinc = width/lines;
float yinc = height/lines;
float xstart = xinc/2f;
float ystart = yinc/2f;
for (int i=0; i<lines; i++) {
path.moveTo(xstart+xinc*i, 0f);
path.lineTo(xstart+xinc*i, height);
path.moveTo(0f, ystart+yinc*i);
path.lineTo(width, ystart+yinc*i);
}
Roi roi = new ShapeRoi(path);
roi.setName(GRID);
roi.setStrokeWidth(0);
overlay.add(roi);
}
public int showDialog(ImagePlus imp, String command, PlugInFilterRunner pfr) {
this.pfr = pfr;
String macroOptions = Macro.getOptions();
if (macroOptions!=null) {
if (macroOptions.indexOf(" interpolate")!=-1)
macroOptions.replaceAll(" interpolate", " interpolation=Bilinear");
else if (macroOptions.indexOf(" interpolation=")==-1)
macroOptions = macroOptions+" interpolation=None";
Macro.setOptions(macroOptions);
}
gd = new GenericDialog("Rotate");
gd.addSlider("Angle:", -90, 90, angle, 0.1);
gd.addNumericField("Grid lines:", gridLines, 0);
gd.addChoice("Interpolation:", methods, methods[interpolationMethod]);
gd.addCheckbox("Fill with background color", fillWithBackground);
if (canEnlarge)
gd.addCheckbox("Enlarge image", enlarge);
else
enlarge = false;
gd.addPreviewCheckbox(pfr);
gd.addDialogListener(this);
gd.showDialog();
if (gd.wasCanceled()) {
if (overlay.size()>0) {
overlay.remove(GRID);
imp.setOverlay(overlay);
}
return DONE;
}
Overlay ovly = imp.getOverlay();
if (ovly!=null) {
ovly.remove(GRID);
if (ovly.size()==0) imp.setOverlay(null);
}
if (enlarge)
flags |= NO_CHANGES; else if (imp.getStackSize()==1)
flags |= KEEP_PREVIEW; done = true;
return IJ.setupDialog(imp, flags);
}
public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
angle = gd.getNextNumber();
if (gd.invalidNumber()) {
if (gd.wasOKed()) IJ.error("Angle is invalid.");
return false;
}
gridLines = (int)gd.getNextNumber();
interpolationMethod = gd.getNextChoiceIndex();
fillWithBackground = gd.getNextBoolean();
if (canEnlarge)
enlarge = gd.getNextBoolean();
return true;
}
public static double getAngle() {
return angle;
}
public void setNPasses(int nPasses) {
}
}