Changeset 1829


Ignore:
Timestamp:
11/20/06 16:00:41 (13 years ago)
Author:
sorber
Message:

Changes additions to FreeformTool:
-Erase function with CTRL key (split function too)
-Curve length and bounding box dimensions change in real time in stats box.
-Properly overrode getDistance method--computes distance to curve, not just bounding box. This makes a difference when trying to select another overlay very
near a freeform.
-Started to cleanup logic/consolidate code in FreeformTool

Location:
trunk/loci/visbio/overlays
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/loci/visbio/overlays/FreeformTool.java

    r1797 r1829  
    4040  protected static final double RECONNECT_THRESH = 0.5;  
    4141 
     42  /** 4 constants describing edit modes */ 
     43  // There are four edit modes.  The method setMode(int) is called to change the mode, in order to 
     44  // properties related to the current mode; e.g., whether a freeform is selected in the list of  
     45  // overlays 
     46  protected static final int ERASE = -1; 
     47  protected static final int CHILL = 0; // could call this WAIT... 
     48  protected static final int DRAW = 1; 
     49  protected static final int EDIT = 2; 
     50 
    4251  // -- Fields -- 
    4352 
     
    4756  /** Tendril wraps info about an edit to a curve */ 
    4857  protected Tendril tendril; 
    49  
    50   protected boolean editing, extending; // whether the tool is in edit mode or extend mode; 
    51   protected int prevNodeIndex, inserted; // used in editing to track changes to the curve  
     58  protected int mode; 
     59 
    5260  protected float[][] pre, post; // chunks of curve tracked in redrawing  
    5361 
     
    5765  public FreeformTool(OverlayTransform overlay) { 
    5866    super(overlay, "Freeform", "Freeform", "freeform.png"); 
    59     editing = extending = false; 
    60     prevNodeIndex = inserted = 0; 
     67    setMode(CHILL); 
    6168    tendril = null; 
    6269  } 
     
    6673  /** Instructs this tool to respond to a mouse press. */ 
    6774  public void mouseDown(float x, float y, int[] pos, int mods) { 
    68     /* 
    69      * When mouseDown occurs, either 
    70      * 1) a new freeform is being drawn, or 
    71      * 2) an existing freeform is being edited 
    72      * 
    73      * 1) occurs if the mouseDown is sufficiently close to an existing freeform.   
    74      * In this case boolean editing = true. 
    75      * else 2) occurs (editing = false) an new freeform is created 
    76      */ 
    77  
    78     // determine if mouseDown was close enough to an existing freeform to invoke editing 
     75    boolean ctl = (mods & InputEvent.CTRL_MASK) != 0;  
    7976    OverlayFreeform target = getClosestFreeform(x, y); 
    80      
    81     if (target == null) { // not close to any freeforms 
     77    if (target != null) { 
     78      freeform = target; 
     79      double[] distSegWt = MathUtil.getDistSegWt(freeform.getNodes(), x, y); 
     80      double dist = distSegWt[0]; 
     81      int seg = (int) distSegWt[1]; 
     82      double weight = distSegWt[2]; 
     83 
     84      if (ctl) { 
     85        setMode(ERASE); 
     86        // test for not terminal node 
     87        if (!(seg == 0 && weight == 0.0) && !(seg == freeform.getNumNodes()-2 && weight == 1.0)) { 
     88          erase(freeform, x, y, dist, seg, weight); 
     89        } 
     90      } else { 
     91        // test for not terminal node 
     92        if (!(seg == 0 && weight == 0.0) && !(seg == freeform.getNumNodes()-2 && weight == 1.0)) { 
     93          // if interior node 
     94          // project click onto curve and insert a new node there,  
     95          // unless nearest point on curve is a node itself 
     96          if (weight == 0.0) { 
     97            // nearest point on seg is the start node 
     98            float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 
     99            freeform.insertNode (seg+1, a[0], a[1]); 
     100            tendril = new Tendril (seg, seg+1, true); 
     101            splitNodes (seg-1, seg+2); 
     102          } else if (weight == 1.0) { 
     103            // nearest point on  seg is the end node 
     104            float[] a = freeform.getNodeCoords(seg+1); // determine projection on seg. 
     105            freeform.insertNode(seg+2, a[0], a[1]); 
     106            tendril = new Tendril (seg+1, seg+2, true); 
     107            splitNodes (seg, seg+3); 
     108          } else { 
     109            float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 
     110            float[] b = freeform.getNodeCoords(seg + 1);         
     111            float[] newXY = computePtOnSegment(a, b, (float) weight); 
     112            freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
     113            freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
     114            tendril = new Tendril(seg+1, seg+2, false); 
     115            splitNodes (seg+1, seg+3); 
     116          }  
     117          setMode(EDIT); 
     118        } else { 
     119          if (seg == 0) freeform.reverseNodes(); 
     120          setMode(DRAW); 
     121        } 
     122      }// end else 
     123    } else {  
    82124      deselectAll(); 
    83125      freeform = new OverlayFreeform(overlay, x, y, x, y); 
    84126      configureOverlay(freeform); 
    85127      overlay.addObject(freeform, pos); 
    86       extending = true; 
     128      setMode(DRAW); 
     129    }  
     130 
     131    overlay.notifyListeners(new TransformEvent(overlay)); 
     132    ((OverlayWidget) overlay.getControls()).refreshListSelection(); 
     133  } // end mouseDown 
     134 
     135  /** Erases or slices a freeform */ 
     136  private void erase(OverlayFreeform freef, float x, float y, double dist, int seg, double weight) { 
     137    //TODO: don't need to check all of exterior cases 
     138    int f1Start, f2Start, f1Stop, f2Stop; 
     139    OverlayFreeform f1, f2; 
     140    boolean doNothing = false; 
     141 
     142    f1Start = 0; 
     143    f1Stop = seg; 
     144    f2Start = seg + 1; 
     145    f2Stop = freef.getNumNodes() - 1; 
     146         
     147    boolean splitFreeform = false;    
     148    if (weight == 0.0) {  
     149      // case: beginning node 
     150      freef.deleteNode(0); 
     151      //print("erase", "weight == 0: click occurred nearest start node"); 
     152    } else if (weight == 1.0) { 
     153      // case end node of segment 
     154      if (seg == freef.getNumNodes() - 2) { 
     155        freef.deleteNode(seg + 1); 
     156        //print("erase", "weight == 1: click occurred nearest end node"); 
     157      } else { 
     158        //print("erase", "weight == 1: click occurred nearest node " + (seg + 1)); 
     159        splitFreeform = true; 
     160        // reassign this guy 
     161        f2Start = seg + 2;  
     162      } 
    87163    } else { 
    88       // ACS TODO set selected in Overlays window for freeform under edit 
    89       freeform = target; 
    90       editing = true; // enter edit mode 
    91  
    92       double[] distSegWt = MathUtil.getDistSegWt(freeform.getNodes(), x, y); 
    93  
    94       double dist = distSegWt[0]; 
    95       int seg = (int) distSegWt[1]; 
    96       double weight = distSegWt[2]; 
     164      // interior segment 
     165      //print("erase", "0 < weight < 1: seg = " + seg); 
     166      splitFreeform = true; 
     167    } 
     168     
     169    if (splitFreeform) { 
     170      //TODO debug weird 'elementAt()' exception that pops up.  Wrong number of object. 
     171      float[][] oldNodes = freef.getNodes(); 
     172      float[][] f1Nodes = new float[2][f1Stop+1]; 
     173      float[][] f2Nodes = new float[2][f2Stop-f2Start + 1]; 
     174 
     175      for (int i = 0; i<2; i++) { 
     176        System.arraycopy(oldNodes[i], 0, f1Nodes[i], 0, f1Stop+1); 
     177        System.arraycopy(oldNodes[i], f2Start, f2Nodes[i], 0, f2Stop-f2Start+1); 
     178      } 
     179      f1 = new OverlayFreeform(overlay, f1Nodes); 
     180      f2 = new OverlayFreeform(overlay, f2Nodes); 
    97181       
    98       // criteria for entering extend mode or edit mode 
    99       if (seg + 2 == freeform.getNumNodes() && weight == 1.0) { // extend 
    100         editing = false; 
    101         extending = true; 
    102       } else if (seg == 0 && weight == 0.0) { // extend 
    103         freeform.reverseNodes(); 
    104         editing = false; 
    105         extending = true; 
    106       } else { // edit 
    107         // project click onto curve and insert a new node there,  
    108         // unless nearest point on curve is a node itself 
    109         // Register this node as previous node 
    110          
    111         if (weight == 0.0) { 
    112           // nearest point on seg is the start node 
    113           float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 
    114           freeform.insertNode (seg+1, a[0], a[1]); 
    115           tendril = new Tendril (seg, seg+1, true); 
    116           splitNodes (seg-1, seg+2); 
    117         } else if (weight == 1.0) { 
    118           // nearest point on  seg is the end node 
    119           float[] a = freeform.getNodeCoords(seg+1); // determine projection on seg. 
    120           freeform.insertNode(seg+2, a[0], a[1]); 
    121           tendril = new Tendril (seg+1, seg+2, true); 
    122           splitNodes (seg, seg+3); 
    123         } else { 
    124           float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 
    125           float[] b = freeform.getNodeCoords(seg + 1);         
    126           float[] newXY = computePtOnSegment(a, b, (float) weight); 
    127           freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
    128           freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
    129           tendril = new Tendril(seg+1, seg+2, false); 
    130           splitNodes (seg+1, seg+3); 
    131         } // end else 
    132       } // end else   
    133     } // end else 
    134   } // end mouseDown 
     182      //System.out.println("printing nodes of original freeform"); 
     183      //printNodes(freef.getNodes()); 
     184      //System.out.println("printing nodes of new sub freeforms"); 
     185      //printNodes(f1.getNodes()); 
     186      //printNodes(f2.getNodes()); 
     187      configureOverlay(f1); 
     188      configureOverlay(f2); 
     189      overlay.addObject(f1); 
     190      overlay.addObject(f2); 
     191      f1.setSelected(false); 
     192      f2.setSelected(false); 
     193      f1.setDrawing(false); 
     194      f2.setDrawing(false); 
     195       
     196      overlay.removeObject(freef); 
     197    } 
     198  } 
    135199 
    136200  /** Wraps info for redrawing a curve.  Appears like a 'tendril' on screen */ 
     
    172236  /** Instructs this tool to respond to a mouse release. */ 
    173237  public void mouseUp(float x, float y, int[] pos, int mods) { 
    174     if (editing && tendril !=null) { 
     238    //print ("mouseUp", "mouseUp begun. mode = " + mode); 
     239    if (mode == DRAW) { 
     240      freeform.truncateNodeArray(); 
     241    } else if (mode == EDIT) { 
    175242      // save coords of tendril root 
    176243      float[] c = freeform.getNodeCoords(tendril.start); 
     
    181248    } 
    182249 
    183     extending = editing = false; 
    184     deselectAll(); 
    185     if (freeform != null) {  
    186       inserted = 0; 
    187       editing = false; 
    188       freeform.truncateNodeArray(); 
     250    if (mode != CHILL) setMode(CHILL); 
     251    overlay.notifyListeners(new TransformEvent(overlay)); 
     252    ((OverlayWidget) overlay.getControls()).refreshListSelection(); 
     253  } 
     254 
     255  /** Sets variables related to edit mode */ 
     256  private void setMode(int newMode) { 
     257    mode = newMode; 
     258    if (mode == DRAW || mode == EDIT || mode == ERASE) { 
     259      freeform.setDrawing(true); 
     260      freeform.setSelected(true); 
     261    } else if (mode == CHILL && freeform != null) { 
    189262      freeform.computeLength(); 
    190263      freeform.updateBoundingBox(); 
     
    192265      freeform.setDrawing(false); 
    193266      freeform = null; 
    194       overlay.notifyListeners(new TransformEvent(overlay)); 
    195267    } 
    196268  } 
     
    199271  public void mouseDrag(float x, float y, int[] pos, int mods) { 
    200272    boolean shift = (mods & InputEvent.SHIFT_MASK) != 0;  
    201     // NEW!! holding shift suppresses reconnect routine, so you can redraw close to the curve.  
    202     // Why not use the wacom pen's click too? TODO 
    203     // I think the pen switch is mapped to Right Click by default, which is already grabbed by VisAD 
    204  
    205     if (editing) { 
     273    boolean ctl = (mods & InputEvent.CTRL_MASK) != 0;  
     274 
     275    if (mode == DRAW) { 
     276      float lastX = freeform.getLastNodeX(); 
     277      float lastY = freeform.getLastNodeY(); 
     278      float dx = x - lastX;  
     279      float dy = y - lastY;  
     280      // compute distance 
     281      double dist = Math.sqrt (dx*dx + dy*dy); 
     282     
     283      if (dist > DRAW_THRESH) { 
     284        freeform.setNextNode(x, y); 
     285        double len = freeform.getCurveLength(); 
     286        freeform.setCurveLength(len + dist); 
     287        freeform.setBoundaries(x,y); // I debated whether to update this realtime.  This is an efficient method,  
     288        // buy updating realtime for erasing requires an O(n) operation every time a node is deleted. 
     289      } 
     290      // mode remains DRAW 
     291    } else if (mode == ERASE) { 
     292      if (freeform == null) { 
     293        OverlayFreeform target = getClosestFreeform(x, y); 
     294        if (target != null) freeform = target; 
     295      } 
     296 
     297      if (!ctl) { 
     298        float lastX = freeform.getLastNodeX(); 
     299        float lastY = freeform.getLastNodeY(); 
     300        float dx = x - lastX;  
     301        float dy = y - lastY;  
     302        // compute distance 
     303        double dist = Math.sqrt (dx*dx + dy*dy); 
    206304       
     305        if (dist > DRAW_THRESH) { 
     306          freeform.setNextNode(x, y); 
     307          double len = freeform.getCurveLength(); 
     308          freeform.setCurveLength(len + dist); 
     309        } 
     310 
     311        setMode(DRAW); 
     312      } else { 
     313        // delete an end node if you're near enough 
     314        float[] beg = freeform.getNodeCoords (0); 
     315        float[] end = freeform.getNodeCoords (freeform.getNumNodes() - 1); 
     316 
     317        double[] drag = {(double) x, (double) y}; 
     318        double[] begd = {(double) beg[0], (double) beg[1]}; 
     319        double[] endd = {(double) end[0], (double) end[1]}; 
     320         
     321        double bdist = MathUtil.getDistance (drag, begd); 
     322        double edist = MathUtil.getDistance (drag, endd); 
     323         
     324        boolean closerToEnd = edist < bdist ? true : false; 
     325        double mdist = closerToEnd ? edist : bdist; 
     326         
     327        if (mdist < DRAW_THRESH) { 
     328          double[] nearest; 
     329          int index, offset; 
     330 
     331          if (closerToEnd) { 
     332            index = freeform.getNumNodes()-1; 
     333            offset = -1; 
     334            nearest = endd; 
     335          } else { 
     336            index = 0; 
     337            offset = 1; 
     338            nearest = begd; 
     339          } 
     340          // adjust curve length 
     341          float[] p = freeform.getNodeCoords(index + offset); 
     342          double[] pd = {(double) p[0], (double) p[1]}; 
     343 
     344          double delta = MathUtil.getDistance (nearest, pd); 
     345          freeform.setCurveLength(freeform.getCurveLength() - delta); 
     346 
     347          // delete appropriate node 
     348          freeform.deleteNode(index); 
     349          freeform.updateBoundingBox(); // WARNING this is O(n) expensive.  Maybe remove it and just update at 
     350                                        // mouseUp? 
     351        } 
     352 
     353        if (freeform.getNumNodes() == 0) { 
     354          setMode(CHILL); 
     355        } 
     356      } 
     357    } else if (mode == EDIT) { 
     358      // extend tendril and or reconnect 
    207359      // get coords of prev node 
    208360      float[] pcoordsf; 
     
    290442            freeform.deleteBetween(endIndex, tendril.tip); 
    291443          } 
    292           editing = false; 
    293444          freeform.computeLength(); 
     445          setMode(CHILL); 
    294446        }            
    295447      } // end reconnect logic 
    296     } else if (extending) {  
    297       deselectAll(); 
    298       float dx = x - freeform.getLastNodeX(); 
    299       float dy = y - freeform.getLastNodeY(); 
    300       double dist = Math.sqrt (dx*dx + dy*dy); 
    301  
    302       if (dist > RECONNECT_THRESH) { 
    303         freeform.setNextNode(x, y); 
    304         float len = freeform.getCurveLength(); 
    305         freeform.setCurveLength(len + (float) dist); 
    306       } 
    307     } else { 
    308       // case !extending !editing 
    309       // Q: can you be both not extending and not editing? 
    310       // A: yes, after succesfully redrawing a segment of the curve.  Further drags do nothing for now. 
    311     } 
    312  
    313     overlay.notifyListeners(new TransformEvent(overlay));  
    314   } // end mouseDrag 
     448   
     449    } 
     450    overlay.notifyListeners(new TransformEvent(overlay)); 
     451  } // end mouseDrag  
    315452   
    316453  //-- Additional methods 
     
    356493 
    357494  /** Prints node array of current freeform; for debugging */ 
     495  private void printNodes(float[][] nodes) { 
     496    System.out.println("Printing nodes..."); 
     497    for (int i = 0; i < nodes[0].length; i++){ 
     498      System.out.println(i+":("+nodes[0][i]+","+nodes[1][i]+")"); 
     499    } 
     500  } 
     501 
     502  /** Prints node array of current freeform; for debugging */ 
    358503  private void printNodes() { 
    359504    if (freeform!=null){  
    360       System.out.println("Printing nodes..."); 
    361       float[][]nodes = freeform.getNodes(); 
    362       for (int i = 0; i < nodes[0].length; i++){ 
    363         System.out.println(i+":("+nodes[0][i]+","+nodes[1][i]+")"); 
    364       } 
     505      float[][] nodes = freeform.getNodes(); 
     506      printNodes(nodes); 
    365507    } 
    366508  } 
  • trunk/loci/visbio/overlays/OverlayFreeform.java

    r1797 r1829  
    2727import java.util.Arrays; 
    2828import visad.*; 
     29import loci.visbio.util.MathUtil; 
    2930 
    3031/** 
     
    5556    Arrays.fill(nodes[1], y1); 
    5657    numNodes = 1; 
     58    computeGridParameters(); 
     59    computeLength(); 
     60  } 
     61   
     62  public OverlayFreeform(OverlayTransform overlay, float[][] nodes) { 
     63    super(overlay); 
     64    x1=x2=y1=y2=0f; 
     65    hasNodes = true; 
     66    this.nodes = nodes; 
     67    numNodes = nodes[0].length; 
     68    maxNodes = nodes[0].length; 
     69    updateBoundingBox(); 
    5770    computeGridParameters(); 
    5871    computeLength(); 
     
    97110 
    98111  /** Computes the shortest distance from this object's bounding box to the given point. */ 
    99   public double getDistance(double x, double y) { 
     112  public double getDistanceToBoundingBox(double x, double y) { 
    100113    double xdist = 0; 
    101114    if (x < x1 && x < x2) xdist = Math.min(x1, x2) - x; 
     
    106119    return Math.sqrt(xdist * xdist + ydist * ydist); 
    107120  } 
     121   
     122  /** Compute the shortest distance from this object to the given point */ 
     123  public double getDistance (double x, double y) { 
     124     double[] distSegWt = MathUtil.getDistSegWt(nodes, (float) x, (float) y); 
     125     return distSegWt[0]; 
     126  } 
    108127 
    109128  /** Retrieves useful statistics about this overlay. */ 
     
    111130    return "Bounds = (" + x1 + ", " + y1 + "), (" + x2 + ", " + y2 + ")\n" + 
    112131      "Number of Nodes = " + numNodes + "\n" + 
    113       "Curve Length = " + curveLength + "\n"; 
     132      "Curve Length = " + (float) curveLength + "\n"; 
    114133  } 
    115134 
  • trunk/loci/visbio/overlays/OverlayObject.java

    r1797 r1829  
    8383 
    8484  /** Length of curve of a noded object */ 
    85   protected float curveLength; 
     85  protected double curveLength; 
    8686 
    8787  // -- Constructor -- 
     
    251251  } 
    252252 
     253  // TEMP // TEMP // TEMP 
     254  public float[] getGridParameters() { 
     255    float[] retvals = {xGrid1, yGrid1, xGrid2, yGrid2, xGrid3, yGrid3, xGrid4, yGrid4}; 
     256    return retvals; 
     257  } 
     258 
    253259  /** Returns coordinates of node at given index in the node array */ 
    254260  public float[] getNodeCoords (int index) { 
     
    269275  /** Returns the number of real nodes in the array */ 
    270276  public int getNumNodes() { return numNodes; } 
     277   
     278  /** Returns total number of nodes in array */ 
     279  public int getMaxNodes() { return maxNodes; } 
    271280   
    272281  /** Gets X coordinate of the overlay's first endpoint. */ 
     
    384393 
    385394  /** Gets length of curve */ 
    386   public float getCurveLength() { return curveLength; } 
     395  public double getCurveLength() { return curveLength; } 
    387396   
    388397  /** Sets length of curve */ 
    389   public void setCurveLength(float len) { curveLength = len; } 
     398  public void setCurveLength(double len) { curveLength = len; } 
    390399 
    391400  /** Computes length of curve */ 
     
    398407      length += MathUtil.getDistance(a, b); 
    399408    } 
    400     this.curveLength = (float) length; 
     409    this.curveLength = length; 
    401410  } 
    402411  
     
    414423  }  
    415424 
     425  /** Prints node array of current freeform; for debugging */ 
     426  private void printNodes(float[][] nodes) { 
     427    System.out.println("Printing nodes..."); 
     428    for (int i = 0; i < nodes[0].length; i++){ 
     429      System.out.println(i+":("+nodes[0][i]+","+nodes[1][i]+")"); 
     430    } 
     431  } 
     432 
    416433  /** Sets next node coordinates. */ 
    417434  public void setNextNode(float x, float y) { 
     435   // System.out.println("OverlayObject.setNextNode(...) called.  numNodes = " + numNodes + ", maxNodes = " + maxNodes); 
     436    //printNodes(getNodes()); 
    418437    if (numNodes >= maxNodes) { 
    419438      maxNodes *= 2; 
     
    460479      maxNodes -= victims; 
    461480      nodes = newNodes; 
     481      if (numNodes == 0) overlay.removeObject(this); 
    462482    } else { 
    463483      //System.out.println("deleteBetween(int, int) out of bounds error"); 
     
    468488  public void deleteNode(int index) { 
    469489    if (index >=0 && index < numNodes) { 
     490      //built-in truncation 
     491      //System.out.println("OverlayObject.deleteNode(" + index +") called.  numNodes = " + numNodes + ", maxNodes = " + maxNodes); 
    470492      float [][] newNodes =  new float[2][numNodes-1]; 
    471493      System.arraycopy(nodes[0], 0, newNodes[0], 0, index); 
     
    474496      System.arraycopy(nodes[1], index+1, newNodes[1], index, numNodes-index-1); 
    475497      numNodes--; 
    476       maxNodes--; 
     498      maxNodes = numNodes; 
    477499      nodes = newNodes; 
     500 
     501      if (numNodes == 0) { 
     502        //System.out.println("destroying " + this); 
     503        overlay.removeObject(this); 
     504      } 
    478505    } 
    479506  } 
     
    518545    } 
    519546    maxNodes = newLength; 
    520     //System.out.println("resize completed. maxNodes = " + maxNodes); 
     547    //System.out.println("resize completed. maxNodes = " + maxNodes + " numNodes =  " + numNodes); 
    521548    return a2; 
    522549  } 
Note: See TracChangeset for help on using the changeset viewer.