
ij.gui.Line Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ij Show documentation
Show all versions of ij Show documentation
ImageJ is an open source Java image processing program inspired by NIH Image for the Macintosh.
package ij.gui;
import ij.*;
import ij.process.*;
import ij.measure.*;
import ij.plugin.Straightener;
import ij.plugin.frame.Recorder;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
/** This class represents a straight line selection. */
public class Line extends Roi {
public int x1, y1, x2, y2; // the line
public double x1d, y1d, x2d, y2d; // the line using sub-pixel coordinates
protected double x1R, y1R, x2R, y2R; // the line, relative to base of bounding rect
private double xHandleOffset, yHandleOffset;
protected double startxd, startyd;
static boolean widthChanged;
private boolean drawOffset;
private boolean dragged;
private int mouseUpCount;
/** Creates a new straight line selection using the specified
starting and ending offscreen integer coordinates. */
public Line(int ox1, int oy1, int ox2, int oy2) {
this((double)ox1, (double)oy1, (double)ox2, (double)oy2);
}
/** Creates a new straight line selection using the specified
starting and ending offscreen double coordinates. */
public Line(double ox1, double oy1, double ox2, double oy2) {
super((int)ox1, (int)oy1, 0, 0);
type = LINE;
x1d=ox1; y1d=oy1; x2d=ox2; y2d=oy2;
x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d;
x=(int)Math.min(x1d,x2d); y=(int)Math.min(y1d,y2d);
x1R=x1d-x; y1R=y1d-y; x2R=x2d-x; y2R=y2d-y;
width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R);
if (!(this instanceof Arrow) && lineWidth>1)
updateWideLine(lineWidth);
updateClipRect();
oldX=x; oldY=y; oldWidth=width; oldHeight=height;
state = NORMAL;
}
/** Starts the process of creating a new user-generated straight line
selection. 'sx' and 'sy' are screen coordinates that specify
the start of the line. The user will determine the end of the line
interactively using rubber banding. */
public Line(int sx, int sy, ImagePlus imp) {
super(sx, sy, imp);
startxd = ic.offScreenXD(sx);
startyd = ic.offScreenYD(sy);
x1R = x2R = startxd - startX;
y1R = y2R = startyd - startY;
type = LINE;
if (!(this instanceof Arrow) && lineWidth>1)
updateWideLine(lineWidth);
drawOffset = Prefs.subPixelResolution;
}
/**
* @deprecated
* replaced by Line(int, int, int, int)
*/
public Line(int ox1, int oy1, int ox2, int oy2, ImagePlus imp) {
this(ox1, oy1, ox2, oy2);
setImage(imp);
}
protected void grow(int sx, int sy) { //mouseDragged
drawLine(sx, sy);
dragged = true;
}
public void mouseMoved(MouseEvent e) {
drawLine(e.getX(), e.getY());
}
protected void handleMouseUp(int screenX, int screenY) {
mouseUpCount++;
if (Prefs.enhancedLineTool && mouseUpCount==1 && !dragged)
return;
state = NORMAL;
if (imp==null) return;
imp.draw(clipX-5, clipY-5, clipWidth+10, clipHeight+10);
if (Recorder.record) {
String method = (this instanceof Arrow)?"makeArrow":"makeLine";
Recorder.record(method, x1, y1, x2, y2);
}
if (getLength()==0.0)
imp.deleteRoi();
}
protected void drawLine(int sx, int sy) {
double xend = ic!=null?ic.offScreenXD(sx):sx;
double yend = ic!=null?ic.offScreenYD(sy):sy;
if (xend<0.0) xend=0.0; if (yend<0.0) yend=0.0;
if (xend>xMax) xend=xMax; if (yend>yMax) yend=yMax;
double xstart=x+x1R, ystart=y+y1R;
if (constrain) {
int i=0;
double dy = Math.abs(yend-ystart);
double dx = Math.abs(xend-xstart);
double comp = dy / dx;
for(;i ystart) {
yend = ystart + dx*PI_MULT[i];
} else {
yend = ystart - dx*PI_MULT[i];
}
} else {
xend = xstart;
}
}
x=(int)Math.min(x+x1R,xend); y=(int)Math.min(y+y1R,yend);
x1R=xstart-x; y1R=ystart-y;
x2R=xend-x; y2R=yend-y;
if (IJ.controlKeyDown()) {
x1R=(int)Math.round(x1R); y1R=(int)Math.round(y1R);
x2R=(int)Math.round(x2R); y2R=(int)Math.round(y2R);
}
width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R);
if (width<1) width=1; if (height<1) height=1;
updateClipRect();
imp.draw(clipX, clipY, clipWidth, clipHeight);
oldX=x; oldY=y;
oldWidth=width; oldHeight=height;
}
/** Used for angle searches in line ROI creation */
private static final double[] PI_SEARCH = {Math.tan(Math.PI/8), Math.tan((3*Math.PI)/8)};
private static final double[] PI_MULT = {0, Math.tan((2*Math.PI)/8)};
void move(int sx, int sy) {
int xNew = ic.offScreenX(sx);
int yNew = ic.offScreenY(sy);
x += xNew - startxd;
y += yNew - startyd;
clipboard=null;
startxd = xNew;
startyd = yNew;
updateClipRect();
if (ignoreClipRect)
imp.draw();
else
imp.draw(clipX, clipY, clipWidth, clipHeight);
oldX = x;
oldY = y;
oldWidth = width;
oldHeight=height;
}
protected void moveHandle(int sx, int sy) {
double offset = getOffset(-0.5);
double ox = ic.offScreenXD(sx)+offset;
double oy = ic.offScreenYD(sy)+offset;
x1d=x+x1R; y1d=y+y1R; x2d=x+x2R; y2d=y+y2R;
double length = Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d));
switch (activeHandle) {
case 0:
double dx = ox-x1d;
double dy = oy-y1d;
x1d=ox;
y1d=oy;
if(center){
x2d -= dx;
y2d -= dy;
}
if(aspect){
double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d)));
double xcd = x1d+(x2d-x1d)/2;
double ycd = y1d+(y2d-y1d)/2;
if(center){
x1d=xcd-ratio*(xcd-x1d);
x2d=xcd+ratio*(x2d-xcd);
y1d=ycd-ratio*(ycd-y1d);
y2d=ycd+ratio*(y2d-ycd);
} else {
x1d=x2d-ratio*(x2d-x1d);
y1d=y2d-ratio*(y2d-y1d);
}
}
break;
case 1:
dx = ox-x2d;
dy = oy-y2d;
x2d=ox;
y2d=oy;
if(center){
x1d -= dx;
y1d -= dy;
}
if(aspect){
double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d)));
double xcd = x1d+(x2d-x1d)/2;
double ycd = y1d+(y2d-y1d)/2;
if(center){
x1d=xcd-ratio*(xcd-x1d);
x2d=xcd+ratio*(x2d-xcd);
y1d=ycd-ratio*(ycd-y1d);
y2d=ycd+ratio*(y2d-ycd);
} else {
x2d=x1d+ratio*(x2d-x1d);
y2d=y1d+ratio*(y2d-y1d);
}
}
break;
case 2:
dx = ox-(x1d+(x2d-x1d)/2);
dy = oy-(y1d+(y2d-y1d)/2);
x1d+=dx; y1d+=dy; x2d+=dx; y2d+=dy;
if (getStrokeWidth()>1) {
x1d+=xHandleOffset; y1d+=yHandleOffset;
x2d+=xHandleOffset; y2d+=yHandleOffset;
}
break;
}
if (constrain) {
double dx = Math.abs(x1d-x2d);
double dy = Math.abs(y1d-y2d);
double xcd = Math.min(x1d,x2d)+dx/2;
double ycd = Math.min(y1d,y2d)+dy/2;
//double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d)));
if (activeHandle==0) {
if (dx>=dy) {
if(aspect){
if(x2d>x1d) x1d=x2d-length;
else x1d=x2d+length;
}
y1d = y2d;
if(center){
y1d=y2d=ycd;
if(aspect){
if(xcd>x1d) {
x1d=xcd-length/2;
x2d=xcd+length/2;
}
else{
x1d=xcd+length/2;
x2d=xcd-length/2;
}
}
}
}else {
if(aspect){
if(y2d>y1d) y1d=y2d-length;
else y1d=y2d+length;
}
x1d = x2d;
if(center){
x1d=x2d=xcd;
if(aspect){
if(ycd>y1d) {
y1d=ycd-length/2;
y2d=ycd+length/2;
}
else{
y1d=ycd+length/2;
y2d=ycd-length/2;
}
}
}
}
} else if (activeHandle==1) {
if (dx>=dy) {
if(aspect){
if(x1d>x2d) x2d=x1d-length;
else x2d=x1d+length;
}
y2d= y1d;
if(center){
y1d=y2d=ycd;
if(aspect){
if(xcd>x1d) {
x1d=xcd-length/2;
x2d=xcd+length/2;
}
else{
x1d=xcd+length/2;
x2d=xcd-length/2;
}
}
}
} else {
if(aspect){
if(y1d>y2d) y2d=y1d-length;
else y2d=y1d+length;
}
x2d = x1d;
if(center){
x1d=x2d=xcd;
if(aspect){
if(ycd>y1d) {
y1d=ycd-length/2;
y2d=ycd+length/2;
}
else{
y1d=ycd+length/2;
y2d=ycd-length/2;
}
}
}
}
}
}
x=(int)Math.min(x1d,x2d); y=(int)Math.min(y1d,y2d);
x1R=x1d-x; y1R=y1d-y;
x2R=x2d-x; y2R=y2d-y;
width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R);
updateClipRect();
imp.draw(clipX, clipY, clipWidth, clipHeight);
oldX = x;
oldY = y;
oldWidth = width;
oldHeight = height;
}
protected void mouseDownInHandle(int handle, int sx, int sy) {
state = MOVING_HANDLE;
activeHandle = handle;
if (getStrokeWidth()<=3)
ic.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
}
/** Draws this line on the image. */
public void draw(Graphics g) {
Color color = strokeColor!=null? strokeColor:ROIColor;
boolean isActiveOverlayRoi = !overlay && isActiveOverlayRoi();
if (isActiveOverlayRoi) {
if (color==Color.cyan)
color = Color.magenta;
else
color = Color.cyan;
}
double x = getXBase();
double y = getYBase();
g.setColor(color);
x1d=x+x1R; y1d=y+y1R; x2d=x+x2R; y2d=y+y2R;
x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d;
double offset = getOffset(0.5);
int sx1 = screenXD(x1d+offset);
int sy1 = screenYD(y1d+offset);
int sx2 = screenXD(x2d+offset);
int sy2 = screenYD(y2d+offset);
int sx3 = sx1 + (sx2-sx1)/2;
int sy3 = sy1 + (sy2-sy1)/2;
Graphics2D g2d = (Graphics2D)g;
if (stroke!=null && !isActiveOverlayRoi)
g2d.setStroke(getScaledStroke());
g.drawLine(sx1, sy1, sx2, sy2);
if (wideLine && !overlay) {
g2d.setStroke(onePixelWide);
g.setColor(getColor());
g.drawLine(sx1, sy1, sx2, sy2);
}
if (!overlay) {
int size2 = HANDLE_SIZE/2;
mag = getMagnification();
handleColor = strokeColor!=null?strokeColor:ROIColor;
drawHandle(g, sx1-size2, sy1-size2);
handleColor=Color.white;
drawHandle(g, sx2-size2, sy2-size2);
drawHandle(g, sx3-size2, sy3-size2);
}
if (state!=NORMAL)
IJ.showStatus(imp.getLocationAsString(x2,y2)+", angle=" + IJ.d2s(getAngle()) + ", length=" + IJ.d2s(getLength()));
if (updateFullWindow)
{updateFullWindow = false; imp.draw();}
}
public double getAngle() {
return getFloatAngle(x1d, y1d, x2d, y2d);
}
/** Returns the length of this line. */
public double getLength() {
if (imp==null || IJ.altKeyDown())
return getRawLength();
else {
Calibration cal = imp.getCalibration();
return Math.sqrt((x2d-x1d)*cal.pixelWidth*(x2d-x1d)*cal.pixelWidth
+ (y2d-y1d)*cal.pixelHeight*(y2d-y1d)*cal.pixelHeight);
}
}
/** Returns the length of this line in pixels. */
public double getRawLength() {
return Math.sqrt((x2d-x1d)*(x2d-x1d)+(y2d-y1d)*(y2d-y1d));
}
/** Returns the pixel values along this line. */
public double[] getPixels() {
double[] profile;
if (getStrokeWidth()<=1) {
ImageProcessor ip = imp.getProcessor();
profile = ip.getLine(x1d, y1d, x2d, y2d);
} else {
ImageProcessor ip2 = (new Straightener()).rotateLine(imp,(int)getStrokeWidth());
if (ip2==null) return new double[0];
int width = ip2.getWidth();
int height = ip2.getHeight();
profile = new double[width];
double[] aLine;
ip2.setInterpolate(false);
for (int y=0; y0.0) {
FloatPolygon fp = getFloatPolygon();
for (int i=0; i1) {
if ((x==x1&&y==y1) || (x==x2&&y==y2))
return true;
else
return getPolygon().contains(x,y);
} else
return false;
}
protected void handleMouseDown(int sx, int sy) {
super.handleMouseDown(sx, sy);
startxd = ic.offScreenXD(sx);
startyd = ic.offScreenYD(sy);
}
/** Returns a handle number if the specified screen coordinates are
inside or near a handle, otherwise returns -1. */
public int isHandle(int sx, int sy) {
int size = HANDLE_SIZE+5;
if (getStrokeWidth()>1) size += (int)Math.log(getStrokeWidth());
int halfSize = size/2;
double offset = getOffset(0.5);
int sx1 = ic.screenXD(x+x1R+offset) - halfSize;
int sy1 = ic.screenYD(y+y1R+offset) - halfSize;
int sx2 = ic.screenXD(x+x2R+offset) - halfSize;
int sy2 = ic.screenYD(y+y2R+offset) - halfSize;
int sx3 = sx1 + (sx2-sx1)/2-1;
int sy3 = sy1 + (sy2-sy1)/2-1;
if (sx>=sx1&&sx<=sx1+size&&sy>=sy1&&sy<=sy1+size) return 0;
if (sx>=sx2&&sx<=sx2+size&&sy>=sy2&&sy<=sy2+size) return 1;
if (sx>=sx3&&sx<=sx3+size+2&&sy>=sy3&&sy<=sy3+size+2) return 2;
return -1;
}
private double getOffset(double value) {
return getDrawOffset()&&getMagnification()>1.0&&!(this instanceof Arrow)?value:0.0;
}
public static int getWidth() {
return lineWidth;
}
public static void setWidth(int w) {
if (w<1) w = 1;
int max = 500;
if (w>max) {
ImagePlus imp2 = WindowManager.getCurrentImage();
if (imp2!=null) {
max = Math.max(max, imp2.getWidth());
max = Math.max(max, imp2.getHeight());
}
if (w>max) w = max;
}
lineWidth = w;
widthChanged = true;
}
public void setStrokeWidth(float width) {
super.setStrokeWidth(width);
if (getStrokeColor()==Roi.getColor())
wideLine = true;
}
/** Return the bounding rectangle of this line. */
public Rectangle getBounds() {
int xmin = (int)Math.round(Math.min(x1d, x2d));
int ymin = (int)Math.round(Math.min(y1d, y2d));
int w = (int)Math.round(Math.abs(x2d - x1d));
int h = (int)Math.round(Math.abs(y2d - y1d));
return new Rectangle(xmin, ymin, w, h);
}
protected int clipRectMargin() {
return 4;
}
/** Nudge end point of line by one pixel. */
public void nudgeCorner(int key) {
if (ic==null) return;
double inc = 1.0/ic.getMagnification();
switch(key) {
case KeyEvent.VK_UP: y2R-=inc; break;
case KeyEvent.VK_DOWN: y2R+=inc; break;
case KeyEvent.VK_LEFT: x2R-=inc; break;
case KeyEvent.VK_RIGHT: x2R+=inc; break;
}
grow(ic.screenXD(x+x2R), ic.screenYD(y+y2R));
}
public boolean getDrawOffset() {
return drawOffset;
}
public void setDrawOffset(boolean drawOffset) {
this.drawOffset = drawOffset;
}
/** Always returns true. */
public boolean subPixelResolution() {
return true;
}
public void setLocation(int x, int y) {
super.setLocation(x, y);
double xx = getXBase();
double yy = getYBase();
x1d=xx+x1R; y1d=yy+y1R; x2d=xx+x2R; y2d=yy+y2R;
x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy