Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
Roi.java
1 package ij.gui;
2 
3 import java.awt.*;
4 import java.awt.image.*;
5 import java.awt.event.KeyEvent;
6 import ij.*;
7 import ij.process.*;
8 import ij.measure.*;
9 import ij.plugin.frame.Recorder;
10 import ij.plugin.filter.Analyzer;
11 
13 public class Roi extends Object implements Cloneable, java.io.Serializable {
14 
15  public static final int CONSTRUCTING=0, MOVING=1, RESIZING=2, NORMAL=3, MOVING_HANDLE=4; // States
16  public static final int RECTANGLE=0, OVAL=1, POLYGON=2, FREEROI=3, TRACED_ROI=4, LINE=5,
17  POLYLINE=6, FREELINE=7, ANGLE=8, COMPOSITE=9; // Types
18  public static final int HANDLE_SIZE = 5;
19  public static final int NOT_PASTING = -1;
20 
21  int startX, startY, x, y, width, height;
22  int activeHandle;
23  int state;
24 
25  public static Roi previousRoi;
26  protected static Color ROIColor = Prefs.getColor(Prefs.ROICOLOR,Color.yellow);
27  protected static int pasteMode = Blitter.COPY;
28 
29  protected int type;
30  protected int xMax, yMax;
31  protected ImagePlus imp;
32  protected ImageCanvas ic;
33  protected int oldX, oldY, oldWidth, oldHeight;
34  protected int clipX, clipY, clipWidth, clipHeight;
35  protected ImagePlus clipboard;
36  protected boolean constrain; // to be square
37  protected boolean center;
38  protected boolean updateFullWindow;
39  protected double mag = 1.0;
40  protected String name;
41  protected ImageProcessor cachedMask;
42 
44  public Roi(int x, int y, int width, int height) {
45  setImage(null);
46  if (width<1) width = 1;
47  if (height<1) height = 1;
48  if (width>xMax) width = xMax;
49  if (height>yMax) height = yMax;
50  //setLocation(x, y);
51  this.x = x;
52  this.y = y;
53  startX = x; startY = y;
54  oldX = x; oldY = y; oldWidth=0; oldHeight=0;
55  this.width = width;
56  this.height = height;
57  oldWidth=width;
58  oldHeight=height;
59  clipX = x;
60  clipY = y;
61  clipWidth = width;
62  clipHeight = height;
63  state = NORMAL;
64  type = RECTANGLE;
65  if (ic!=null) {
66  Graphics g = ic.getGraphics();
67  draw(g);
68  g.dispose();
69  }
70  }
71 
73  public Roi(int x, int y, ImagePlus imp) {
74  setImage(imp);
75  setLocation(x, y);
76  width = 0;
77  height = 0;
78  state = CONSTRUCTING;
79  type = RECTANGLE;
80  }
81 
83  public Roi(int x, int y, int width, int height, ImagePlus imp) {
84  this(x, y, width, height);
85  setImage(imp);
86  }
87 
88  public void setLocation(int x, int y) {
89  if (x<0) x = 0;
90  if (y<0) y = 0;
91  if ((x+width)>xMax) x = xMax-width;
92  if ((y+height)>yMax) y = yMax-height;
93  //IJ.write(imp.getTitle() + ": Roi.setlocation(" + x + "," + y + ")");
94  this.x = x;
95  this.y = y;
96  startX = x; startY = y;
97  oldX = x; oldY = y; oldWidth=0; oldHeight=0;
98  }
99 
100  public void setImage(ImagePlus imp) {
101  this.imp = imp;
102  cachedMask = null;
103  if (imp==null) {
104  ic = null;
105  clipboard = null;
106  xMax = 99999;
107  yMax = 99999;
108  } else {
109  ic = imp.getCanvas();
110  xMax = imp.getWidth();
111  yMax = imp.getHeight();
112  }
113  }
114 
115  public int getType() {
116  return type;
117  }
118 
119  public int getState() {
120  return state;
121  }
122 
124  public double getLength() {
125  double pw=1.0, ph=1.0;
126  if (imp!=null) {
127  Calibration cal = imp.getCalibration();
128  pw = cal.pixelWidth;
129  ph = cal.pixelHeight;
130  }
131  return 2.0*width*pw+2.0*height*ph;
132  }
133 
136  public double getFeretsDiameter() {
137  double pw=1.0, ph=1.0;
138  if (imp!=null) {
139  Calibration cal = imp.getCalibration();
140  pw = cal.pixelWidth;
141  ph = cal.pixelHeight;
142  }
143  return Math.sqrt(width*width*pw*pw+height*height*ph*ph);
144  }
145 
147  public Rectangle getBounds() {
148  return new Rectangle(x, y, width, height);
149  }
150 
152  public Rectangle getBoundingRect() {
153  return getBounds();
154  }
155 
162  public Polygon getPolygon() {
163  int[] xpoints = new int[4];
164  int[] ypoints = new int[4];
165  xpoints[0] = x;
166  ypoints[0] = y;
167  xpoints[1] = x+width;
168  ypoints[1] = y;
169  xpoints[2] = x+width;
170  ypoints[2] = y+height;
171  xpoints[3] = x;
172  ypoints[3] = y+height;
173  return new Polygon(xpoints, ypoints, 4);
174  }
175 
178  public synchronized Object clone() {
179  try {
180  Roi r = (Roi)super.clone();
181  r.setImage(null);
182  return r;
183  }
184  catch (CloneNotSupportedException e) {return null;}
185  }
186 
187  protected void grow(int xNew, int yNew) {
188  if (clipboard!=null)
189  return;
190  if (xNew < 0) xNew = 0;
191  if (yNew < 0) yNew = 0;
192  if (constrain) {
193  // constrain selection to be square
194  int dx, dy, d;
195  dx = xNew - x;
196  dy = yNew - y;
197  if (dx<dy)
198  d = dx;
199  else
200  d = dy;
201  xNew = x + d;
202  yNew = y + d;
203  }
204 
205  if (center) {
206  width = Math.abs(xNew - startX)*2;
207  height = Math.abs(yNew - startY)*2;
208  x = startX - width/2;
209  y = startY - height/2;
210  } else {
211  width = Math.abs(xNew - startX);
212  height = Math.abs(yNew - startY);
213  x = (xNew>=startX)?startX:startX - width;
214  y = (yNew>=startY)?startY:startY - height;
215  if ((x+width) > xMax)
216  width = xMax-x;
217  if ((y+height) > yMax)
218  height = yMax-y;
219  }
220  updateClipRect();
221  imp.draw(clipX, clipY, clipWidth, clipHeight);
222  oldX = x;
223  oldY = y;
224  oldWidth = width;
225  oldHeight = height;
226  }
227 
228  protected void moveHandle(int ox, int oy) {
229  if (clipboard!=null)
230  return;
231  if (ox<0) ox=0; if (oy<0) oy=0;
232  if (ox>xMax) ox=xMax; if (oy>yMax) oy=yMax;
233  //IJ.log("moveHandle: "+activeHandle+" "+ox+" "+oy);
234  int x1=x, y1=y, x2=x1+width, y2=y+height;
235  switch (activeHandle) {
236  case 0: x=ox; y=oy; break;
237  case 1: y=oy; break;
238  case 2: x2=ox; y=oy; break;
239  case 3: x2=ox; break;
240  case 4: x2=ox; y2=oy; break;
241  case 5: y2=oy; break;
242  case 6: x=ox; y2=oy; break;
243  case 7: x=ox; break;
244  }
245  if (x<x2)
246  width=x2-x;
247  else
248  {width=1; x=x2;}
249  if (y<y2)
250  height = y2-y;
251  else
252  {height=1; y=y2;}
253  if (constrain)
254  height = width;
255  updateClipRect();
256  imp.draw(clipX, clipY, clipWidth, clipHeight);
257  oldX=x; oldY=y;
258  oldWidth=width; oldHeight=height;
259  }
260 
261  void move(int xNew, int yNew) {
262  x += xNew - startX;
263  y += yNew - startY;
264  if (type!=RECTANGLE || clipboard==null) {
265  if (x<0) x=0; if (y<0) y=0;
266  if ((x+width)>xMax) x = xMax-width;
267  if ((y+height)>yMax) y = yMax-height;
268  }
269  startX = xNew;
270  startY = yNew;
271  updateClipRect();
272  imp.draw(clipX, clipY, clipWidth, clipHeight);
273  oldX = x;
274  oldY = y;
275  oldWidth = width;
276  oldHeight=height;
277  }
278 
280  public void nudge(int key) {
281  switch(key) {
282  case KeyEvent.VK_UP:
283  y--;
284  if (y<0 && (type!=RECTANGLE||clipboard==null))
285  y = 0;
286  break;
287  case KeyEvent.VK_DOWN:
288  y++;
289  if ((y+height)>=yMax && (type!=RECTANGLE||clipboard==null))
290  y = yMax-height;
291  break;
292  case KeyEvent.VK_LEFT:
293  x--;
294  if (x<0 && (type!=RECTANGLE||clipboard==null))
295  x = 0;
296  break;
297  case KeyEvent.VK_RIGHT:
298  x++;
299  if ((x+width)>=xMax && (type!=RECTANGLE||clipboard==null))
300  x = xMax-width;
301  break;
302  }
303  updateClipRect();
304  imp.draw(clipX, clipY, clipWidth, clipHeight);
305  oldX = x; oldY = y;
306  showStatus();
307  }
308 
311  public void nudgeCorner(int key) {
312  if (type>OVAL || clipboard!=null)
313  return;
314  switch(key) {
315  case KeyEvent.VK_UP:
316  height--;
317  if (height<1) height = 1;
318  break;
319  case KeyEvent.VK_DOWN:
320  height++;
321  if ((y+height) > yMax) height = yMax-y;
322  break;
323  case KeyEvent.VK_LEFT:
324  width--;
325  if (width<1) width = 1;
326  break;
327  case KeyEvent.VK_RIGHT:
328  width++;
329  if ((x+width) > xMax) width = xMax-x;
330  break;
331  }
332  updateClipRect();
333  imp.draw(clipX, clipY, clipWidth, clipHeight);
334  oldX = x; oldY = y;
335  cachedMask = null;
336  showStatus();
337  }
338 
339  protected void updateClipRect() {
340  // Finds the union of current and previous roi
341  clipX = (x<=oldX)?x:oldX;
342  clipY = (y<=oldY)?y:oldY;
343  clipWidth = ((x+width>=oldX+oldWidth)?x+width:oldX+oldWidth) - clipX + 1;
344  clipHeight = ((y+height>=oldY+oldHeight)?y+height:oldY+oldHeight) - clipY + 1;
345  int m = 3;
346  if (ic!=null) {
347  double mag = ic.getMagnification();
348  if (mag<1.0)
349  m = (int)(3/mag);
350  }
351  clipX-=m; clipY-=m;
352  clipWidth+=m*2; clipHeight+=m*2;
353  }
354 
355  protected void handleMouseDrag(int sx, int sy, int flags) {
356  if (ic==null) return;
357  constrain = (flags&Event.SHIFT_MASK)!=0;
358  center = (flags&Event.CTRL_MASK)!=0 || (IJ.isMacintosh()&&(flags&Event.META_MASK)!=0);
359  int ox = ic.offScreenX(sx);
360  int oy = ic.offScreenY(sy);
361  switch(state) {
362  case CONSTRUCTING:
363  grow(ox, oy);
364  break;
365  case MOVING:
366  move(ox, oy);
367  break;
368  case MOVING_HANDLE:
369  moveHandle(ox, oy);
370  break;
371  default:
372  break;
373  }
374  }
375 
376  int getHandleSize() {
377  double mag = ic!=null?ic.getMagnification():1.0;
378  double size = HANDLE_SIZE/mag;
379  return (int)(size*mag);
380  }
381 
382  public void draw(Graphics g) {
383  if (ic==null) return;
384  g.setColor(ROIColor);
385  mag = ic.getMagnification();
386  int sw = (int)(width*mag);
387  int sh = (int)(height*mag);
388  int sx1 = ic.screenX(x);
389  int sy1 = ic.screenY(y);
390  int sx2 = sx1+sw/2;
391  int sy2 = sy1+sh/2;
392  int sx3 = sx1+sw;
393  int sy3 = sy1+sh;
394  g.drawRect(sx1, sy1, sw, sh);
395  if (state!=CONSTRUCTING && clipboard==null) {
396  int size2 = HANDLE_SIZE/2;
397  drawHandle(g, sx1-size2, sy1-size2);
398  drawHandle(g, sx2-size2, sy1-size2);
399  drawHandle(g, sx3-size2, sy1-size2);
400  drawHandle(g, sx3-size2, sy2-size2);
401  drawHandle(g, sx3-size2, sy3-size2);
402  drawHandle(g, sx2-size2, sy3-size2);
403  drawHandle(g, sx1-size2, sy3-size2);
404  drawHandle(g, sx1-size2, sy2-size2);
405  }
406  drawPreviousRoi(g);
407  if (state!=NORMAL) showStatus();
408  if (updateFullWindow)
409  {updateFullWindow = false; imp.draw();}
410  }
411 
412  void drawPreviousRoi(Graphics g) {
413  if (previousRoi!=null && previousRoi!=this && ic.roiModState!=ImageCanvas.NO_MODS) {
414  previousRoi.setImage(imp);
415  previousRoi.draw(g);
416  }
417  }
418 
419  void drawHandle(Graphics g, int x, int y) {
420  double size = (width*height)*mag;
421  if (type==LINE) {
422  size = Math.sqrt(width*width+height*height);
423  size *= size*mag;
424  }
425  if (size>6000.0) {
426  g.setColor(Color.black);
427  g.fillRect(x,y,5,5);
428  g.setColor(Color.white);
429  g.fillRect(x+1,y+1,3,3);
430  } else if (size>1500.0) {
431  g.setColor(Color.black);
432  g.fillRect(x+1,y+1,4,4);
433  g.setColor(Color.white);
434  g.fillRect(x+2,y+2,2,2);
435  } else {
436  g.setColor(Color.black);
437  g.fillRect(x+1,y+1,3,3);
438  g.setColor(Color.white);
439  g.fillRect(x+2,y+2,1,1);
440  }
441  }
442 
443  public void drawPixels() {
444  endPaste();
445  imp.getProcessor().drawRect(x, y, width, height);
446  if (Line.getWidth()>1)
447  updateFullWindow = true;
448  }
449 
450  public boolean contains(int x, int y) {
451  Rectangle r = new Rectangle(this.x, this.y, width, height);
452  return r.contains(x, y);
453  }
454 
457  public int isHandle(int sx, int sy) {
458  if (clipboard!=null) return -1;
459  double mag = ic.getMagnification();
460  int size = HANDLE_SIZE+3;
461  int halfSize = size/2;
462  int sx1 = ic.screenX(x) - halfSize;
463  int sy1 = ic.screenY(y) - halfSize;
464  int sx3 = ic.screenX(x+width) - halfSize;
465  int sy3 = ic.screenY(y+height) - halfSize;
466  int sx2 = sx1 + (sx3 - sx1)/2;
467  int sy2 = sy1 + (sy3 - sy1)/2;
468  if (sx>=sx1&&sx<=sx1+size&&sy>=sy1&&sy<=sy1+size) return 0;
469  if (sx>=sx2&&sx<=sx2+size&&sy>=sy1&&sy<=sy1+size) return 1;
470  if (sx>=sx3&&sx<=sx3+size&&sy>=sy1&&sy<=sy1+size) return 2;
471  if (sx>=sx3&&sx<=sx3+size&&sy>=sy2&&sy<=sy2+size) return 3;
472  if (sx>=sx3&&sx<=sx3+size&&sy>=sy3&&sy<=sy3+size) return 4;
473  if (sx>=sx2&&sx<=sx2+size&&sy>=sy3&&sy<=sy3+size) return 5;
474  if (sx>=sx1&&sx<=sx1+size&&sy>=sy3&&sy<=sy3+size) return 6;
475  if (sx>=sx1&&sx<=sx1+size&&sy>=sy2&&sy<=sy2+size) return 7;
476  return -1;
477  }
478 
479  protected void mouseDownInHandle(int handle, int sx, int sy) {
480  state = MOVING_HANDLE;
481  activeHandle = handle;
482  }
483 
484  protected void handleMouseDown(int sx, int sy) {
485  if (state==NORMAL) {
486  state = MOVING;
487  startX = ic.offScreenX(sx);
488  startY = ic.offScreenY(sy);
489  showStatus();
490  }
491  }
492 
493  protected void handleMouseUp(int screenX, int screenY) {
494  state = NORMAL;
495  imp.draw(clipX-5, clipY-5, clipWidth+10, clipHeight+10);
496  if (Recorder.record) {
497  String method;
498  if (type==LINE) {
499  if (imp==null) return;
500  Line line = (Line)imp.getRoi();
501  Recorder.record("makeLine", line.x1, line.y1, line.x2, line.y2);
502  } else if (type==OVAL)
503  Recorder.record("makeOval", x, y, width, height);
504  else
505  Recorder.record("makeRectangle", x, y, width, height);
506  }
507  modifyRoi();
508  }
509 
510  public void modifyRoi() {
511  //IJ.log("modifyRoi: "+ic+(ic!=null?""+ic.roiModState:"")+" "+previousRoi);
512  if (ic==null || ic.roiModState==ImageCanvas.NO_MODS || previousRoi==null)
513  return;
514  ShapeRoi s1 = null;
515  ShapeRoi s2 = null;
516  if (previousRoi instanceof ShapeRoi)
517  s1 = (ShapeRoi)previousRoi;
518  else
519  s1 = new ShapeRoi(previousRoi);
520  if (this instanceof ShapeRoi)
521  s2 = (ShapeRoi)this;
522  else
523  s2 = new ShapeRoi(this);
524  if (ic.roiModState==ImageCanvas.ADD_TO_ROI)
525  s1.or(s2);
526  else
527  s1.not(s2);
528  ic.roiModState = ImageCanvas.NO_MODS;
529  Roi[] rois = s1.getRois();
530  int type2 = rois[0].getType();
531  //IJ.log(rois.length+" "+type2);
532  if (rois.length==1 && (type2==POLYGON||type2==FREEROI))
533  imp.setRoi(rois[0]);
534  else
535  imp.setRoi(s1);
536  }
537 
538  protected void showStatus() {
539  String value;
540  if (state!=CONSTRUCTING && type==RECTANGLE && width<=25 && height<=25) {
541  ImageProcessor ip = imp.getProcessor();
542  double v = ip.getPixelValue(x,y);
543  int digits = (imp.getType()==ImagePlus.GRAY8||imp.getType()==ImagePlus.GRAY16)?0:2;
544  value = ", value="+IJ.d2s(v,digits);
545  } else
546  value = "";
547  Calibration cal = imp.getCalibration();
548  String size;
549  if (cal.scaled() && !IJ.altKeyDown())
550  size = ", w="+IJ.d2s(width*cal.pixelWidth)+", h="+IJ.d2s(height*cal.pixelHeight);
551  else
552  size = ", w="+width+", h="+height;
553  IJ.showStatus(imp.getLocationAsString(x,y)+size+value);
554  }
555 
556  public ImageProcessor getMask() {
557  return null;
558  }
559 
560  void startPaste(ImagePlus clipboard) {
561  IJ.showStatus("Pasting...");
562  this.clipboard = clipboard;
563  imp.getProcessor().snapshot();
564  updateClipRect(); //v1.24e
565  if (IJ.debugMode) IJ.log("startPaste: "+clipX+" "+clipY+" "+clipWidth+" "+clipHeight);
566  imp.draw(clipX, clipY, clipWidth, clipHeight);
567  }
568 
569  void updatePaste() {
570  if (clipboard!=null) {
571  ImageProcessor ip = imp.getProcessor();
572  ip.reset();
573  ip.copyBits(clipboard.getProcessor(), x, y, pasteMode);
574  if (type!=RECTANGLE) //v1.24e
575  ip.reset(imp.getMask());
576  ic.setImageUpdated();
577  }
578  }
579 
580  public void endPaste() {
581  if (clipboard!=null) {
582  ImageProcessor ip = imp.getProcessor();
583  if (pasteMode!=Blitter.COPY) ip.reset();
584  ip.copyBits(clipboard.getProcessor(), x, y, pasteMode);
585  if (type!=RECTANGLE) //v1.24e
586  ip.reset(imp.getMask());
587  ip.snapshot();
588  clipboard = null;
589  imp.updateAndDraw();
590  Undo.setup(Undo.FILTER, imp);
591  }
592  }
593 
594  public void abortPaste() {
595  clipboard = null;
596  imp.getProcessor().reset();
597  imp.updateAndDraw();
598  }
599 
601  public double getAngle(int x1, int y1, int x2, int y2) {
602  final int q1=0, q2orq3=2, q4=3; //quadrant
603  int quadrant;
604  double dx = x2-x1;
605  double dy = y1-y2;
606  double angle;
607 
608  if (imp!=null) {
609  Calibration cal = imp.getCalibration();
610  dx *= cal.pixelWidth;
611  dy *= cal.pixelHeight;
612  }
613 
614  if (dx!=0.0)
615  angle = Math.atan(dy/dx);
616  else {
617  if (dy>=0.0)
618  angle = Math.PI/2.0;
619  else
620  angle = -Math.PI/2.0;
621  }
622  angle = (180.0/Math.PI)*angle;
623  if (dx>=0.0 && dy>=0.0)
624  quadrant = q1;
625  else if (dx<0.0)
626  quadrant = q2orq3;
627  else
628  quadrant = q4;
629  switch (quadrant) {
630  case q1:
631  break;
632  case q2orq3:
633  angle = angle+180.0;
634  break;
635  case q4:
636  angle = angle+360.0;
637  }
638  return angle;
639  }
640 
642  public static Color getColor() {
643  return ROIColor;
644  }
645 
647  public static void setColor(Color c) {
648  ROIColor = c;
649  }
650 
652  public String getName() {
653  return name;
654  }
655 
657  public void setName(String name) {
658  this.name = name;
659  }
660 
664  public static void setPasteMode(int transferMode) {
665  int previousMode = pasteMode;
666  pasteMode = transferMode;
667  ImagePlus imp = IJ.getInstance().getImagePlus();
668  if (imp==null)
669  return;
670  if (previousMode!=Blitter.COPY) {
671  ImageProcessor ip = imp.getProcessor();
672  ip.reset();
673  if (pasteMode!=Blitter.COPY) {
674  //ip.copyBits(clipboard.getProcessor(), x, y, pasteMode);
675  //ic.setImageUpdated();
676  }
677  }
678  imp.updateAndDraw();
679  }
680 
685  public int getPasteMode() {
686  if (clipboard==null)
687  return NOT_PASTING;
688  else
689  return pasteMode;
690  }
691 
693  public static int getCurrentPasteMode() {
694  return pasteMode;
695  }
696 
698  public boolean isArea() {
699  return (type>=RECTANGLE && type<=TRACED_ROI) || type==COMPOSITE;
700  }
701 
703  boolean isLine() {
704  return type>=LINE && type<=FREELINE;
705  }
706 
708  public String getTypeAsString() {
709  String s="";
710  switch(type) {
711  case POLYGON: s="Polygon"; break;
712  case FREEROI: s="Freehand"; break;
713  case TRACED_ROI: s="Traced"; break;
714  case POLYLINE: s="Polyline"; break;
715  case FREELINE: s="Freeline"; break;
716  case ANGLE: s="Angle"; break;
717  case LINE: s="Straight Line"; break;
718  case OVAL: s="Oval"; break;
719  case COMPOSITE: s = "Composite"; break;
720  default: s="Rectangle"; break;
721  }
722  return s;
723  }
724 
725  public String toString() {
726  return ("Roi: type="+type+", x="+x+", y="+y+", width="+width+", height="+height);
727  }
728 
729 }