Back to index

moin  1.9.0~rc2
PolygonFigure.java
Go to the documentation of this file.
00001 /*
00002  * Fri Feb 28 07:47:05 1997  Doug Lea  (dl at gee)
00003  * Based on PolyLineFigure
00004  */
00005 
00006 package CH.ifa.draw.contrib;
00007 
00008 import java.awt.*;
00009 import java.util.*;
00010 import java.io.*;
00011 import java.net.*;
00012 import CH.ifa.draw.framework.*;
00013 import CH.ifa.draw.util.*;
00014 import CH.ifa.draw.standard.*;
00015 import CH.ifa.draw.figures.*;
00016 
00020 public  class PolygonFigure extends AttributeFigure {
00021     
00025     static final int TOO_CLOSE = 2;
00026     
00027     /*
00028      * Serialization support.
00029      */
00030     private static final long serialVersionUID = 6254089689239215026L;
00031     private int polygonFigureSerializedDataVersion = 1;
00032     
00033     protected Polygon fPoly = new Polygon();
00034     
00035     public PolygonFigure() {
00036        super();
00037     }
00038     
00039     public PolygonFigure(int x, int y) {
00040        fPoly.addPoint(x, y);
00041     }
00042     
00043     public PolygonFigure(Polygon p) {
00044        fPoly = new Polygon(p.xpoints, p.ypoints, p.npoints);
00045     }
00046     
00047     public Rectangle displayBox() {
00048        return bounds(fPoly);
00049     }
00050     
00051     
00052     public boolean isEmpty() {
00053        return (fPoly.npoints < 3 ||
00054               (size().width < TOO_CLOSE) && (size().height < TOO_CLOSE));
00055     }
00056     
00057     public Vector handles() {
00058        Vector handles = new Vector(fPoly.npoints);
00059        for (int i = 0; i < fPoly.npoints; i++)
00060            handles.addElement(new PolygonHandle(this, locator(i), i));
00061        handles.addElement(new PolygonScaleHandle(this));
00062        //handles.addElement(new PolygonPointAddHandle(this));
00063        return handles;
00064     }
00065     
00066     
00067     public void basicDisplayBox(Point origin, Point corner) {
00068        Rectangle r = displayBox();
00069        int dx = origin.x - r.x;
00070        int dy = origin.y - r.y;
00071        fPoly.translate(dx, dy);
00072        r = displayBox();
00073        Point oldCorner = new Point(r.x + r.width, r.y + r.height);
00074        Polygon p = getPolygon();
00075        scaleRotate(oldCorner, p, corner);
00076     }
00077     
00081     public Polygon getPolygon() {
00082        return new Polygon(fPoly.xpoints, fPoly.ypoints, fPoly.npoints);
00083     }
00084     
00085     public Point center() {
00086        return center(fPoly);
00087     }
00088     
00089     public Enumeration points() {
00090        Vector pts = new Vector(fPoly.npoints);
00091        for (int i = 0; i < fPoly.npoints; ++i)
00092            pts.addElement(new Point(fPoly.xpoints[i], fPoly.ypoints[i]));
00093        return pts.elements();
00094     }
00095     
00096     public int pointCount() {
00097        return fPoly.npoints;
00098     }
00099     
00100     public void basicMoveBy(int dx, int dy) {
00101        fPoly.translate(dx, dy);
00102     }
00103     
00104     public void drawBackground(Graphics g) {
00105        g.fillPolygon(fPoly);
00106     }
00107     
00108     public void drawFrame(Graphics g) {
00109        g.drawPolygon(fPoly);
00110     }
00111     
00112     public boolean containsPoint(int x, int y) {
00113        return fPoly.contains(x, y);
00114     }
00115     
00116     public Connector connectorAt(int x, int y) {
00117        return new ChopPolygonConnector(this);
00118     }
00119     
00123     public  void addPoint(int x, int y) {
00124        fPoly.addPoint(x, y);
00125        changed();
00126     }
00127     
00128     
00132     public  void setPointAt(Point p, int i) {
00133        willChange();
00134        fPoly.xpoints[i] = p.x;
00135        fPoly.ypoints[i] = p.y;
00136        changed();
00137     }
00138     
00142     public  void insertPointAt(Point p, int i) {
00143        willChange();
00144        int n = fPoly.npoints + 1;
00145        int[] xs = new int[n];
00146        int[] ys = new int[n];
00147        for (int j = 0; j < i; ++j) {
00148            xs[j] = fPoly.xpoints[j];
00149            ys[j] = fPoly.ypoints[j];
00150        }
00151        xs[i] = p.x;
00152        ys[i] = p.y;
00153        for (int j = i; j < fPoly.npoints; ++j) {
00154            xs[j+1] = fPoly.xpoints[j];
00155            ys[j+1] = fPoly.ypoints[j];
00156        }
00157        
00158        fPoly = new Polygon(xs, ys, n);
00159        changed();
00160     }
00161     
00162     public  void removePointAt(int i) {
00163        willChange();
00164        int n = fPoly.npoints - 1;
00165        int[] xs = new int[n];
00166        int[] ys = new int[n];
00167        for (int j = 0; j < i; ++j) {
00168            xs[j] = fPoly.xpoints[j];
00169            ys[j] = fPoly.ypoints[j];
00170        }
00171        for (int j = i; j < n; ++j) {
00172            xs[j] = fPoly.xpoints[j+1];
00173            ys[j] = fPoly.ypoints[j+1];
00174        }
00175        fPoly = new Polygon(xs, ys, n);
00176        changed();
00177     }
00178     
00182     public  void scaleRotate(Point anchor, Polygon originalPolygon, Point p) {
00183        willChange();
00184        
00185        // use center to determine relative angles and lengths
00186        Point ctr = center(originalPolygon);
00187        double anchorLen = Geom.length(ctr.x, ctr.y, anchor.x, anchor.y);
00188        
00189        if (anchorLen > 0.0) {
00190            double newLen = Geom.length(ctr.x, ctr.y, p.x, p.y);
00191            double ratio = newLen / anchorLen;
00192            
00193            double anchorAngle = Math.atan2(anchor.y - ctr.y, anchor.x - ctr.x);
00194            double newAngle = Math.atan2(p.y - ctr.y, p.x - ctr.x);
00195            double rotation = newAngle - anchorAngle;
00196            
00197            int n = originalPolygon.npoints;
00198            int[] xs = new int[n];
00199            int[] ys = new int[n];
00200            
00201            for (int i = 0; i < n; ++i) {
00202               int x = originalPolygon.xpoints[i];
00203               int y = originalPolygon.ypoints[i];
00204               double l = Geom.length(ctr.x, ctr.y, x, y) * ratio;
00205               double a = Math.atan2(y - ctr.y, x - ctr.x) + rotation;
00206               xs[i] = (int)(ctr.x + l * Math.cos(a) + 0.5);
00207               ys[i] = (int)(ctr.y + l * Math.sin(a) + 0.5);
00208            }
00209            fPoly =  new Polygon(xs, ys, n);
00210        }
00211        changed();
00212     }
00213     
00214     
00218     public void smoothPoints() {
00219        willChange();
00220        boolean removed = false;
00221        int n = fPoly.npoints;
00222        do {
00223            removed = false;
00224            int i = 0;
00225            while (i < n && n >= 3) {
00226               int nxt = (i + 1) % n;
00227               int prv = (i - 1 + n) % n;
00228               
00229               if ((distanceFromLine(fPoly.xpoints[prv], fPoly.ypoints[prv],
00230                                   fPoly.xpoints[nxt], fPoly.ypoints[nxt],
00231                                   fPoly.xpoints[i], fPoly.ypoints[i]) < TOO_CLOSE)) {
00232                   removed = true;
00233                   --n;
00234                   for (int j = i; j < n; ++j) {
00235                      fPoly.xpoints[j] = fPoly.xpoints[j+1];
00236                      fPoly.ypoints[j] = fPoly.ypoints[j+1];
00237                   }
00238               }
00239               else
00240                   ++i;
00241            }
00242        } while(removed);
00243        if (n != fPoly.npoints)
00244            fPoly =  new Polygon(fPoly.xpoints, fPoly.ypoints, n);
00245        changed();
00246     }
00247     
00252     public int splitSegment(int x, int y) {
00253        int i = findSegment(x, y);
00254        if (i != -1) {
00255            insertPointAt(new Point(x, y), i+1);
00256            return i + 1;
00257        }
00258        else
00259            return -1;
00260     }
00261     
00262     public Point pointAt(int i) {
00263        return new Point(fPoly.xpoints[i], fPoly.ypoints[i]);
00264     }
00265     
00269     public Point outermostPoint() {
00270        Point ctr = center();
00271        int outer = 0;
00272        long dist = 0;
00273        
00274        for (int i = 0; i < fPoly.npoints; ++i) {
00275            long d = Geom.length2(ctr.x, ctr.y, fPoly.xpoints[i], fPoly.ypoints[i]);
00276            if (d > dist) {
00277               dist = d;
00278               outer = i;
00279            }
00280        }
00281        
00282        return new Point(fPoly.xpoints[outer], fPoly.ypoints[outer]);
00283     }
00284     
00285     
00290     public int findSegment(int x, int y) {
00291        double dist = TOO_CLOSE;
00292        int best = -1;
00293        
00294        for (int i = 0; i < fPoly.npoints; i++) {
00295            int n = (i + 1) % fPoly.npoints;
00296            double d =  distanceFromLine(fPoly.xpoints[i], fPoly.ypoints[i],
00297                                     fPoly.xpoints[n], fPoly.ypoints[n],
00298                                     x, y);
00299            if (d < dist) {
00300               dist = d;
00301               best = i;
00302            }
00303        }
00304        return best;
00305     }
00306     
00307     public Point chop(Point p) {
00308        return chop(fPoly, p);
00309     }
00310     
00311     public String getMap() {
00312        String sense = (String)getAttribute("Sensitive");
00313        if (sense == null || sense.length() == 0)
00314            return "";
00315        try {
00316            sense = URLDecoder.decode(sense);
00317        } catch (Exception e) {}
00318        Rectangle box = displayBox();
00319        String coords = "";
00320        for (int i = 0; i < fPoly.npoints; i++) {
00321            if (i > 0)
00322               coords += ",";
00323            coords += fPoly.xpoints[i] + "," + fPoly.ypoints[i];
00324        }
00325        return "<area shape=\"poly\" coords=\"" +
00326            coords + "\" />\n";
00327     }
00328     
00329     public void write(StorableOutput dw) {
00330        super.write(dw);
00331        dw.writeInt(fPoly.npoints);
00332        for (int i = 0; i < fPoly.npoints; ++i) {
00333            dw.writeInt(fPoly.xpoints[i]);
00334            dw.writeInt(fPoly.ypoints[i]);
00335        }
00336     }
00337     
00338     public void read(StorableInput dr) throws IOException {
00339        super.read(dr);
00340        int size = dr.readInt();
00341        int[] xs = new int[size];
00342        int[] ys = new int[size];
00343        for (int i = 0; i < size; i++) {
00344            xs[i] = dr.readInt();
00345            ys[i] = dr.readInt();
00346        }
00347        fPoly = new Polygon(xs, ys, size);
00348     }
00349     
00353     public static Locator locator(final int pointIndex) {
00354        return new AbstractLocator() {
00355               public Point locate(Figure owner) {
00356                   PolygonFigure plf = (PolygonFigure)owner;
00357                   // guard against changing PolygonFigures -> temporary hack
00358                   if (pointIndex < plf.pointCount())
00359                      return ((PolygonFigure)owner).pointAt(pointIndex);
00360                   return new Point(-1, -1);
00361               }
00362            };
00363     }
00364     
00370     public static double distanceFromLine(int xa, int ya,
00371                                      int xb, int yb,
00372                                      int xc, int yc) {
00373        
00374        
00375        // source:http://vision.dai.ed.ac.uk/andrewfg/c-g-a-faq.html#q7
00376        //Let the point be C (XC,YC) and the line be AB (XA,YA) to (XB,YB).
00377        //The length of the
00378        //      line segment AB is L:
00379        //
00380        //                    ___________________
00381        //                   |        2         2
00382        //              L = \| (XB-XA) + (YB-YA)
00383        //and
00384        //
00385        //                  (YA-YC)(YA-YB)-(XA-XC)(XB-XA)
00386        //              r = -----------------------------
00387        //                              L**2
00388        //
00389        //                  (YA-YC)(XB-XA)-(XA-XC)(YB-YA)
00390        //              s = -----------------------------
00391        //                              L**2
00392        //
00393        //      Let I be the point of perpendicular projection of C onto AB, the
00394        //
00395        //              XI=XA+r(XB-XA)
00396        //              YI=YA+r(YB-YA)
00397        //
00398        //      Distance from A to I = r*L
00399        //      Distance from C to I = s*L
00400        //
00401        //      If r < 0 I is on backward extension of AB
00402        //      If r>1 I is on ahead extension of AB
00403        //      If 0<=r<=1 I is on AB
00404        //
00405        //      If s < 0 C is left of AB (you can just check the numerator)
00406        //      If s>0 C is right of AB
00407        //      If s=0 C is on AB
00408        
00409        int xdiff = xb - xa;
00410        int ydiff = yb - ya;
00411        long l2 = xdiff*xdiff + ydiff*ydiff;
00412        
00413        if (l2 == 0) return Geom.length(xa, ya, xc, yc);
00414        
00415        double rnum =  (ya-yc) * (ya-yb) - (xa-xc) * (xb-xa);
00416        double r = rnum / l2;
00417        
00418        if (r < 0.0 || r > 1.0) return Double.MAX_VALUE;
00419        
00420        double xi = xa + r * xdiff;
00421        double yi = ya + r * ydiff;
00422        double xd = xc - xi;
00423        double yd = yc - yi;
00424        return Math.sqrt(xd * xd + yd * yd);
00425        
00426        /*
00427          for directional version, instead use
00428          double snum =  (ya-yc) * (xb-xa) - (xa-xc) * (yb-ya);
00429          double s = snum / l2;
00430          
00431          double l = Math.sqrt((double)l2);
00432          return = s * l;
00433        */
00434     }
00435     
00440     public static Rectangle bounds(Polygon p) {
00441        int minx = Integer.MAX_VALUE;
00442        int miny = Integer.MAX_VALUE;
00443        int maxx = Integer.MIN_VALUE;
00444        int maxy = Integer.MIN_VALUE;
00445        int n = p.npoints;
00446        for (int i = 0; i < n; i++) {
00447            int x = p.xpoints[i];
00448            int y = p.ypoints[i];
00449            if (x > maxx) maxx = x;
00450            if (x < minx) minx = x;
00451            if (y > maxy) maxy = y;
00452            if (y < miny) miny = y;
00453        }
00454        
00455        return new Rectangle(minx, miny, maxx-minx, maxy-miny);
00456     }
00457     
00458     public static Point center(Polygon p) {
00459        long sx = 0;
00460        long sy = 0;
00461        int n = p.npoints;
00462        for (int i = 0; i < n; i++) {
00463            sx += p.xpoints[i];
00464            sy += p.ypoints[i];
00465        }
00466        
00467        return new Point((int)(sx/n), (int)(sy/n));
00468     }
00469     
00470     public static Point chop(Polygon poly, Point p) {
00471        Point ctr = center(poly);
00472        int cx = -1;
00473        int cy = -1;
00474        long len = Long.MAX_VALUE;
00475        
00476        // Try for points along edge
00477        
00478        for (int i = 0; i < poly.npoints; ++i) {
00479            int nxt = (i + 1) % poly.npoints;
00480            Point chop = Geom.intersect(poly.xpoints[i],
00481                                    poly.ypoints[i],
00482                                    poly.xpoints[nxt],
00483                                    poly.ypoints[nxt],
00484                                    p.x,
00485                                    p.y,
00486                                    ctr.x,
00487                                    ctr.y);
00488            if (chop != null) {
00489               long cl = Geom.length2(chop.x, chop.y, p.x, p.y);
00490               if (cl < len) {
00491                   len = cl;
00492                   cx = chop.x;
00493                   cy = chop.y;
00494               }
00495            }
00496        }
00497        // if none found, pick closest vertex
00498        //if (len ==  Long.MAX_VALUE) {
00499        { // try anyway
00500            for (int i = 0; i < poly.npoints; ++i) {
00501               long l = Geom.length2(poly.xpoints[i], poly.ypoints[i], p.x, p.y);
00502               if (l < len) {
00503                   len = l;
00504                   cx = poly.xpoints[i];
00505                   cy = poly.ypoints[i];
00506               }
00507            }
00508        }
00509        return new Point(cx, cy);
00510     }
00511     
00512 }
00513