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.Area;
15  import java.awt.geom.GeneralPath;
16  import java.awt.geom.Line2D;
17  import java.awt.geom.Point2D;
18  import java.awt.image.BufferedImage;
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 FourPassImageShaper {
35      
36      public static Shape getShape(BufferedImage image) {
37          Area area = new Area(leftEdge(image));
38          area.intersect(new Area(bottomEdge(image)));
39          area.intersect(new Area(rightEdge(image)));
40          area.intersect(new Area(topEdge(image)));
41          return area;
42      }
43      /**
44       * Checks to see if point p is on a line that passes thru
45       * points p1 and p2. If p is on the line, extend the line
46       * segment so that it is from p1 to the location of p.
47       * If the point p is not on the line, update my shape
48       * with a line extending to the old p2 location, make
49       * the old p2 the new p1, and make p2 the old p
50       * @param p1
51       * @param p2
52       * @param p
53       * @param line
54       * @param path
55       * @return
56       */
57      private static Point2D detectLine(Point2D p1, Point2D p2, Point2D p, 
58              Line2D line, GeneralPath path) {
59      	
60          // check for line
61          // if p is on the line that extends thru p1 and p2
62      	if(line.ptLineDistSq(p) == 0) { // p is on the line p1,p2
63              // extend line so that p2 is at p
64              p2.setLocation(p);
65          } else { // its not on the current line
66          	// start a new line from p2 to p
67              p1.setLocation(p2);
68              p2.setLocation(p);
69              line.setLine(p1,p2);
70              // end the ongoing path line at the new p1 (the old p2)
71              path.lineTo((float)p1.getX(), (float)p1.getY());
72          }
73          return p2;
74      }
75      /**
76       * trace the left side of the image
77       * @param image
78       * @param path
79       * @return
80       */
81      private static Shape leftEdge(BufferedImage image) {
82          GeneralPath path = new GeneralPath();
83          Point2D p1 = null;
84          Point2D p2 = null;
85          Line2D line = new Line2D.Float();
86          Point2D p = new Point2D.Float();
87          int foundPointY = -1;
88          for(int i=0; i<image.getHeight(); i++) {
89              // go until we reach an opaque point, then stop
90              for(int j=0; j<image.getWidth(); j++) {
91                  if((image.getRGB(j,i) & 0xff000000) != 0) {
92                      // this is a point I want
93                      p = new Point2D.Float(j,i);
94                      foundPointY = i;
95                      break;
96                  }
97              }
98              if(foundPointY >= 0) {
99              	if(p2 == null) {
100             		// this is the first point found. project line to right edge
101             		p1 = new Point2D.Float(image.getWidth()-1, foundPointY);
102             		path.moveTo(p1.getX(), p1.getY());
103             		p2 = new Point2D.Float();
104             		p2.setLocation(p);
105             	} else {
106             		p2 = detectLine(p1, p2, p, line, path);
107             	}
108             }
109         }
110         path.lineTo(p.getX(), p.getY());
111         if(foundPointY >= 0) {
112         	path.lineTo(image.getWidth()-1, foundPointY);
113         }
114         path.closePath();
115         return path;
116     }
117     
118     /**
119      * trace the bottom of the image
120      * @param image
121      * @param path
122      * @param start
123      * @return
124      */
125     private static Shape bottomEdge(BufferedImage image) {
126         GeneralPath path = new GeneralPath();
127         Point2D p1 = null;
128         Point2D p2 = null;
129         Line2D line = new Line2D.Float();
130         Point2D p = new Point2D.Float();
131         int foundPointX = -1;
132         for(int i=0; i<image.getWidth(); i++) {
133             for(int j=image.getHeight()-1; j>=0; j--) {
134                 if((image.getRGB(i,j) & 0xff000000) != 0) {
135                     // this is a point I want
136                     p.setLocation(i,j);
137                     foundPointX = i;
138                     break;
139                 }
140             }
141             if(foundPointX >= 0) {
142             	if(p2 == null) {
143             		// this is the first point found. project line to top edge
144             		p1 = new Point2D.Float(foundPointX, 0);
145             		// path starts here
146             		path.moveTo(p1.getX(), p1.getY());
147             		p2 = new Point2D.Float();
148             		p2.setLocation(p);
149             	} else {
150             		p2 = detectLine(p1, p2, p, line, path);
151             	}
152             }
153         }
154         path.lineTo(p.getX(), p.getY());
155         if(foundPointX >= 0) {
156         	path.lineTo(foundPointX, 0);
157         }
158         path.closePath();
159         return path;
160     }
161     
162     /**
163      * trace the right side of the image
164      * @param image
165      * @param path
166      * @param start
167      * @return
168      */
169     private static Shape rightEdge(BufferedImage image) {
170         GeneralPath path = new GeneralPath();
171         Point2D p1 = null;
172         Point2D p2 = null;
173         Line2D line = new Line2D.Float();
174         Point2D p = new Point2D.Float();
175         int foundPointY = -1;
176         
177         for(int i=image.getHeight()-1; i>=0; i--) {
178             for(int j=image.getWidth()-1; j>=0; j--) {
179                 if((image.getRGB(j,i) & 0xff000000) != 0) {
180                     // this is a point I want
181                     p.setLocation(j,i);
182                     foundPointY = i;
183                     break;
184                 }
185             }
186             if(foundPointY >= 0) {
187             	if(p2 == null) {
188             		// this is the first point found. project line to top edge
189             		p1 = new Point2D.Float(0, foundPointY);
190             		// path starts here
191             		path.moveTo(p1.getX(), p1.getY());
192             		p2 = new Point2D.Float();
193             		p2.setLocation(p);
194             	} else {
195             		p2 = detectLine(p1, p2, p, line, path);
196             	}
197             }
198         }
199         path.lineTo(p.getX(), p.getY());
200         if(foundPointY >= 0) {
201         	path.lineTo(0, foundPointY);
202         }
203         path.closePath();
204         return path;
205     }
206     
207     /**
208      * trace the top of the image
209      * @param image
210      * @param path
211      * @param start
212      * @return
213      */
214     private static Shape topEdge(BufferedImage image) {
215         GeneralPath path = new GeneralPath();
216         Point2D p1 = null;
217         Point2D p2 = null;
218         Line2D line = new Line2D.Float();
219         Point2D p = new Point2D.Float();
220         int foundPointX = -1;
221         
222         for(int i=image.getWidth()-1; i>=0; i--) {
223             for(int j=0; j<image.getHeight(); j++) {
224                 if((image.getRGB(i,j) & 0xff000000) != 0) {
225                     // this is a point I want
226                     p.setLocation(i,j);
227                     foundPointX = i;
228                     break;
229                 }
230             }
231             if(foundPointX >= 0) {
232             	if(p2 == null) {
233             		// this is the first point found. project line to top edge
234             		p1 = new Point2D.Float(foundPointX, image.getHeight()-1);
235             		// path starts here
236             		path.moveTo(p1.getX(), p1.getY());
237             		p2 = new Point2D.Float();
238             		p2.setLocation(p);
239             	} else {
240             		p2 = detectLine(p1, p2, p, line, path);
241             	}
242             }
243         }
244         path.lineTo(p.getX(), p.getY());
245         if(foundPointX >= 0) {
246         	path.lineTo(foundPointX, image.getHeight()-1);
247         }
248         path.closePath();
249         return path;
250     }
251 }