/** * Constructs an image edit mode with specified manager. * * @param modeManager the mode manager for this mode. */ public ImageEditMode(ModeManager modeManager, PixelsView pixels, float vnull, float[][] v) { super(modeManager); setName("Edit"); // setIcon(loadIcon(ImageEditMode.class,"resources/ImageEdit16.gif")); // setIcon(loadIcon(MouseTrackMode.class,"resources/Track24.gif")); setMnemonicKey(KeyEvent.VK_E); setAcceleratorKey(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0)); setShortDescription("Edit points"); _tile = pixels.getTile(); _pixels = pixels; fill(vnull, v); _vnull = vnull; _n1 = v[0].length; _n2 = v.length; _v = v; _is2 = new ImageSampler2(_v); _ns = 0; _x1 = new float[0][0]; _x2 = new float[0][0]; _vx = new float[0][0]; _points = new PointsView(_x1, _x2); if (pixels.getOrientation() == PixelsView.Orientation.X1RIGHT_X2UP) { _points.setOrientation(PointsView.Orientation.X1RIGHT_X2UP); } else { _points.setOrientation(PointsView.Orientation.X1DOWN_X2RIGHT); } _points.setStyle("w-o"); }
/** * A mode for editing an image by drawing curves. <em> This class is a hack for testing * diffusion/interpolation ideas. </em> * * @author Dave Hale, Colorado School of Mines * @version 2007.09.06 */ public class ImageEditMode extends Mode { private static final long serialVersionUID = 1L; /** * Constructs an image edit mode with specified manager. * * @param modeManager the mode manager for this mode. */ public ImageEditMode(ModeManager modeManager, PixelsView pixels, float vnull, float[][] v) { super(modeManager); setName("Edit"); // setIcon(loadIcon(ImageEditMode.class,"resources/ImageEdit16.gif")); // setIcon(loadIcon(MouseTrackMode.class,"resources/Track24.gif")); setMnemonicKey(KeyEvent.VK_E); setAcceleratorKey(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0)); setShortDescription("Edit points"); _tile = pixels.getTile(); _pixels = pixels; fill(vnull, v); _vnull = vnull; _n1 = v[0].length; _n2 = v.length; _v = v; _is2 = new ImageSampler2(_v); _ns = 0; _x1 = new float[0][0]; _x2 = new float[0][0]; _vx = new float[0][0]; _points = new PointsView(_x1, _x2); if (pixels.getOrientation() == PixelsView.Orientation.X1RIGHT_X2UP) { _points.setOrientation(PointsView.Orientation.X1RIGHT_X2UP); } else { _points.setOrientation(PointsView.Orientation.X1DOWN_X2RIGHT); } _points.setStyle("w-o"); } /////////////////////////////////////////////////////////////////////////// // protected protected void setActive(Component component, boolean active) { if (component instanceof Tile) { Tile tile = (Tile) component; if (active) { tile.addTiledView(_points); tile.addMouseListener(_ml); tile.addMouseWheelListener(_mwl); InputMap im = tile.getInputMap(); ActionMap am = tile.getActionMap(); im.put(KS_BACK_SPACE, "backspace"); im.put(KS_UP, "up"); im.put(KS_DOWN, "down"); am.put("backspace", _bsa); am.put("up", _uaa); am.put("down", _daa); } else { tile.removeTiledView(_points); tile.removeMouseListener(_ml); tile.removeMouseWheelListener(_mwl); InputMap im = tile.getInputMap(); ActionMap am = tile.getActionMap(); im.remove(KS_BACK_SPACE); im.remove(KS_UP); im.remove(KS_DOWN); am.remove("backspace"); am.remove("up"); am.remove("down"); } } } /////////////////////////////////////////////////////////////////////////// // private private Tile _tile; // tile containing the pixels view private PixelsView _pixels; // pixels view of image to edit private PointsView _points; // points view of editing curves private int _isSelected = -1; // index of selected segment private int _ipSelected = -1; // index of selected point private boolean _creating; // true if in process of creating a segment float _vnull; // the null (unknown) image value private int _n1; // number of samples in 1st dimension of image private int _n2; // number of samples in 2nd dimension of image float[][] _v; // array[n2][n1] of image values to edit private int _ns; // number of segments float[][] _x1; // x1 coordinates of points in segments float[][] _x2; // x2 coordinates of points in segments float[][] _vx; // values v(x1,x2) private int _xdown; // x coordinate where mouse down private int _ydown; // y coordinate where mouse down private int _xedit; // x coordinate of current edit private int _yedit; // y coordinate of current edit private boolean _hasMotionListener; // true if handling mouse drag private ImageSampler2 _is2; // used to get/set image samples // Event handlers. private static KeyStroke KS_BACK_SPACE = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0); private static KeyStroke KS_DOWN = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0); private static KeyStroke KS_UP = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0); private Action _bsa = new AbstractAction() { public void actionPerformed(ActionEvent e) { onBackSpace(); } }; private Action _uaa = new AbstractAction() { public void actionPerformed(ActionEvent e) { onArrow(1.0f); } }; private Action _daa = new AbstractAction() { public void actionPerformed(ActionEvent e) { onArrow(-1.0f); } }; private MouseListener _ml = new MouseAdapter() {; public void mousePressed(MouseEvent e) { onMouseDown(e); } public void mouseReleased(MouseEvent e) { onMouseUp(e); } }; private MouseWheelListener _mwl = new MouseWheelListener() {; public void mouseWheelMoved(MouseWheelEvent e) { onMouseWheel(e); } }; private MouseMotionListener _mml = new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { onMouseDrag(e); } }; private void onBackSpace() { removeSelectedPoint(); updateAll(); } private void removeSelectedPoint() { int is = _isSelected; int ip = _ipSelected; if (is >= 0 && ip >= 0) { removePoint(is, ip); deselect(); } } private void onArrow(float dv) { int is = _isSelected; int ip = _ipSelected; if (is >= 0 && ip >= 0) { _vx[is][ip] += dv; trace("value=" + _vx[is][ip]); updateImage(); updatePixelsView(); } } private void onMouseDown(MouseEvent e) { if (_tile != getTile(e)) return; // Remember mouse down location. int x = _xdown = e.getX(); int y = _ydown = e.getY(); // What segment and point, if any, was clicked? int[] i = {0, 0}, p = {0, 0}; int icode = getSegmentAndPoint(x, y, i, p); int is = i[0], ip = i[1]; int xp = p[0], yp = p[1]; // trace("onMouseDown: is="+is+" ip="+ip); // If currently in the process of creating a new segment, ... if (_creating) { // If mouse is on previous (selected) point in the created segment, // then end segment creation. if (icode == 1 && isSelected(is, ip)) { _creating = false; } // Else append a new point to the created segment. else { is = _isSelected; ip = appendPoint(x, y); selectPoint(is, ip); addMotionListener(); } } // Else if not currently in the process of creating a new segment, ... else { // If mouse on existing point, ... if (icode == 1) { // If alt key down, delete the point. if (e.isAltDown()) { selectPoint(is, ip); removeSelectedPoint(); } // Else, select the point else { selectPoint(is, ip); addMotionListener(); } } // Else if mouse on existing segment, ... else if (icode == 2) { // If shift key down, add a new point to existing segment. if (e.isShiftDown()) { insertPoint(is, ip, x, y); selectPoint(is, ip); addMotionListener(); } } // Else if mouse down away from all segments, ... else { // If shift key down, create a new segment. if (e.isShiftDown()) { is = appendSegment(x, y); ip = 0; selectPoint(is, ip); addMotionListener(); _creating = true; } // Else deselect all points. else { deselect(); } } } updateAll(); } private void onMouseDrag(MouseEvent e) { changeSelectedPoint(e.getX(), e.getY()); updateAll(); } private void onMouseUp(MouseEvent e) { if (hasMotionListener()) { changeSelectedPoint(e.getX(), e.getY()); removeMotionListener(); } updateAll(); } private void onMouseWheel(MouseWheelEvent e) { float dv = (float) (-e.getWheelRotation()); // trace("dv="+dv); onArrow(dv); } private void addMotionListener() { _tile.addMouseMotionListener(_mml); _hasMotionListener = true; } private void removeMotionListener() { _tile.removeMouseMotionListener(_mml); _hasMotionListener = false; } private boolean hasMotionListener() { return _hasMotionListener; } private Tile getTile(MouseEvent e) { return (Tile) e.getSource(); } private void updateAll() { updateImage(); updatePixelsView(); updatePointsView(); } private void updateImage() { fill(_vnull, _v); for (int is = 0; is < _ns; ++is) { float[] x1 = _x1[is]; float[] x2 = _x2[is]; float[] vx = _vx[is]; int np = _x1[is].length; if (np == 1) { _is2.set(x1[0], x2[0], vx[0]); } else if (np > 1) { for (int ip = 1; ip < np; ++ip) { float x1a = x1[ip - 1]; float x2a = x2[ip - 1]; float vxa = vx[ip - 1]; float x1b = x1[ip]; float x2b = x2[ip]; float vxb = vx[ip]; ImageSampler2.Line line = _is2.sampleLine(x1a, x2a, x1b, x2b); int nr = line.nr(); float ra = line.ra(); float rb = line.rb(); float dvx = (nr > 1) ? (vxb - vxa) / (float) (nr - 1) : vxa; for (int ir = 0; ir < nr; ++ir) { float vxi = vxa + (float) ir * dvx; line.set(ir, vxi); } } } } } private void updatePixelsView() { _pixels.set(_v); } private void updatePointsView() { _points.set(_x1, _x2); } private int x(float x1, float x2) { Transcaler ts = _tile.getTranscaler(); PointsView.Orientation pvo = _points.getOrientation(); if (pvo == PointsView.Orientation.X1RIGHT_X2UP) { Projector p = _tile.getHorizontalProjector(); return ts.x(p.u(x1)); } else { Projector p = _tile.getVerticalProjector(); return ts.x(p.u(x2)); } } private int y(float x1, float x2) { Transcaler ts = _tile.getTranscaler(); PointsView.Orientation pvo = _points.getOrientation(); if (pvo == PointsView.Orientation.X1RIGHT_X2UP) { Projector p = _tile.getHorizontalProjector(); return ts.y(p.u(x2)); } else { Projector p = _tile.getVerticalProjector(); return ts.y(p.u(x1)); } } private float x1(int x, int y) { Transcaler ts = _tile.getTranscaler(); PointsView.Orientation pvo = _points.getOrientation(); if (pvo == PointsView.Orientation.X1RIGHT_X2UP) { Projector p = _tile.getHorizontalProjector(); return (float) p.v(ts.x(x)); } else { Projector p = _tile.getVerticalProjector(); return (float) p.v(ts.y(y)); } } private float x2(int x, int y) { Transcaler ts = _tile.getTranscaler(); PointsView.Orientation pvo = _points.getOrientation(); if (pvo == PointsView.Orientation.X1RIGHT_X2UP) { Projector p = _tile.getHorizontalProjector(); return (float) p.v(ts.y(y)); } else { Projector p = _tile.getVerticalProjector(); return (float) p.v(ts.x(x)); } } private static double pointToPointSquared(double xp, double yp, double xa, double ya) { double xq = xp - xa; double yq = yp - ya; return xq * xq + yq * yq; } private static double pointToSegmentSquared( double xp, double yp, double xa, double ya, double xb, double yb, double[] q) { double xq = xp - xa; double yq = yp - ya; double xr = xb - xa; double yr = yb - ya; double rn = xq * xr + yq * yr; if (rn <= 0.0) { xq = xa; yq = ya; } else { double rd = xr * xr + yr * yr; if (rd <= rn) { xq = xb; yq = yb; } else { double r = rn / rd; xq = xa + r * xr; yq = ya + r * yr; } } if (q != null) { q[0] = xq; q[1] = yq; } xq -= xp; yq -= yp; return xq * xq + yq * yq; } private void changeSelectedPoint(int x, int y) { changePoint(_isSelected, _ipSelected, x, y); } private void changePoint(int is, int ip, int x, int y) { // trace("changePoint: is="+is+" ip="+ip); _x1[is][ip] = x1(x, y); _x2[is][ip] = x2(x, y); } private int appendPoint(int is, int x, int y) { int ip = _x1[is].length; insertPoint(is, ip, x, y); return ip; } private int appendPoint(int x, int y) { return appendPoint(_ns - 1, _xdown, _ydown); } private int appendSegment(int x, int y) { insertPoint(_ns, 0, x, y); return _ns - 1; } private void insertPoint(int is, int ip, int x, int y) { Check.argument(is <= _ns, "is in bounds"); Check.argument(is == _ns && ip == 0 || is < _ns && ip <= _x1[is].length, "is in bounds"); if (is == _ns) { int ns = is + 1; float[][] x1 = new float[ns][0]; float[][] x2 = new float[ns][0]; float[][] vx = new float[ns][0]; for (int js = 0; js < _ns; ++js) { x1[js] = _x1[js]; x2[js] = _x2[js]; vx[js] = _vx[js]; } _x1 = x1; _x2 = x2; _vx = vx; _ns = ns; } int np = _x1[is].length; float[] x1p = _x1[is]; float[] x2p = _x2[is]; float[] vxp = _vx[is]; float[] x1t = new float[np + 1]; float[] x2t = new float[np + 1]; float[] vxt = new float[np + 1]; for (int jp = 0; jp < ip; ++jp) { x1t[jp] = x1p[jp]; x2t[jp] = x2p[jp]; vxt[jp] = vxp[jp]; } x1t[ip] = x1(x, y); x2t[ip] = x2(x, y); vxt[ip] = _is2.get(x1t[ip], x2t[ip]); for (int jp = ip; jp < np; ++jp) { x1t[jp + 1] = x1p[jp]; x2t[jp + 1] = x2p[jp]; vxt[jp + 1] = vxp[jp]; } _x1[is] = x1t; _x2[is] = x2t; _vx[is] = vxt; } private void removePoint(int is, int ip) { int np = _x1[is].length; if (np == 1) { float[][] x1t = new float[_ns - 1][]; float[][] x2t = new float[_ns - 1][]; float[][] vxt = new float[_ns - 1][]; for (int js = 0; js < is; ++js) { x1t[js] = _x1[js]; x2t[js] = _x2[js]; vxt[js] = _vx[js]; } for (int js = is + 1; js < _ns; ++js) { x1t[js - 1] = _x1[js]; x2t[js - 1] = _x2[js]; vxt[js - 1] = _vx[js]; } --_ns; _x1 = x1t; _x2 = x2t; _vx = vxt; } else { float[] x1p = _x1[is]; float[] x2p = _x2[is]; float[] vxp = _vx[is]; float[] x1t = new float[np - 1]; float[] x2t = new float[np - 1]; float[] vxt = new float[np - 1]; for (int jp = 0; jp < ip; ++jp) { x1t[jp] = x1p[jp]; x2t[jp] = x2p[jp]; vxt[jp] = vxp[jp]; } for (int jp = ip + 1; jp < np; ++jp) { x1t[jp - 1] = x1p[jp]; x2t[jp - 1] = x2p[jp]; vxt[jp - 1] = vxp[jp]; } _x1[is] = x1t; _x2[is] = x2t; _vx[is] = vxt; } } private boolean isSelected(int is, int ip) { return _isSelected == is && _ipSelected == ip; } private void selectPoint(int is, int ip) { _isSelected = is; _ipSelected = ip; } private void deselect() { _isSelected = -1; _ipSelected = -1; } /** * Gets indices of a nearby segment and point, if close enough. If an existing segment is close * enough, both segment and point indices {is,ip} are returned. If an existing point on the * segment is close enough, then ip is the index of that existing point. Otherwise, if a line * between two existing points is close enough, then ip is the larger index of those two existing * points. In both cases, the pixel coordinates {x,y} of the closest point are returned. If no * existing segment is close enough, then {is,ip} = {-1,-1}. * * @param x the x coordinate in pixels. * @param y the y coordinate in pixels. * @param i array {is,ip} of indices of segment and point; {-1,-1} if none. * @param p array {xp,yp} coordinates of close point. This point could be an existing point or a * point on a line between two existing points. * @return 0, if no existing segment (or point) is close enough; 1, if the closest point already * exists in a segment; 2, if the closest point lies on a segment between two existing points * in that segment. */ private int getSegmentAndPoint(int x, int y, int[] i, int[] p) { double[] pt = {0.0, 0.0}; double dpmin = 25.0; // distance must be less than 5 pixels double dsmin = 25.0; // 25 is this distance squared int icode = 0; // initially assume no segment is close enough int ipmin = -1; int ismin = -1; int xtmin = -1; int ytmin = -1; for (int is = 0; is < _ns; ++is) { double xa = 0.0; double ya = 0.0; double xb = 0.0; double yb = 0.0; int np = _x1[is].length; for (int ip = 0; ip < np; ++ip) { xb = xa; yb = ya; xa = x(_x1[is][ip], _x2[is][ip]); ya = y(_x1[is][ip], _x2[is][ip]); double dp = pointToPointSquared(x, y, xa, ya); if (dp < dpmin) { ipmin = ip; ismin = is; dpmin = dp; xtmin = (int) (xa + 0.5); ytmin = (int) (ya + 0.5); icode = 1; } else if (ip > 0 && icode != 1) { double ds = pointToSegmentSquared(x, y, xa, ya, xb, yb, pt); if (ds < dsmin) { ipmin = ip; ismin = is; dsmin = ds; xtmin = (int) (pt[0] + 0.5); ytmin = (int) (pt[1] + 0.5); icode = 2; } } } } i[0] = ismin; i[1] = ipmin; p[0] = xtmin; p[1] = ytmin; return icode; } private void drawLine(JComponent c, int x, int y) { _xedit = x; _yedit = y; Graphics g = c.getGraphics(); g.setColor(Color.BLACK); g.setXORMode(c.getBackground()); g.drawLine(_xdown, _ydown, _xedit, _yedit); g.dispose(); } public static void main(String[] args) { SwingUtilities.invokeLater( new Runnable() { public void run() { go(); } }); } private static void go() { int n1 = 101; int n2 = 101; // float[][] f = sin(rampfloat(0.0f,0.1f,0.1f,n1,n2)); float[][] f = zerofloat(n1, n2); PlotPanel.Orientation orientation = PlotPanel.Orientation.X1DOWN_X2RIGHT; PlotPanel panel = new PlotPanel(orientation); PixelsView pv = panel.addPixels(f); pv.setInterpolation(PixelsView.Interpolation.NEAREST); pv.setColorModel(ColorMap.JET); panel.addColorBar("time"); panel.setVLabel("depth (km)"); panel.setHLabel("distance (km)"); PlotFrame frame = new PlotFrame(panel); frame.setDefaultCloseOperation(PlotFrame.EXIT_ON_CLOSE); frame.setSize(800, 700); frame.setFontSize(24); frame.setVisible(true); ModeManager mm = frame.getModeManager(); ImageEditMode iem = new ImageEditMode(mm, pv, 0.0f, f); iem.setActive(true); } private void trace(String s) { System.out.println(s); } }