View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.renderer;
31  
32  import java.util.Arrays;
33  import java.util.List;
34  
35  import org.openimaj.math.geometry.point.Point2d;
36  
37  /**
38   * Implementation of the scan-line rasterisation algorithm for filling polygons.
39   * 
40   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
41   */
42  public class ScanRasteriser {
43  	/**
44  	 * Listen for scans.
45  	 * 
46  	 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
47  	 */
48  	public static interface ScanLineListener {
49  		/**
50  		 * Process the given scan
51  		 * 
52  		 * @param x1
53  		 *            starting x (inclusive)
54  		 * @param x2
55  		 *            ending x (inclusive)
56  		 * @param y
57  		 *            the y ordinate
58  		 */
59  		public abstract void process(int x1, int x2, int y);
60  	}
61  
62  	private ScanRasteriser() {
63  		// not instantiable
64  	}
65  
66  	/**
67  	 * The scan-fill algorithm. Scans are reported via the
68  	 * {@link ScanLineListener}.
69  	 * 
70  	 * @param p
71  	 *            the connected points to rasterise
72  	 * @param listener
73  	 *            the {@link ScanLineListener}
74  	 */
75  	public static void scanFill(List<Point2d> p, ScanLineListener listener) {
76  		int nScans;
77  
78  		final int n = p.size();
79  		int miny = Integer.MAX_VALUE;
80  		int maxy = Integer.MIN_VALUE;
81  		for (int i = 0; i < n; i++) {
82  			final Point2d pt = p.get(i);
83  
84  			miny = Math.min(miny, Math.round(pt.getY()));
85  			maxy = Math.max(maxy, Math.round(pt.getY()));
86  		}
87  
88  		final float[] scans = new float[n];
89  
90  		for (int y = miny; y <= maxy; y++) {
91  			nScans = 0;
92  
93  			for (int i = 0; i < n; i++) {
94  				final int index1, index2;
95  				if (i == 0) {
96  					index1 = n - 1;
97  					index2 = 0;
98  				} else {
99  					index1 = i - 1;
100 					index2 = i;
101 				}
102 
103 				final Point2d p1 = p.get(index1);
104 				final Point2d p2 = p.get(index2);
105 
106 				float y1 = p1.getY();
107 				float y2 = p2.getY();
108 
109 				float x1, x2;
110 				if (y1 < y2) {
111 					x1 = p1.getX();
112 					x2 = p2.getX();
113 				} else if (y1 > y2) {
114 					y2 = p1.getY();
115 					y1 = p2.getY();
116 					x2 = p1.getX();
117 					x1 = p2.getX();
118 				} else {
119 					continue;
120 				}
121 
122 				if ((y >= y1) && (y < y2)) {
123 					scans[nScans++] = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
124 				} else if ((y == maxy) && (y > y1) && (y <= y2)) {
125 					scans[nScans++] = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
126 				}
127 			}
128 
129 			Arrays.sort(scans, 0, nScans);
130 
131 			for (int i = 0; i < nScans; i += 2) {
132 				if (i + 1 < nScans)
133 					listener.process(Math.round(scans[i]), Math.round(scans[i + 1]), y);
134 			}
135 		}
136 	}
137 }