Changeset 2875


Ignore:
Timestamp:
06/15/07 14:44:47 (13 years ago)
Author:
sorber
Message:

First try at improving Freeform erasing. Misc. changes to OverlayNodedObject.

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

Legend:

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

    r2868 r2875  
    9595  protected Tendril tendril; 
    9696 
    97   /** Stores the current mode (ERASE, CHILL, DRAW, EDIT) */ 
     97  /** Stores the current mode (INIT, ERASE, CHILL, DRAW, EDIT) */ 
    9898  protected int mode; 
    9999 
     
    126126    double dpy = (double) py; 
    127127 
    128     // find closest freeform 
    129     double maxThresh = ERASE_THRESH; 
    130     DistanceQuery distanceQuery = getClosestFreeform(display, dpx, 
    131         dpy, maxThresh); 
    132     OverlayFreeform target = distanceQuery.freeform; 
    133  
    134     // operate on closest freeform 
    135     if (target != null) { 
    136       // editing operations on closest freeform 
    137       freeform = target; 
    138  
    139       double dist = distanceQuery.dist; 
    140       int seg = distanceQuery.seg; 
    141       double weight = distanceQuery.wt; 
    142  
    143       if (distanceQuery.isNearEndNode()) 
    144       { 
    145         // near an end node 
    146         if (ctl && dist < ERASE_THRESH) { 
    147           // enter erase mode 
    148           setMode(ERASE); 
    149         } 
    150         else if (dist < RESUME_THRESH) { 
     128    // if ctl, erase 
     129    // else if near edit 
     130     
     131    if (ctl) { 
     132      setMode(ERASE); 
     133      freeform = null; 
     134    } 
     135    else { 
     136      // Logic of this section: 
     137      // if close to freeform 
     138        // if near end node, resume drawing 
     139        // else begin editing 
     140      // else 
     141        // record initial click points 
     142 
     143      // find closest freeform 
     144      double maxThresh = ERASE_THRESH; 
     145      DistanceQuery distanceQuery = getClosestFreeform(display, dpx, 
     146          dpy, maxThresh); 
     147      OverlayFreeform target = distanceQuery.freeform; 
     148 
     149      // operate on closest freeform 
     150      if (target != null) { 
     151        // editing operations on closest freeform 
     152        freeform = target; 
     153 
     154        double dist = distanceQuery.dist; 
     155        int seg = distanceQuery.seg; 
     156        double weight = distanceQuery.wt; 
     157 
     158        if (distanceQuery.isNearEndNode() && dist < RESUME_THRESH) { 
    151159          // resume drawing 
    152160          if (seg == 0) freeform.reverseNodes(); 
    153161          setMode(DRAW); 
    154162        } 
    155       } 
    156       else { 
    157         // near an interior node 
    158         if (ctl && dist < ERASE_THRESH) { 
    159           // split and enter erase mode 
    160           slice(freeform, dist, seg, weight); 
    161           freeform = null; 
    162         } 
    163         else if (dist < EDIT_THRESH) { 
    164           // begin editing 
    165           // project click onto curve and insert a new node there, 
    166           // unless nearest point on curve is a node itself 
    167           // 
    168           // note: because of the way getDistSegWt() works, weight can 
    169           // never be 0.0 except if seg = 0. 
    170           if (weight == 1.0) { // nearest point on seg is the end node 
    171             // determine projection on seg. 
    172             float[] a = freeform.getNodeCoords(seg+1); 
    173             freeform.insertNode(seg+2, a[0], a[1]); 
    174             tendril = new Tendril (seg+1, seg+2, true); 
    175             splitNodes (freeform, seg, seg+3); 
    176           } 
    177           else { 
    178             // determine projection on seg. 
    179             // insert a pair of nodes there, starting a tendril 
    180             float[] a = freeform.getNodeCoords(seg); 
    181             float[] b = freeform.getNodeCoords(seg + 1); 
    182             float[] newXY = computePtOnSegment(a, b, (float) weight); 
    183             freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
    184             freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
    185             tendril = new Tendril(seg+1, seg+2, false); 
    186             splitNodes (freeform, seg+1, seg+3); 
    187           } 
    188           setMode(EDIT); 
    189         } 
    190       } 
    191     } 
    192     else { // no freeform was sufficiently close 
    193       // drawing operations on new freeform 
    194       if (!ctl) { 
    195         // record initial coordinates of freeform 
    196         downX = dx; 
    197         downY = dy; 
    198         setMode(INIT); 
     163        else { 
     164          // near an interior node 
     165          if (dist < EDIT_THRESH) { 
     166            // begin editing 
     167            // project click onto curve and insert a new node there, 
     168            // unless nearest point on curve is a node itself 
     169            // 
     170            // note: because of the way getDistSegWt() works, weight can 
     171            // never be 0.0 except if seg = 0. 
     172            if (weight == 1.0) { // nearest point on seg is the end node 
     173              // determine projection on seg. 
     174              float[] a = freeform.getNodeCoords(seg+1); 
     175              freeform.insertNode(seg+2, a[0], a[1]); 
     176              tendril = new Tendril (seg+1, seg+2, true); 
     177              splitNodes (freeform, seg, seg+3); 
     178            } 
     179            else { 
     180              // determine projection on seg. 
     181              // insert a pair of nodes there, starting a tendril 
     182              float[] a = freeform.getNodeCoords(seg); 
     183              float[] b = freeform.getNodeCoords(seg + 1); 
     184              float[] newXY = computePtOnSegment(a, b, (float) weight); 
     185              freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
     186              freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
     187              tendril = new Tendril(seg+1, seg+2, false); 
     188              splitNodes (freeform, seg+1, seg+3); 
     189            } 
     190            setMode(EDIT); 
     191          } 
     192        } 
     193      } 
     194      else { // no freeform was sufficiently close 
     195        // drawing operations on new freeform 
     196        if (!ctl) { 
     197          // record initial coordinates of freeform 
     198          downX = dx; 
     199          downY = dy; 
     200          setMode(INIT); 
     201        } 
    199202      } 
    200203    } 
     
    254257 
    255258      if (minDist < DRAW_THRESH) { 
     259        System.out.println("I'm here"); 
     260        System.out.println("freeform has the following nodes:"); 
     261        freeform.printNodes(); 
     262        System.out.println("other freeform has the following nodes:"); 
     263        OverlayFreeform other = (OverlayFreeform) otherFreefs.get(index); 
     264        other.printNodes(); 
     265        String s = closerToHead ? "head" : "tail"; 
     266        System.out.println("connecting freeform to " + s +  
     267            " of other freeform"); 
     268 
    256269        connectFreeforms(freeform, 
    257270          (OverlayFreeform) otherFreefs.get(index), closerToHead); 
     
    273286          // I debated whether to call setBoundaries with every mouseDrag. 
    274287          // This is an efficient 
    275           // method, but corresponding update when erasing 
    276           // erasing requires an O(n) 
    277           // scan of the nodes every time a node is deleted. 
     288          // method, but the corresponding update when erasing 
     289          // requires an O(n) 
     290          // scan of the nodes many times when a node is deleted. 
    278291          freeform.setBoundaries(s[0], s[1]); 
    279292        } 
     
    282295    } 
    283296    else if (mode == ERASE) { 
    284       if (freeform == null) { 
    285         // DISTANCE COMPUTATION compare floats with floats 
    286         DistanceQuery distanceQuery = getClosestFreeform(display, 
    287             dpx, dpy, ERASE_THRESH); 
    288         OverlayFreeform target = distanceQuery.freeform; 
    289         if (target != null) freeform = target; 
    290       } 
    291  
    292       if (freeform != null) { 
    293         // delete an end node if you're near enough 
    294         float[] beg = freeform.getNodeCoords(0); 
    295         float[] end = freeform.getNodeCoords(freeform.getNumNodes() - 1); 
    296  
    297         int[] dragPxl = {px, py}; 
    298  
    299         double bdist = getPixelDistanceToClick(display, beg, dragPxl); 
    300         double edist = getPixelDistanceToClick(display, beg, dragPxl);  
    301  
    302         boolean closerToEnd = edist < bdist ? true : false; 
    303         double mdist = closerToEnd ? edist : bdist; 
    304  
    305         if (mdist < ERASE_THRESH) { 
    306           if (!closerToEnd) freeform.reverseNodes(); 
    307           if (ctl) { 
    308             float[] nearest = new float[2]; 
    309             if (closerToEnd) nearest = end; 
    310             else nearest = beg; 
    311  
    312             // adjust curve length 
    313             int index = freeform.getNumNodes()-1; // last node in freef 
    314             float[] last = freeform.getNodeCoords(index); 
    315             double delta = MathUtil.getDistance(nearest, last); 
    316             freeform.setCurveLength(freeform.getCurveLength() - delta); 
    317  
    318             // delete last node 
    319             freeform.deleteNode(index); 
    320             if (!freeform.hasData()) { 
    321              setMode(CHILL); 
    322             } 
    323           } 
    324           else setMode(DRAW); 
    325         } 
    326       } 
    327       // do nothing if too far from curve 
     297      if (ctl) { 
     298        float thresh = (float) ERASE_THRESH * 
     299          OverlayUtil.getMultiplier(display); 
     300        eraseNearestNodeWithin(thresh, dx, dy); 
     301      } 
     302      else { 
     303        setMode(DRAW); 
     304      } 
    328305    } 
    329306    else if (mode == EDIT) { 
     
    443420  public void mouseUp(DisplayEvent e, int px, int py, 
    444421      float dx, float dy, int[] pos, int mods) { 
    445     //print ("mouseUp", "mouseUp begun. mode = " + mode); 
    446422    if (mode == DRAW) { 
    447423      freeform.truncateNodeArray(); 
     
    467443   *  measurements..._. Nucleic Acids Research, V 10 No. 5. (1982) p. 1694 
    468444   */ 
    469   private float[] smooth (float[] un, float[] cn1) { 
     445  private float[] smooth(float[] un, float[] cn1) { 
    470446    float[] cn = new float[2]; 
    471447    for (int i=0; i<2; i++) { 
     
    473449    } 
    474450    return cn; 
    475   } 
    476  
    477   /** Slices a freeform in two. */ 
    478   private void slice(OverlayFreeform freef, double dist, int seg, double weight) 
    479   { 
    480     int f1Start, f2Start, f1Stop, f2Stop; 
    481     OverlayFreeform f1, f2; 
    482  
    483     f1Start = 0; 
    484     f1Stop = seg; 
    485     f2Start = seg + 1; 
    486     f2Stop = freef.getNumNodes() - 1; 
    487  
    488     // if nearest point is a node itself, exclude it from both halves 
    489     if (weight == 0.0) f1Stop = seg - 1; 
    490     else if (weight == 1.0) f2Start = seg + 2; 
    491  
    492     float[][] oldNodes = freef.getNodes(); 
    493     int numNodes1 = f1Stop + 1; 
    494     int numNodes2 = f2Stop - f2Start + 1; 
    495  
    496     if (numNodes1 > 1) { 
    497       float[][] f1Nodes = new float[2][numNodes1]; 
    498  
    499       for (int i=0; i<2; i++) { 
    500         System.arraycopy(oldNodes[i], 0, f1Nodes[i], 0, numNodes1); 
    501       } 
    502  
    503       f1 = new OverlayFreeform(overlay, f1Nodes); 
    504       overlay.addObject(f1); 
    505       f1.setSelected(false); 
    506       f1.setDrawing(false); 
    507     } 
    508  
    509     if (numNodes2 > 1) { 
    510       float[][] f2Nodes = new float[2][numNodes2]; 
    511  
    512       for (int i = 0; i<2; i++) { 
    513         System.arraycopy(oldNodes[i], f2Start, f2Nodes[i], 0, numNodes2); 
    514       } 
    515  
    516       f2 = new OverlayFreeform(overlay, f2Nodes); 
    517       overlay.addObject(f2); 
    518       f2.setSelected(false); 
    519       f2.setDrawing(false); 
    520     } 
    521  
    522     // quietly dispose of the original freeform. 
    523     overlay.removeObject(freef); 
    524   } 
    525  
    526   /** Wraps distance and address of a freeform object. */ 
    527   protected class DistanceQuery { 
    528     public double dist, wt; 
    529     public int seg; 
    530     public OverlayFreeform freeform; 
    531  
    532     /** Constructs a DistanceQuery object. */ 
    533     public DistanceQuery(double[] distSegWt, OverlayFreeform f) { 
    534       this.freeform = f; 
    535       this.dist = distSegWt[0]; 
    536       this.seg = (int) distSegWt[1]; 
    537       this.wt = distSegWt[2]; 
    538     } 
    539  
    540     /** Whether the nearest node is an end node. */ 
    541     public boolean isNearEndNode() { 
    542       return (seg == 0 && wt == 0.0) || 
    543         (seg == freeform.getNumNodes()-2 && wt == 1.0); 
    544     } 
    545   } 
    546  
    547   /** Wraps information for redrawing a curve 
    548    *  Appears like a 'tendril' on screen */ 
    549   protected class Tendril { 
    550     public int start, stop, tip; 
    551     public boolean nodal; 
    552  
    553     public Tendril (int start, int stop, boolean nodal) { 
    554       this.start = start; 
    555       this.stop = stop; 
    556       this.nodal = nodal; 
    557       // initial value of tip is nonsensical.  Use as a check. 
    558       this.tip = -1; 
    559     } 
    560  
    561     public void increment() { 
    562       tendril.tip++; 
    563       tendril.start++; 
    564       tendril.stop++; 
    565     } 
    566451  } 
    567452 
     
    584469  } 
    585470 
     471  /** Compiles a list of other freeforms at the current dimensional position. */ 
     472  protected Vector getFreeforms(boolean includeThis) { 
     473    OverlayObject[] objs = overlay.getObjects(); 
     474    otherFreefs = new Vector(10); 
     475    for (int i=0; i<objs.length; i++) { 
     476      if (objs[i] instanceof OverlayFreeform &&  
     477          (includeThis || objs[i] != freeform)) { 
     478        otherFreefs.add(objs[i]); 
     479      } 
     480    } 
     481    return otherFreefs; 
     482  } 
     483 
    586484  /** 
    587485   * Changes the edit mode. Depending on the new mode, some properties may 
     
    599497 
    600498      // create list of freeforms 
    601       OverlayObject[] objs = overlay.getObjects(); 
    602       for (int i=0; i<objs.length; i++) { 
    603         if (objs[i] instanceof OverlayFreeform && objs[i] != freeform) { 
    604           otherFreefs.add(objs[i]); 
    605         } 
    606       } 
     499      otherFreefs = getFreeforms(false); 
    607500    } 
    608501    else if (mode == EDIT) { 
     
    614507    else if (mode == ERASE) { 
    615508      deselectAll(); 
    616       freeform.setDrawing(true); 
    617       freeform.setSelected(true); 
    618       otherFreefs.removeAllElements(); 
    619509    } 
    620510    else if (mode == CHILL) { 
     
    646536  { 
    647537    if (!head) f2.reverseNodes(); 
    648     float[][] newNodes = new float[2][f1.getNumNodes()+f2.getNumNodes()]; 
     538    float[][] f1Nodes = f1.getNodes(); 
     539    float[][] f2Nodes = f2.getNodes(); 
     540    int len1 = f1.getNumNodes(); 
     541    int len2 = f2.getNumNodes(); 
     542    float[][] newNodes = new float[2][len1 + len2]; 
    649543 
    650544    for (int i=0; i<2; i++) { 
    651       System.arraycopy(f1.getNodes()[i], 0, newNodes[i], 0, f1.getNumNodes()); 
    652       System.arraycopy(f2.getNodes()[i], 0, newNodes[i], 
    653         f1.getNumNodes(), f2.getNumNodes()); 
     545      System.arraycopy(f1Nodes[i], 0, newNodes[i], 0, len1);  
     546      System.arraycopy(f2Nodes[i], 0, newNodes[i], len1, len2); 
    654547    } 
    655548 
     
    659552    overlay.addObject(f3); 
    660553    freeform = f3; // store the new freeform 
     554  } 
     555 
     556  /** Erases the nearest node of freeforms nearby. */ 
     557  protected void eraseNearestNodeWithin(float thresh, float x, float y) { 
     558    Vector freefs = getFreeforms(true); // collect all other freeforms, 
     559    // including the current one tracked by this tool 
     560    if (freefs.size() == 0) return; 
     561    // compute closest node of closest freeform w/ respect to threshold 
     562    double minDist = Double.POSITIVE_INFINITY; 
     563    int minIndex = -1; 
     564    OverlayFreeform closest = null; 
     565    for (int i=0; i<freefs.size(); i++) { 
     566      OverlayFreeform current = (OverlayFreeform) freefs.get(i); 
     567      double[] distIndex = current.getNearestNode(x, y); 
     568      double dist = distIndex[0]; 
     569      int index = (int) distIndex[1]; 
     570      if (dist < thresh && dist < minDist) { 
     571        minDist = dist; 
     572        minIndex = index; 
     573        closest = current; 
     574      } 
     575    } 
     576 
     577    // remove that node if it exists, possibly splitting the freeform 
     578    // in two. 
     579    if (closest != null) { 
     580      OverlayFreeform[] children = closest.removeNode(minIndex); 
     581      // remove freeforms with 1 or 0 nodes that result  
     582      if (closest.getNumNodes() <= 1) overlay.removeObject(closest); 
     583      removeEmptyFreeforms(children); 
     584    } 
     585  } 
     586 
     587  /** Removes any freeforms with one or fewer nodes */ 
     588  protected void removeEmptyFreeforms(OverlayFreeform[] objs) { 
     589    for (int i=0; i<objs.length; i++) { 
     590      if (objs[i] == null) continue; 
     591      else if (objs[i].getNumNodes() <= 1) overlay.removeObject(objs[i]); 
     592    } 
    661593  } 
    662594 
     
    761693    } 
    762694  } 
     695 
     696 
     697  // -- Inner Classes --  
     698 
     699  /**  
     700   * Wraps information about the distance from a point to a freeform object.  
     701   * The nearest point is expressed in terms of its location on the freeform  
     702   * using two variables: 
     703   * <code>seg</code> the segment on which the closest point lies, between 
     704   * nodes seg and seg+1 
     705   * <code>wt</code> the relative distance along the segment where the closest 
     706   * point lies, between 0.0 and 1.0 
     707   */ 
     708  protected class DistanceQuery { 
     709 
     710    // -- Fields -- 
     711 
     712    /** The index of the first node of the closest segment */  
     713    public int seg; 
     714 
     715    /** 
     716     * The weight, between 0.0 and 1.0, representing the relative distance  
     717     * along the segment (seg, seg+1) that must be traveled from the node at 
     718     * index seg to reach the closest point on the curve. 
     719     */  
     720    public double wt; 
     721 
     722    /** The distance to the freeform. */ 
     723    public double dist; 
     724 
     725    /** The nearest freeform itself. */ 
     726    public OverlayFreeform freeform; 
     727 
     728    // -- Constructor --  
     729     
     730    /** Constructs a DistanceQuery object. */ 
     731    public DistanceQuery(double[] distSegWt, OverlayFreeform f) { 
     732      this.freeform = f; 
     733      this.dist = distSegWt[0]; 
     734      this.seg = (int) distSegWt[1]; 
     735      this.wt = distSegWt[2]; 
     736    } 
     737 
     738    // -- Methods --  
     739     
     740    /** Whether the nearest node is an end node. */ 
     741    public boolean isNearEndNode() { 
     742      return (seg == 0 && wt == 0.0) || 
     743        (seg == freeform.getNumNodes()-2 && wt == 1.0); 
     744    } 
     745  } 
     746 
     747  /**  
     748   * The Tendril class wraps information for the temporary section of a  
     749   * freeform, which appears like a 'tendril' on screen, created during an edit 
     750   * of an existing freeform.  
     751   * The FreeformTool uses this information to modify the freeform if the 
     752   * edit is successful, or to remove the tendril if the edit is aborted. 
     753   * 
     754   * The section of the freeform represented by a tendril is actually a 
     755   * loop, not a single line, like loop of thread pushed through a needle.  
     756   * The loop has two parts, between start and tip and between tip and stop. 
     757   * If an edit is completed, half of the tendril  
     758   */ 
     759  protected class Tendril { 
     760     
     761    // -- Fields -- 
     762 
     763    /** The index of the last node of the tendril. */ 
     764    public int start; 
     765 
     766    /** The index of the last node of the tendril. */ 
     767    public int stop;  
     768 
     769    /** The index of the tip node of the tendril. */ 
     770    public int tip; 
     771 
     772    /** Whether this tendril began on a node or in the middle of a segment. */ 
     773    public boolean nodal; 
     774 
     775    /** Constructs a new tendril. */ 
     776    public Tendril (int start, int stop, boolean nodal) { 
     777      this.start = start; 
     778      this.stop = stop; 
     779      this.nodal = nodal; 
     780      // initial value of tip is nonsensical.  Use as a check. 
     781      this.tip = -1; 
     782    } 
     783 
     784    /** Update the tendril to reflect that another node has been drawn. */ 
     785    public void increment() { 
     786      tendril.tip++; 
     787      tendril.start++; 
     788      tendril.stop++; 
     789    } 
     790  } 
    763791} // end class FreeformTool 
  • trunk/loci/visbio/overlays/OverlayNodedObject.java

    r2865 r2875  
    2929import loci.visbio.util.MathUtil; 
    3030import visad.*; 
    31 import visad.util.CursorUtil; 
    3231 
    3332/** 
     
    153152 
    154153  /** Returns whether this object is drawable, i.e., is of nonzero 
    155   *  size, area, length, etc. 
    156   */ 
     154  *  size, area, length, etc.  */ 
    157155  public boolean hasData() { 
    158156    if (isDrawing()) return (numNodes > 0); 
     
    224222  } 
    225223 
    226   /** 
    227    * Computes the shortest distance from this 
    228    * object's bounding box to the given point. 
    229    */ 
     224  /** Compute the shortest distance from this object to the given point. */ 
     225  public double getDistance (double x, double y) { 
     226     double[] distSegWt = MathUtil.getDistSegWt(nodes, (float) x, (float) y); 
     227     return distSegWt[0]; 
     228  } 
     229 
     230  /** Gets the nearest node to the given point. */ 
     231  protected double[] getNearestNode(float x, float y) { 
     232    int minIndex = -1; 
     233    double minDist = Double.POSITIVE_INFINITY; 
     234    float[] p = new float[]{x, y}; 
     235    for (int i=0; i<numNodes; i++) { 
     236      float[] c = {nodes[0][i], nodes[1][i]};  
     237      double dist = MathUtil.getDistance(c, p);  
     238      if (dist < minDist) { 
     239        minIndex = i; 
     240        minDist = dist; 
     241      } 
     242    } 
     243 
     244    return new double[]{minDist, (double) minIndex}; 
     245  } 
     246 
     247  /** Returns a specific statistic of this object */ 
     248  public String getStat(String name) { 
     249    if (name.equals(BOUNDS)) { 
     250      return "(" + x1 + ", " + y1 + "), (" + x2 + ", " + y2 + ")"; 
     251    } 
     252    else if (name.equals(NODES)) { 
     253      return "" + numNodes; 
     254    } 
     255    else if (name.equals(LENGTH)) { 
     256      return "" + (float) curveLength; 
     257    } 
     258    else return "No such statistic for this overlay type"; 
     259  } 
     260 
     261  /** Retrieves useful statistics about this overlay. */ 
     262  public String getStatistics() { 
     263    return BOUNDS + " = (" + x1 + ", " + y1 + "), (" + x2 + ", " + y2 + ")\n" + 
     264      NODES +" = " + numNodes + "\n" + 
     265      LENGTH + " = " + (float) curveLength + "\n"; 
     266  } 
     267 
     268  /** True iff this overlay has an endpoint coordinate pair. */ 
     269  public boolean hasEndpoint() { return true; } 
     270 
     271  /** True iff this overlay has a second endpoint coordinate pair. */ 
     272  public boolean hasEndpoint2() { return true; } 
     273 
     274  /** True iff there is a highlighted node. */ 
     275  public boolean isHighlightNode() { return highlightNode; } 
     276 
     277  /** Returns index of highlighted node. */ 
     278  public int getHighlightedNodeIndex() { return highlightIndex; } 
     279 
     280  /** True iff this overlay supports the filled parameter. */ 
     281  public boolean canBeFilled() { return true; } 
     282 
     283  /** True iff this overlay can be resized using X1, X2, Y1, Y2 entry boxes */ 
     284  public boolean areBoundsEditable() { return false; } 
     285  // currently, only non-noded objects can be resized this way. 
     286  // (Actually could perform scaling on all nodes) 
     287 
     288  // -- Object API methods -- 
     289 
     290  /** Computes the shortest distance from this object's bounding box to the 
     291   * given point. */ 
    230292  public double getDistanceToBoundingBox(double x, double y) { 
    231293    double xdist = 0; 
     
    237299    return Math.sqrt(xdist * xdist + ydist * ydist); 
    238300  } 
    239  
    240   /** Compute the shortest distance from this object to the given point */ 
    241   public double getDistance (double x, double y) { 
    242      double[] distSegWt = MathUtil.getDistSegWt(nodes, (float) x, (float) y); 
    243      return distSegWt[0]; 
    244   } 
    245  
    246   /** Returns a specific statistic of this object */ 
    247   public String getStat(String name) { 
    248     if (name.equals(BOUNDS)) { 
    249       return "(" + x1 + ", " + y1 + "), (" + x2 + ", " + y2 + ")"; 
    250     } 
    251     else if (name.equals(NODES)) { 
    252       return "" + numNodes; 
    253     } 
    254     else if (name.equals(LENGTH)) { 
    255       return "" + (float) curveLength; 
    256     } 
    257     else return "No such statistic for this overlay type"; 
    258   } 
    259  
    260   /** Retrieves useful statistics about this overlay. */ 
    261   public String getStatistics() { 
    262     return BOUNDS + " = (" + x1 + ", " + y1 + "), (" + x2 + ", " + y2 + ")\n" + 
    263       NODES +" = " + numNodes + "\n" + 
    264       LENGTH + " = " + (float) curveLength + "\n"; 
    265   } 
    266  
    267   /** True iff this overlay has an endpoint coordinate pair. */ 
    268   public boolean hasEndpoint() { return true; } 
    269  
    270   /** True iff this overlay has a second endpoint coordinate pair. */ 
    271   public boolean hasEndpoint2() { return true; } 
    272  
    273   /** True iff there is a highlighted node. */ 
    274   public boolean isHighlightNode() { return highlightNode; } 
    275  
    276   /** Returns index of highlighted node. */ 
    277   public int getHighlightedNodeIndex() { return highlightIndex; } 
    278  
    279   /** True iff this overlay supports the filled parameter. */ 
    280   public boolean canBeFilled() { return true; } 
    281  
    282   /** True iff this overlay can be resized using X1, X2, Y1, Y2 entry boxes */ 
    283   public boolean areBoundsEditable() { return false; } 
    284   // currently, only non-noded objects can be resized this way. 
    285   // (Actually could perform some rad scaling on all nodes) 
    286  
    287   // -- Object API methods -- 
    288301 
    289302  /** Highlight a node. */ 
     
    427440  public void setLastNode(float[] c) { 
    428441    setLastNode(c[0], c[1]); 
    429   } 
    430  
    431   /** Prints node array of current freeform; for debugging */ 
    432   private void printNodes(float[][] nodes) { 
    433     System.out.println("Printing nodes..."); 
    434     for (int i = 0; i < nodes[0].length; i++){ 
    435       System.out.println(i+":("+nodes[0][i]+","+nodes[1][i]+")"); 
    436     } 
    437442  } 
    438443 
     
    516521    } 
    517522  } 
     523   
     524  // NOTE: Right now this method returns Freeforms only, though it could be used 
     525  // on Polylines too.  
     526  /** Deletes a node from the freeform object, creating two new freeforms  
     527   *  if the node deleted is an interior node.  
     528   *  Returns resulting new freeforms if any. */ 
     529  public OverlayFreeform[] removeNode(int index) { 
     530    OverlayFreeform[] children = {null, null};  
     531    if (index == 0 || index == numNodes - 1) { 
     532      deleteNode(index); 
     533    } 
     534    else { 
     535      children = slice(index, 0.0); 
     536    } 
     537    return children; 
     538  } 
     539 
     540  // NOTE: Right now this method returns Freeforms only, though it could be used 
     541  // on Polylines too.  
     542  /**  
     543   * Slices a noded object in two.  
     544   * @param seg the index of the segment on which to slice 
     545   * @param weight the relative distance along the segment to slice 
     546   * The parameters seg and weight indicate a 'cut point' on the freeform.  
     547   * If weight is strictly between 0 and 1, the cut point is actually between 
     548   * two nodes, and all nodes of the original object are transferred to  
     549   * the child objects.  
     550   */ 
     551  private OverlayFreeform[] slice(int seg, double weight) 
     552  { 
     553    // create two new freeforms from the remainder of this freeform 
     554    OverlayFreeform f1 = null, f2 = null; 
     555 
     556    // compute indices into the node array of this freeform  
     557    int f1Start, f2Start, f1Stop, f2Stop; 
     558    f1Start = 0; 
     559    f1Stop = seg; 
     560    f2Start = seg + 1; 
     561    f2Stop = numNodes - 1; 
     562 
     563    // if the cut point is a node itself, exclude that node from both halves 
     564    if (weight == 0.0) f1Stop = seg - 1; 
     565    else if (weight == 1.0) f2Start = seg + 2; 
     566 
     567    int numNodes1 = f1Stop + 1; 
     568    int numNodes2 = f2Stop - f2Start + 1; 
     569 
     570    // create new object if number of nodes in object > 1 
     571    if (numNodes1 > 1) { 
     572      float[][] f1Nodes = new float[2][numNodes1]; 
     573 
     574      for (int i=0; i<2; i++) { 
     575        System.arraycopy(nodes[i], 0, f1Nodes[i], 0, numNodes1); 
     576      } 
     577 
     578      f1 = new OverlayFreeform(overlay, f1Nodes); 
     579      overlay.addObject(f1); 
     580      f1.setSelected(false); 
     581      f1.setDrawing(false); 
     582    } 
     583 
     584    // create new object if number of nodes in object > 1 
     585    if (numNodes2 > 1) { 
     586      float[][] f2Nodes = new float[2][numNodes2]; 
     587 
     588      for (int i = 0; i<2; i++) { 
     589        System.arraycopy(nodes[i], f2Start, f2Nodes[i], 0, numNodes2); 
     590      } 
     591 
     592      f2 = new OverlayFreeform(overlay, f2Nodes); 
     593      overlay.addObject(f2); 
     594      f2.setSelected(false); 
     595      f2.setDrawing(false); 
     596    } 
     597 
     598    // dispose of original freeform 
     599    overlay.removeObject(this); 
     600 
     601    return new OverlayFreeform[]{f1, f2}; 
     602  } 
    518603 
    519604  /** Reverses the node array (and therefore its internal orientation) */ 
     
    561646  } 
    562647 
    563   /** Returns a scaling value [domain length units/pixel] */ 
    564   protected float getScalingValue(DisplayImpl d) { 
    565     double[] oDom = CursorUtil.pixelToDomain(d, 0, 0); 
    566     double[] pDom = CursorUtil.pixelToDomain(d, 1, 0); 
    567     double scl = MathUtil.getDistance(oDom, pDom); 
    568     return (float) scl; 
     648  // -- Helper Methods for Debugging --  
     649 
     650  /** Prints node array of current freeform; for debugging */ 
     651  private void printNodes(float[][] nodes) { 
     652    System.out.println("Printing nodes..."); 
     653    for (int i = 0; i < nodes[0].length; i++){ 
     654      System.out.println(i+":("+nodes[0][i]+","+nodes[1][i]+")"); 
     655    } 
     656  } 
     657 
     658  /** Prints node array of current freeform.  For debugging. */ 
     659  public void printNodes() { 
     660    printNodes(nodes); 
    569661  } 
    570662} 
Note: See TracChangeset for help on using the changeset viewer.