如何启动bucket-fill算法

时间:2014-12-05 14:55:28

标签: java

我有一项任务,但我不知道如何开始。如果有人知道,请发给我一些帖子的链接。 我有一个最小的Java类,我需要更新“填充”方法,以便它按照以下描述工作:

  

假装您正在使用MS Paint或类似的图形应用程序。您的工作是实施铲斗填充工具。更具体地说,给定一个二维网格,用户点击的(X,Y)位置和颜色,设计一个可用于填充网格的适当部分的算法。

     

铲斗填充算法应该“绘制”连接到用户点击的像素的所有像素,一直到颜色变化的边界。因此,例如,如果用户点击白色像素并指定绿色,则桶填充工具将所有触摸的白色像素转换为绿色像素。但是,它不会影响图像中完全独立部分的白色像素。

最小的Java类:

class BucketFill {

    private char[][] pixels;

    public BucketFill(char[][] pixels) {
        this.pixels = pixels;
    }

    public void fill(int x, int y, char color) {
        // TODO: make this method work
    }

    public void inspect() {
        for (int y = 0; y < pixels.length; y++) {
            for (int x = 0; x < pixels[y].length; x++) {
                System.out.print(pixels[y][x]);
            }
            System.out.print("\n");
        }
    }

    public static void main(String argv[]) {
        char pixels[][] =
        {
            { 'O', 'X', 'X', 'X', 'X' },
            { 'X', 'O', 'O', 'O', 'X' },
            { 'X', 'O', '#', 'O', 'X' },
            { 'X', 'O', 'O', 'O', 'X' },
            { 'X', 'X', 'X', 'X', 'X' },
            { 'X', 'X', 'X', '#', '#' },
            { 'X', 'X', 'X', 'X', 'X' }
        };
        BucketFill bucketFill = new BucketFill(pixels);
        bucketFill.fill(0, 0, '*');
        bucketFill.fill(3, 0, 'O');
        bucketFill.fill(2, 1, '@');
        bucketFill.inspect();
    }
}

2 个答案:

答案 0 :(得分:3)

您可以使用Flood Fill Algorithm。 Flood Fill

您可以使用基于堆栈的递归来实现它。它只是递归地为连接的节点着色。

答案 1 :(得分:0)

这是我使用Java的方法-随时提出改进建议:

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Value;

import java.util.Arrays;
import java.util.Queue;
import java.util.function.Function;
import java.util.stream.Collectors;


public class FloodFill {

    /**
     * @param area The whole area
     * @param x User target position X
     * @param y User target position Y
     * @param color The new color
     */
    public static void paint(int[][] area, int x, int y, int color) {
        Coordinates target = new Coordinates(x, y);
        Queue<Coordinates> queue = Arrays.stream(Direction.values()).map(direction -> direction.getNextPosition().apply(target))
                .collect(Collectors.toCollection(LinkedNoReenterQueue::new));
        paint(area, queue, color, area[x][y]);
        switchColor(area, target, color);
    }

    @RequiredArgsConstructor
    @Getter
    private enum Direction {
        NORTH(coordinates -> {
            return new Coordinates(coordinates.x, coordinates.y + 1);
        }),
        SOUTH(coordinates -> {
            return new Coordinates(coordinates.x, coordinates.y - 1);
        }),
        EAST(coordinates -> {
            return new Coordinates(coordinates.x + 1, coordinates.y);
        }),
        WEST(coordinates -> {
            return new Coordinates(coordinates.x - 1, coordinates.y);
        }),
        NORTHEAST(coordinates -> {
            return new Coordinates(coordinates.x + 1, coordinates.y + 1);
        }),
        NORTHWEST(coordinates -> {
            return new Coordinates(coordinates.x - 1, coordinates.y + 1);
        }),
        SOUTHEAST(coordinates -> {
            return new Coordinates(coordinates.x + 1, coordinates.y - 1);
        }),
        SOUTHWEST(coordinates -> {
            return new Coordinates(coordinates.x - 1, coordinates.y - 1);
        });

        private final Function<Coordinates, Coordinates> nextPosition;

    }

    @Value
    private static class Coordinates {
       int x;
       int y;
    }

    private static void switchColor(int[][] area, Coordinates coordinates, int color) {
        area[coordinates.x][coordinates.y] = color;
    }

    private static boolean shouldSwitch(int[][] area, Coordinates coordinates, int previousColor) {
        return area[coordinates.x][coordinates.y] == previousColor;
    }

    private static boolean isValid(int[][] area, Coordinates coordinates) {
        return coordinates.getX() >= 0 && coordinates.getY() >= 0 && coordinates.getX() < area.length && coordinates.getY() < area[0].length;
    }

    private static void paint(int[][] area, Queue<Coordinates> queue, int newColor, int oldColor) {
        while (!queue.isEmpty()) {
            Coordinates nextPosition = queue.poll();
            if (isValid(area, nextPosition) && shouldSwitch(area, nextPosition, oldColor)) {
                switchColor(area, nextPosition, newColor);
                for (Direction direction : Direction.values()) {
                    queue.add(direction.getNextPosition().apply(nextPosition));
                }
            }
        }
    }

}

为避免重复填充相同的像素,请考虑以下数据结构:

导入java.util。*;

public abstract class NoReenterQueue<E> extends AbstractQueue<E> {

    private Set<E> ledger;

    protected NoReenterQueue() {
        this.ledger = new HashSet<>();
    }

    @Override
    public final boolean offer(E e) {
        if (!ledger.contains(e)) {
            ledger.add(e);
            insert(e);
        }
        return true;
    }

    protected abstract boolean insert(E e);

}

并且:

import java.util.Iterator;
import java.util.LinkedList;

public class LinkedNoReenterQueue<E> extends NoReenterQueue<E>{
    private LinkedList<E> linkedList = new LinkedList<>();

    @Override
    protected boolean insert(E e) {
        return linkedList.add(e);
    }

    @Override
    public Iterator<E> iterator() {
        return linkedList.iterator();
    }

    @Override
    public int size() {
        return linkedList.size();
    }

    @Override
    public E poll() {
        return linkedList.poll();
    }

    @Override
    public E peek() {
        return linkedList.peek();
    }
}

例如:

2222222
2111112
2122212
2122212
2122212
2111112
2222222

给出target =(3,3)和newColor = 3:

2222222
2111112
2133312
2133312
2133312
2111112
2222222