package ij.plugin;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.measure.*;
import ij.util.Tools;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
public class Grid implements PlugIn, DialogListener {
private static final String OPTIONS = "grid.options";
private static final String GRID = "|GRID|";
private static double crossSize = 0.1;
private static String[] colors = {"Red","Green","Blue","Magenta","Cyan","Yellow","Orange","Black","White"};
private final static int LINES=0, HLINES=1, CROSSES=2, POINTS=3, CIRCLES=4, NONE=4;
private static String[] types = {"Lines","Horizontal Lines", "Crosses", "Points", "Circles", "None"};
private Random random = new Random(System.currentTimeMillis());
private ImagePlus imp;
private double tileWidth, tileHeight;
private int xstart, ystart;
private int linesV, linesH;
private double pixelWidth=1.0, pixelHeight=1.0;
private String units = "pixels";
private boolean isMacro;
private Roi gridOnEntry;
private String type = types[LINES];
private double areaPerPoint;
private static double saveAreaPerPoint;
private String color = "Cyan";
private boolean bold;
private boolean randomOffset;
private boolean centered;
private Checkbox centerCheckbox, randomCheckbox;
public void run(String arg) {
imp = IJ.getImage();
Overlay overlay = imp.getOverlay();
int index = overlay!=null?overlay.getIndex(GRID):-1;
if (index>=0)
gridOnEntry = overlay.get(index);
if (showDialog() && !isMacro)
saveSettings();
}
private void drawCircles(double size) {
double R = size*tileWidth;
if (R<1) R =1;
if (bold && type.equals(types[POINTS])) R*=1.5;
double kappa = 0.5522847498f;
GeneralPath path = new GeneralPath();
for(int h=0; h<linesV; h++) {
for(int v=0; v<linesH; v++) {
double x = xstart+h*tileWidth;
double y = ystart+v*tileHeight;
path.moveTo(x, y-R);
path.curveTo(x+R*kappa, y-R, x+R, y-R*kappa, x+R, y);
path.curveTo(x+R, y+R*kappa, x+R*kappa, y+R, x, y+R );
path.curveTo(x-R*kappa, y+R, x-R, y+R*kappa, x-R, y);
path.curveTo(x-R, y-R*kappa, x-R*kappa, y-R, x, y-R );
path.closePath();
}
}
drawGrid(path);
}
private void drawCrosses() {
GeneralPath path = new GeneralPath();
double arm = crossSize*tileWidth;
if (arm<3) arm=3;
for(int h=0; h<linesV; h++) {
for(int v=0; v<linesH; v++) {
double x = xstart+h*tileWidth;
double y = ystart+v*tileHeight;
path.moveTo(x-arm, y);
path.lineTo(x+arm, y);
path.moveTo(x, y-arm);
path.lineTo(x, y+arm);
}
}
drawGrid(path);
}
private void drawLines() {
GeneralPath path = new GeneralPath();
int width = imp.getWidth();
int height = imp.getHeight();
for(int i=0; i<linesV; i++) {
float xoff = (float)(xstart+i*tileWidth);
path.moveTo(xoff,0f);
path.lineTo(xoff, height);
}
for(int i=0; i<linesH; i++) {
float yoff = (float)(ystart+i*tileHeight);
path.moveTo(0f, yoff);
path.lineTo(width, yoff);
}
drawGrid(path);
}
private void drawHorizontalLines() {
GeneralPath path = new GeneralPath();
int width = imp.getWidth();
int height = imp.getHeight();
for(int i=0; i<linesH; i++) {
float yoff = (float)(ystart+i*tileHeight);
path.moveTo(0f, yoff);
path.lineTo(width, yoff);
}
drawGrid(path);
}
void drawGrid(Shape shape) {
if (shape==null) {
Overlay overlay = imp.getOverlay();
if (overlay!=null) {
if (overlay.size()>1) {
overlay.remove(GRID);
imp.draw();
} else
imp.setOverlay(null);
}
} else {
Roi roi = new ShapeRoi(shape);
roi.setStrokeColor(Colors.getColor(color,Color.cyan));
if (bold && linesV*linesH<5000) {
ImageCanvas ic = imp.getCanvas();
double mag = ic!=null?ic.getMagnification():1.0;
double width = 2.0;
if (mag<1.0)
width = width/mag;
roi.setStrokeWidth(width);
}
IJ.showStatus(linesV*linesH+" nodes");
Overlay overlay = imp.getOverlay();
if (overlay!=null)
overlay.remove(GRID);
else
overlay = new Overlay();
overlay.add(roi, GRID);
imp.setOverlay(overlay);
}
}
private boolean showDialog() {
isMacro = Macro.getOptions()!=null;
if (!isMacro)
getSettings();
int width = imp.getWidth();
int height = imp.getHeight();
Calibration cal = imp.getCalibration();
int places;
if (cal.scaled()) {
pixelWidth = cal.pixelWidth;
pixelHeight = cal.pixelHeight;
units = cal.getUnits();
places = 2;
} else {
pixelWidth = 1.0;
pixelHeight = 1.0;
units = "pixels";
places = 0;
}
if (areaPerPoint==0.0)
areaPerPoint = (width*cal.pixelWidth*height*cal.pixelHeight)/81.0; GenericDialog gd = new GenericDialog("Grid...");
gd.addChoice("Grid type:", types, type);
gd.addNumericField("Area per point:", areaPerPoint, places, 6, units+"^2");
gd.addChoice("Color:", colors, color);
gd.addCheckbox("Bold", bold);
gd.addCheckbox("Center grid on image", centered);
gd.addCheckbox("Random offset", randomOffset);
gd.addDialogListener(this);
if (!isMacro) {
Vector v = gd.getCheckboxes();
centerCheckbox = (Checkbox)v.elementAt(1);
randomCheckbox = (Checkbox)v.elementAt(2);
}
dialogItemChanged(gd, null);
gd.showDialog();
if (gd.wasCanceled()) {
Overlay overlay = imp.getOverlay();
if (overlay!=null && gridOnEntry!=null) {
overlay.remove(GRID);
overlay.add(gridOnEntry);
imp.draw();
} else
drawGrid(null);
return false;
} else
return true;
}
public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
int width = imp.getWidth();
int height = imp.getHeight();
type = gd.getNextChoice();
areaPerPoint = gd.getNextNumber();
color = gd.getNextChoice();
bold = gd.getNextBoolean();
centered = gd.getNextBoolean();
randomOffset = gd.getNextBoolean();
if (randomOffset) {
centered = false;
if (centerCheckbox!=null)
centerCheckbox.setState(false);
}
double minArea= (width*height)/50000.0;
if (type.equals(types[CROSSES])&&minArea<50.0)
minArea = 50.0;
else if (minArea<16)
minArea = 16.0;
if (areaPerPoint/(pixelWidth*pixelHeight)<minArea) {
String err = "\"Area per Point\" too small";
if (gd.wasOKed())
IJ.error("Grid", err);
else
IJ.showStatus(err);
return true;
}
double tileSize = Math.sqrt(areaPerPoint);
tileWidth = tileSize/pixelWidth;
tileHeight = tileSize/pixelHeight;
if (centered) {
xstart = (int)Math.round((width%tileWidth)/2.0);
ystart = (int)Math.round((height%tileHeight)/2.0);
} else if (randomOffset) {
xstart = (int)(random.nextDouble()*tileWidth);
ystart = (int)(random.nextDouble()*tileHeight);
} else {
xstart = (int)(tileWidth/2.0+0.5);
ystart = (int)(tileHeight/2.0+0.5);
}
linesV = (int)((width-xstart)/tileWidth)+1;
linesH = (int)((height-ystart)/tileHeight)+1;
if (gd.invalidNumber())
return true;
drawGrid();
return true;
}
private void drawGrid() {
if (type.equals(types[LINES]))
drawLines();
else if (type.equals(types[HLINES]))
drawHorizontalLines();
else if (type.equals(types[CROSSES]))
drawCrosses();
else if (type.equals(types[POINTS]))
drawCircles(0.01);
else if (type.equals(types[CIRCLES]))
drawCircles(0.1);
else
drawGrid(null);
}
private void getSettings() {
String prefs = Prefs.get(OPTIONS, "Lines,Cyan,-");
String[] options = Tools.split(prefs, ",");
if (options.length>=3) {
type = options[0];
if ("None".equals(type))
type = types[LINES];
areaPerPoint = saveAreaPerPoint;
color = options[1];
bold = options[2].contains("bold");
centered = options[2].contains("centered");
randomOffset = options[2].contains("random");
if (centered)
randomOffset = false;
}
}
private void saveSettings() {
String options = type+","+color+",";
String options2 = (bold?"bold ":"")+(centered?"centered ":"")+(randomOffset?"random ":"");
if (options2.length()==0)
options2 = "-";
Prefs.set(OPTIONS, options+options2);
saveAreaPerPoint = areaPerPoint;
}
}