View Javadoc
1   /*
2    * Copyright (c) 2005, The JUNG Authors
3    * All rights reserved.
4    *
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
7    *
8    * Created on Jun 17, 2005
9    */
10  
11  package edu.uci.ics.jung.visualization;
12  
13  import java.awt.Shape;
14  import java.awt.geom.GeneralPath;
15  import java.awt.geom.Line2D;
16  import java.awt.geom.Point2D;
17  import java.awt.image.BufferedImage;
18  
19  
20  /**
21   * Provides Supplier methods that, given a BufferedImage, an Image,
22   * or the fileName of an image, will return a java.awt.Shape that
23   * is the contiguous traced outline of the opaque part of the image.
24   * This could be used to define an image for use in a Vertex, where
25   * the shape used for picking and edge-arrow placement follows the
26   * opaque part of an image that has a transparent background.
27   * The methods try to detect lines in order to minimize points
28   * in the path
29   * 
30   * @author Tom Nelson 
31   *
32   * 
33   */
34  public class PivotingImageShaper {
35      
36      /**
37       * the number of pixels to skip while sampling the
38       * images edges
39       */
40      static int sample = 1;
41      /**
42       * the first x coordinate of the shape. Used to discern
43       * when we are done 
44       */
45      static int firstx = 0;
46      
47      
48      /**
49       * Given an image, possibly with a transparent background, return
50       * the Shape of the opaque part of the image
51       * @param image the image whose shape is being returned
52       * @return the Shape
53       */
54      public static Shape getShape(BufferedImage image) {
55          firstx = 0;
56          return leftEdge(image, new GeneralPath());
57      }
58      
59      private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, 
60              Line2D line, GeneralPath path) {
61          if(p2 == null) {
62              p2 = p;
63              line.setLine(p1,p2);
64          }
65          // check for line
66          else if(line.ptLineDistSq(p) < 1) { // its on the line
67              // make it p2
68              p2.setLocation(p);
69          } else { // its not on the current line
70              p1.setLocation(p2);
71              p2.setLocation(p);
72              line.setLine(p1,p2);
73              path.lineTo((float)p1.getX(), (float)p1.getY());
74          }
75          return p2;
76      }
77      /**
78       * trace the left side of the image
79       * @param image
80       * @param path
81       * @return
82       */
83      private static Shape leftEdge(BufferedImage image, GeneralPath path) {
84          int lastj = 0;
85          Point2D p1 = null;
86          Point2D p2 = null;
87          Line2D line = new Line2D.Float();
88          for(int i=0; i<image.getHeight(); i+=sample) {
89              boolean aPointExistsOnThisLine = false;
90              // go until we reach an opaque point, then stop
91              for(int j=0; j<image.getWidth(); j+=sample) {
92                  if((image.getRGB(j,i) & 0xff000000) != 0) {
93                      // this is a point I want
94                      Point2D p = new Point2D.Float(j,i);
95                      aPointExistsOnThisLine = true;
96                      if(path.getCurrentPoint() != null) {
97                          // this is a continuation of a path
98                          p2 = detectLine(p1,p2,p,line,path);
99                      } else {
100                         // this is the first point in the path
101                         path.moveTo(j,i);
102                         firstx = j;
103                         p1 = p;
104                     }
105                     lastj = j;
106                     break;
107                 }
108             }
109             if(aPointExistsOnThisLine == false) {
110                 break;
111             }
112         }
113         return bottomEdge(image, path, lastj);
114     }
115     
116     /**
117      * trace the bottom of the image
118      * @param image
119      * @param path
120      * @param start
121      * @return
122      */
123     private static Shape bottomEdge(BufferedImage image, GeneralPath path, int start) {
124         int lastj = 0;
125         Point2D p1 = path.getCurrentPoint();
126         Point2D p2 = null;
127         Line2D line = new Line2D.Float();
128         for(int i=start; i<image.getWidth(); i+=sample) {
129             boolean aPointExistsOnThisLine = false;
130             for(int j=image.getHeight()-1; j>=0; j-=sample) {
131                 if((image.getRGB(i,j) & 0xff000000) != 0) {
132                     // this is a point I want
133                     Point2D p = new Point2D.Float(i,j);
134                     aPointExistsOnThisLine = true;
135                     p2 = detectLine(p1,p2,p,line,path);
136                     lastj = j;
137                     break;
138                 }
139             }
140             if(aPointExistsOnThisLine == false) {
141                 break;
142             }
143         }
144         return rightEdge(image, path, lastj);
145     }
146     
147     /**
148      * trace the right side of the image
149      * @param image
150      * @param path
151      * @param start
152      * @return
153      */
154     private static Shape rightEdge(BufferedImage image, GeneralPath path, int start) {
155         int lastj = 0;
156         Point2D p1 = path.getCurrentPoint();
157         Point2D p2 = null;
158         Line2D line = new Line2D.Float();
159         for(int i=start; i>=0; i-=sample) {
160             boolean aPointExistsOnThisLine = false;
161 
162             for(int j=image.getWidth()-1; j>=0; j-=sample) {
163                 if((image.getRGB(j,i) & 0xff000000) != 0) {
164                     // this is a point I want
165                     Point2D p = new Point2D.Float(j,i);
166                     aPointExistsOnThisLine = true;
167                     p2 = detectLine(p1,p2,p,line,path);
168                     lastj=j;
169                     break;
170                 }
171             }
172             if(aPointExistsOnThisLine == false) {
173                 break;
174             }
175         }
176         return topEdge(image, path, lastj);
177     }
178     
179     /**
180      * trace the top of the image
181      * @param image
182      * @param path
183      * @param start
184      * @return
185      */
186     private static Shape topEdge(BufferedImage image, GeneralPath path, int start) {
187         Point2D p1 = path.getCurrentPoint();
188         Point2D p2 = null;
189         Line2D line = new Line2D.Float();
190         for(int i=start; i>=firstx; i-=sample) {
191             boolean aPointExistsOnThisLine = false;
192             for(int j=0; j<image.getHeight(); j+=sample) {
193                 if((image.getRGB(i,j) & 0xff000000) != 0) {
194                     // this is a point I want
195                     Point2D p = new Point2D.Float(i,j);
196                     aPointExistsOnThisLine = true;
197                     p2 = detectLine(p1,p2,p,line,path);
198                     break;
199                 }
200             }
201             if(aPointExistsOnThisLine == false) {
202                 break;
203             }
204         }
205         path.closePath();
206         return path;
207     }
208 }