Commits

Dmytro Kovalchuk committed 74521ff Merge

Merged issue-20 branch. Closes issue 20

  • Participants
  • Parent commits ce0919b, f1b0e32

Comments (0)

Files changed (22)

cross-stitch-api/src/main/java/net/anatolich/cstitch/schema/Canvas.java

 import net.anatolich.cstitch.palette.api.PaletteColor;
 
 /**
- *
+ * Canvas on which stitching schema is displayed.
  * @author anatolich
  */
 public interface Canvas {
     Legend getUsedColors();
 
     int getWidth();
+    
+    /**
+     * Checks whether provided coordinates belongs to canvas or not.
+     * @param coordinates coordinates to be checked.
+     * @return true when coordinates is on canvas.
+     */
+    boolean isOnCanvas(Coordinates coordinates);
 
     void setColorAt(PaletteColor color, Coordinates coords);
 

cross-stitch-api/src/main/java/net/anatolich/cstitch/schema/Coordinates.java

         return y;
     }
 
+    /**
+     * Gets coordinates situated on provided offset by provided direction.
+     * @param direction
+     * @param offset
+     * @return 
+     */
     public Coordinates byDirection(Direction direction, int offset) {
         switch (direction) {
             case NORTH:
         }
     }
 
+    /**
+     * Gets neighbor coordinates using provided direction. 
+     * @param direction
+     * @return 
+     */
     public Coordinates byDirection(Direction direction) {
         return byDirection(direction, 1);
     }

cross-stitch-api/src/main/java/net/anatolich/cstitch/schema/DefaultCanvas.java

 import net.anatolich.cstitch.palette.api.PaletteColor;
 
 /**
- * Default implementation of Canvas.
+ * Default rectangular canvas.
  * 
  * @author anatolich
  * @since 1.0
     }
 
     @Override
+    public boolean isOnCanvas(Coordinates c) {
+        return c.getX() >=0 && c.getX() < width 
+                && c.getY() >= 0 && c.getY() < height;
+    }
+    
+    @Override
     public void setColorAt(PaletteColor colorIndex, Coordinates coords) {
         setColorAt(colorIndex, coords.getX(), coords.getY());
     }

swing-api-impl/pom.xml

       <artifactId>swing-api</artifactId>
       <version>1.0-SNAPSHOT</version>
     </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>

swing-api-impl/src/main/java/net/anatolich/cstitch/swing/tools/fill/FillAlgorithm.java

+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.cstitch.swing.tools.fill;
+
+import net.anatolich.cstitch.palette.api.PaletteColor;
+import net.anatolich.cstitch.schema.Canvas;
+import net.anatolich.cstitch.schema.Coordinates;
+
+/**
+ * Fill closed region of canvas.
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public interface FillAlgorithm {
+
+    /**
+     * Fills region of canvas
+     * @param x x coordinate of starting point
+     * @param y y coordinate of starting point
+     * @param fillColor color to fill with
+     * @param canvas canvas to operate on
+     */
+    public void fill(Coordinates coords, PaletteColor fillColor, Canvas canvas);
+}

swing-api-impl/src/main/java/net/anatolich/cstitch/swing/tools/fill/impl/ShellFill.java

+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.cstitch.swing.tools.fill.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+import net.anatolich.cstitch.palette.api.PaletteColor;
+import net.anatolich.cstitch.schema.Canvas;
+import net.anatolich.cstitch.schema.Coordinates;
+import net.anatolich.cstitch.swing.tools.fill.FillAlgorithm;
+
+/**
+ * Simple implementation of region fill algorithm.
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class ShellFill implements FillAlgorithm {
+
+    @Override
+    public void fill(Coordinates coords, PaletteColor fillColor, Canvas canvas) {
+        PaletteColor replacedColor = canvas.getColorAt(coords);
+        if (replacedColor.equals(fillColor)){
+            return ;
+        }
+        canvas.setColorAt(fillColor, coords);
+        Set<Coordinates> shell = new HashSet<>();
+        shell.add(coords);
+
+        while (!shell.isEmpty()) {
+            Set<Coordinates> newShell = new HashSet<>();
+            for (Coordinates coordinates : shell) {
+
+                final Coordinates northCoords = coordinates.byDirection(Coordinates.Direction.NORTH);
+                checkCoordinates(canvas, northCoords, replacedColor, newShell, fillColor);
+
+                final Coordinates eastCoords = coordinates.byDirection(Coordinates.Direction.EAST);
+                checkCoordinates(canvas, eastCoords, replacedColor, newShell, fillColor);
+
+                final Coordinates southCoords = coordinates.byDirection(Coordinates.Direction.SOUTH);
+                checkCoordinates(canvas, southCoords, replacedColor, newShell, fillColor);
+
+                final Coordinates westCoords = coordinates.byDirection(Coordinates.Direction.WEST);
+                checkCoordinates(canvas, westCoords, replacedColor, newShell, fillColor);
+            }
+
+            shell = newShell;
+        }
+    }
+
+    private void checkCoordinates(Canvas canvas, Coordinates c, PaletteColor replacedColor, Set<Coordinates> newShell, PaletteColor fillColor) {                
+        if (canvas.isOnCanvas(c) && canvas.getColorAt(c) == replacedColor) {
+            newShell.add(c);
+            canvas.setColorAt(fillColor, c);
+        }
+    }
+}

swing-api-impl/src/main/java/net/anatolich/cstitch/swing/tools/impl/RegionFill.java

+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.cstitch.swing.tools.impl;
+
+import java.awt.event.MouseEvent;
+import javax.swing.ImageIcon;
+import net.anatolich.cstitch.schema.Coordinates;
+import net.anatolich.cstitch.swing.tools.fill.FillAlgorithm;
+import net.anatolich.cstitch.swing.tools.fill.impl.ShellFill;
+import net.anatolich.cstitch.ui.editor.tools.AbstractSwingTool;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class RegionFill extends AbstractSwingTool {
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        int x = getSchemaX(e);
+        int y = getSchemaY(e);
+        
+        Coordinates coordinates = new Coordinates(x, y);
+        FillAlgorithm filler = new ShellFill();
+        filler.fill(coordinates, getEditor().getSchema().getUsedColors().getSelectedColor(), getEditor().getSchema());
+    }
+
+    @Override
+    public String getName() {
+        return "Fill";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Fills closed region of canvas";
+    }
+    
+    @Override
+    protected ImageIcon getIcon() {
+        return new ImageIcon(getClass().getResource("/net/anatolich/cstitch/swing/tools/impl/color-fill.png"));
+    }
+
+    @Override
+    protected ImageIcon getSmallIcon() {
+        return new ImageIcon(getClass().getResource("/net/anatolich/cstitch/swing/tools/impl/color-fill_16.png"));
+    }
+}

swing-api-impl/src/main/java/net/anatolich/cstitch/swing/tools/impl/ToolsInjector.java

         service.registerTool(new Eraser());
         service.registerTool(new Zoom());
         service.registerTool(new ColorPicker());
+        service.registerTool(new RegionFill());
     }
 }

swing-api-impl/src/main/resources/net/anatolich/cstitch/swing/tools/impl/color-fill.png

Added
New image

swing-api-impl/src/main/resources/net/anatolich/cstitch/swing/tools/impl/color-fill_16.png

Added
New image

swing-api-impl/src/test/java/net/anatolich/cstitch/swing/tools/fill/AbstractFillAlgorithmTest.java

+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.cstitch.swing.tools.fill;
+
+import java.awt.Color;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import net.anatolich.cstitch.palette.api.PaletteColor;
+import net.anatolich.cstitch.schema.*;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public abstract class AbstractFillAlgorithmTest {
+
+    private PaletteColor baseColor = new PaletteColor("base", Color.BLACK); // Not filled color
+    private PaletteColor additionalColor = new PaletteColor("additional", Color.GREEN); // Yet another not filled color
+    private PaletteColor replacedColor = new PaletteColor("replaced", Color.WHITE); // Color to be replaced
+    private PaletteColor fillColor = new PaletteColor("fill", Color.BLUE); // Color which replaces
+    private Legend legend;
+    private final FillAlgorithm filler;
+
+    /**
+     * Creates test with specified implementation of fill algorithm.
+     * @param filler 
+     */
+    public AbstractFillAlgorithmTest(FillAlgorithm filler) {
+        this.filler = filler;
+    }
+
+    @Before
+    public void setUp() {
+        legend = new DefaultLegend(replacedColor);
+        legend.addColor(baseColor);
+        legend.addColor(additionalColor);
+        legend.addColor(fillColor);
+
+    }
+
+    protected final Canvas loadCanvas(InputStream is) {
+        try {
+            List<String> lines = new ArrayList<>();
+            BufferedReader layoutReader = new BufferedReader(new InputStreamReader(is));
+            String line = layoutReader.readLine();
+            while (line != null) {
+                lines.add(line);
+                line = layoutReader.readLine();
+            }
+            int width = 0;
+            for (String string : lines) {
+                if (string.length() > width) {
+                    width = string.length();
+                }
+            }
+
+            int height = lines.size();
+
+            Canvas canvas = new DefaultCanvas(width, height, legend);
+            for (int y = 0; y < height; y++) {
+                String layoutLine = lines.get(y);
+                for (int x = 0; x < layoutLine.length(); x++) {
+                    char symbol = layoutLine.charAt(x);
+                    PaletteColor color;
+                    switch (symbol) {
+                        case '0':
+                            color = replacedColor;
+                            break;
+                        case 'X':
+                            color = baseColor;
+                            break;
+                        case 'F':
+                            color = fillColor;
+                            break;
+                        case 'A':
+                            color = additionalColor;
+                            break;
+                        default:
+                            throw new RuntimeException("Wrong symbol in layout file: " + symbol);
+                    }
+
+                    canvas.setColorAt(color, x, y);
+                }
+
+            }
+
+            return canvas;
+
+        } catch (IOException ex) {
+            throw new RuntimeException("Invalid layout file format", ex);
+        }
+    }
+
+    // Returns input stream for resource file in classpath
+    private Canvas loadCanvas(String fileName) {
+        String path = fileName + ".layout";
+        InputStream stream = AbstractFillAlgorithmTest.class.getResourceAsStream(path);
+        return loadCanvas(stream);
+    }
+
+    protected final String exportCanvasLayout(Canvas canvas) {
+        StringBuilder builder = new StringBuilder();
+        for (int y = 0; y < canvas.getHeight(); y++) {
+            for (int x = 0; x < canvas.getWidth(); x++) {
+                PaletteColor color = canvas.getColorAt(x, y);
+                if (color == replacedColor) {
+                    builder.append('0');
+                }
+                if (color == baseColor) {
+                    builder.append('X');
+                }
+                if (color == fillColor) {
+                    builder.append('F');
+                }
+                if (color == additionalColor) {
+                    builder.append('A');
+                }
+            }
+            builder.append("\n");
+        }
+        return builder.toString();
+    }
+
+    @Test
+    public void testBoxFill() {
+        final String layoutName = "box";
+        final Coordinates coords = new Coordinates(2, 2);
+        testLayout(layoutName, coords);
+    }
+    
+    @Test
+    public void testCorridorFill() {
+        final Coordinates coords = new Coordinates(4, 1);
+        final String layoutName = "corridor";
+        testLayout(layoutName, coords);
+    }
+    
+    @Test
+    public void testDiagonalFill() {
+        final Coordinates coords = new Coordinates(2, 3);
+        String layoutName = "diagonal";
+        testLayout(layoutName, coords);
+        
+    }
+    
+    @Test
+    public void testEmptyFill() {
+        final String layoutName = "empty";
+        final Coordinates coords = new Coordinates(2, 3);
+        testLayout( layoutName, coords);        
+    }
+    
+    @Test
+    public void testSingleFill() {
+        final String layoutName = "single";
+        final Coordinates coords = new Coordinates(1, 1);
+        testLayout( layoutName, coords);        
+    }
+
+    private void testLayout(String layoutName, Coordinates coords) {
+        final String expectedLayoutName = layoutName + "-expected";
+        
+        Canvas canvas = loadCanvas(layoutName);
+        
+        Canvas expectedCanvas = loadCanvas(expectedLayoutName);
+        String expected = exportCanvasLayout(expectedCanvas);
+        
+        filler.fill(coords, fillColor, canvas);
+        
+        String actual = exportCanvasLayout(canvas);
+        
+        assertEquals("Canvas must be apppropriately filled", expected, actual);
+    }
+}

swing-api-impl/src/test/java/net/anatolich/cstitch/swing/tools/fill/impl/ShellFillTest.java

+/*
+ * Copyright 2012 Anatolich <anatolich@anatolich.net>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.anatolich.cstitch.swing.tools.fill.impl;
+
+import net.anatolich.cstitch.swing.tools.fill.AbstractFillAlgorithmTest;
+
+/**
+ *
+ * @author Anatolich <anatolich@anatolich.net>
+ */
+public class ShellFillTest extends AbstractFillAlgorithmTest{
+    
+    public ShellFillTest() {
+        super(new ShellFill());
+    }
+
+}

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/box-expected.layout

+XXXXXXXX
+0XFFFFX0
+0XFFFFX0
+0XFFFFX0
+0XXXXXX0

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/box.layout

+XXXXXXXX
+0X0000X0
+0X0000X0
+0X0000X0
+0XXXXXX0

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/corridor-expected.layout

+XXXXXXXXXX
+XXXXFXXXXX
+XXFFFFFFFX
+XXXFXXXXXX
+XFFFX0000X
+XXXXXXXXXX

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/corridor.layout

+XXXXXXXXXX
+XXXX0XXXXX
+XX0000000X
+XXX0XXXXXX
+X000X0000X
+XXXXXXXXXX

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/diagonal-expected.layout

+XXXXXXXX
+X0XXXXXX
+XXFXXXXX
+XFFFFFFX
+XXFFFFXX
+XXXXFFXX
+XXX0XXXX
+XXXXXXXX

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/diagonal.layout

+XXXXXXXX
+X0XXXXXX
+XX0XXXXX
+X000000X
+XX0000XX
+XXXX00XX
+XXX0XXXX
+XXXXXXXX

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/empty-expected.layout

+FFFFFFFF
+FFFFFFFF
+FFFFFFFF
+FFFFFFFF
+FFFFFFFF
+FFFFFFFF
+FFFFFFFF
+FFFFFFFF

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/empty.layout

+00000000
+00000000
+00000000
+00000000
+00000000
+00000000
+00000000
+00000000

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/single-expected.layout

+XXX
+XFX
+XXX

swing-api-impl/src/test/resources/net/anatolich/cstitch/swing/tools/fill/single.layout

+XXX
+X0X
+XXX