001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.renderer; 031 032import java.util.Arrays; 033import java.util.List; 034 035import org.openimaj.math.geometry.point.Point2d; 036 037/** 038 * Implementation of the scan-line rasterisation algorithm for filling polygons. 039 * 040 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 041 */ 042public class ScanRasteriser { 043 /** 044 * Listen for scans. 045 * 046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 047 */ 048 public static interface ScanLineListener { 049 /** 050 * Process the given scan 051 * 052 * @param x1 053 * starting x (inclusive) 054 * @param x2 055 * ending x (inclusive) 056 * @param y 057 * the y ordinate 058 */ 059 public abstract void process(int x1, int x2, int y); 060 } 061 062 private ScanRasteriser() { 063 // not instantiable 064 } 065 066 /** 067 * The scan-fill algorithm. Scans are reported via the 068 * {@link ScanLineListener}. 069 * 070 * @param p 071 * the connected points to rasterise 072 * @param listener 073 * the {@link ScanLineListener} 074 */ 075 public static void scanFill(List<Point2d> p, ScanLineListener listener) { 076 int nScans; 077 078 final int n = p.size(); 079 int miny = Integer.MAX_VALUE; 080 int maxy = Integer.MIN_VALUE; 081 for (int i = 0; i < n; i++) { 082 final Point2d pt = p.get(i); 083 084 miny = Math.min(miny, Math.round(pt.getY())); 085 maxy = Math.max(maxy, Math.round(pt.getY())); 086 } 087 088 final float[] scans = new float[n]; 089 090 for (int y = miny; y <= maxy; y++) { 091 nScans = 0; 092 093 for (int i = 0; i < n; i++) { 094 final int index1, index2; 095 if (i == 0) { 096 index1 = n - 1; 097 index2 = 0; 098 } else { 099 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}