Check if a curve is closed - java

How can I check efficiently if a curve is closed? For example look this figure:
The curve will always be white on a black background.
I tried with flood fill algorithm but not works well with this situation (I don't understand how modify it).
Here the code:
public static boolean isWhite(BufferedImage image, int posX, int posY) {
Color color = new Color(image.getRGB(posX, posY));
int r=color.getRed();
int g=color.getGreen();
int b=color.getBlue();
if(r==0&&g==0&&b==0)
return false;
return true;
}
public static void checkClosed(BufferedImage bimg) {
boolean[][] painted = new boolean[bimg.getHeight()][bimg.getWidth()];
for (int i = 0; i < bimg.getHeight(); i++) {
for (int j = 0; j < bimg.getWidth(); j++) {
if (isWhite(bimg, j, i) && !painted[i][j]) {
Queue<Point> queue = new LinkedList<Point>();
queue.add(new Point(j, i));
int pixelCount = 0;
while (!queue.isEmpty()) {
Point p = queue.remove();
if ((p.x >= 0) && (p.x < bimg.getWidth() && (p.y >= 0) && (p.y < bimg.getHeight()))) {
if (!painted[p.y][p.x] && isWhite(bimg, p.x, p.y)) {
painted[p.y][p.x] = true;
pixelCount++;
queue.add(new Point(p.x + 1, p.y));
queue.add(new Point(p.x - 1, p.y));
queue.add(new Point(p.x, p.y + 1));
queue.add(new Point(p.x, p.y - 1));
}
}
}
System.out.println("Blob detected : " + pixelCount + " pixels");
}
}
}
}

The way to see if the boundary in your image is closed is by doing a flood fill of the boundary starting at all the image edge pixels. That is, you put all the background pixels that are at the image edge on the queue, then flood fill from there.
Next, check to see if any background pixels are left. If the flood fill filled inside the object, the boundary wasn’t closed.

Related

How to convert the mouse position in pixels into the row and column on the grid?

I am basically making a battleship guessing game where you have to the position of a ship by the click of your mouse. When a position of the ship is guessed correctly it deletes that ship cell from the array and when every cell is guessed correctly, the game is over.
What I am now struggling on is to
keep the ship cells within the canvas
convert the mouse position in pixels into the row and column on the grid
if the guess is correct, add the guess to the hit array and if missed adding it to the miss array.
when a guess is made, in addition to colouring the cell, print either “Hit!” or “Miss!” on the cell
sinking the ship when all cells have been hit
In your code you've mixed rows and columns. The x coordinate goes from the left to the right, this are the columns. The y axis goes from the top to the bottom and corresponds to the rows.
Don't store column, row, hit and miss in arrays. But use 2-dimensional arrays to store the position of the ship and the positions of mouse clicks:
boolean [][] ship;
boolean [][] click;
keep the ship cells within the canvas
If the direction is horizontal, then the x start position of the ship has to be less than NUM_COLS - shipLength:
randomX = (int)random(NUM_COLS - shipLength);
randomY = (int)random(NUM_ROWS);
If the direction is horizontal, then the y start position of the ship has to be less than NUM_ROWS - shipLength:
randomX = (int)random(NUM_COLS);
randomY = (int)random(NUM_ROWS - shipLength);
Call randomShip in setup rather than draw:
void setup() {
size(600, 500);
randomShip();
println(store);
}
void draw() {
// randomShip(); <---- delete
drawCells (row, column, shipLength, (255) );
}
Generate the random position and size of the ship in randomShip;
void randomShip () {
ship = new boolean[NUM_COLS][NUM_ROWS];
click = new boolean[NUM_COLS][NUM_ROWS];
shipLength = (int)random (3, 8);
int store = (int)random(vert, horz);
if (store >= 0) {
int randomX = (int)random(NUM_COLS - shipLength);
int randomY = (int)random(NUM_ROWS);
for (int i = 0; i < shipLength; i++ ) {
ship[randomX + i][randomY] = true;
}
} else {
int randomX = (int)random(NUM_COLS);
int randomY = (int)random(NUM_ROWS - shipLength);
for (int i = 0; i < shipLength; i++ ) {
ship[randomX][randomY+1] = true;
}
}
println(shipLength);
}
convert the mouse position in pixels into the row and column on the grid
if the guess is correct, add the guess to the hit array and if missed adding it to the miss array.
The cell which was clicked can be get by the dividing the mouse coordinates mouseX and mouseY by CELLSIZE
int cell_x = mouseX / CELLSIZE;
int cell_y = mouseY / CELLSIZE;
Store mark the clicked cells and count the hits and miss in mouseClicked:
void mouseClicked () {
int cell_x = mouseX / CELLSIZE;
int cell_y = mouseY / CELLSIZE;
if (!click[cell_x][cell_y]) {
click[cell_x][cell_y] = true;
if ( ship[cell_x][cell_y] ) {
hitCount ++;
} else {
missCount ++;
}
}
}
when a guess is made, in addition to colouring the cell, print either “Hit!” or “Miss!” on the cell
Evaluate the ship position (ship[][]) and clicked positions (click[][]) in drawCells. Draw the cells and the text dependent on the states in 2 nested loops:
void drawCells(int colour) {
for (int i = 0; i < NUM_COLS; i++) {
for (int j = 0; j < NUM_ROWS; j++) {
float x = i * CELLSIZE;
float y = j * CELLSIZE;
if (ship[i][j]) {
fill (colour);
rect(x, y, CELLSIZE, CELLSIZE);
}
if (click[i][j]) {
fill(255, 0, 0);
textSize(15);
text(ship[i][j] ? "hit" : "miss", x+10, y+30);
}
}
}
}
sinking the ship when all cells have been hit
Handle the end of the game in draw:
e.g.
void draw() {
drawCells(255);
if (hitCount == shipLength ) {
// [...]
}
}
Full code listing:
final int CELLSIZE = 50;
final int NUM_ROWS = 10;
final int NUM_COLS = 12;
int horz = (int)random(50);
int vert = (int)random(-50);
int store;
int shipLength;
boolean [][] ship;
boolean [][] click;
int hitCount = 0;
int missCount = 0;
void setup() {
size(600, 500);
randomShip();
println(store);
}
void draw() {
drawCells(255);
if (hitCount == shipLength ) {
// [...]
}
}
void drawCells(int colour) {
for (int i = 0; i < NUM_COLS; i++) {
for (int j = 0; j < NUM_ROWS; j++) {
float x = i * CELLSIZE;
float y = j * CELLSIZE;
if (ship[i][j]) {
fill (colour);
rect(x, y, CELLSIZE, CELLSIZE);
}
if (click[i][j]) {
fill(255, 0, 0);
textSize(15);
text(ship[i][j] ? "hit" : "miss", x+10, y+30);
}
}
}
}
void randomShip () {
ship = new boolean[NUM_COLS][NUM_ROWS];
click = new boolean[NUM_COLS][NUM_ROWS];
hitCount = 0;
missCount = 0;
shipLength = (int)random (3, 8);
int store = (int)random(vert, horz);
if (store >= 0) {
int randomX = (int)random(NUM_COLS - shipLength);
int randomY = (int)random(NUM_ROWS);
for (int i = 0; i < shipLength; i++ ) {
ship[randomX + i][randomY] = true;
}
} else {
int randomX = (int)random(NUM_COLS);
int randomY = (int)random(NUM_ROWS - shipLength);
for (int i = 0; i < shipLength; i++ ) {
ship[randomX][randomY+1] = true;
}
}
println(shipLength);
}
void mouseClicked () {
int cell_x = mouseX / CELLSIZE;
int cell_y = mouseY / CELLSIZE;
if (!click[cell_x][cell_y]) {
click[cell_x][cell_y] = true;
if ( ship[cell_x][cell_y] ) {
hitCount ++;
} else {
missCount ++;
}
}
}

Find index near mouseX, mouseY with matrix

I am creating a game sort of based on the Game of Life and Death. The goal of the project is when the cells turn white they will reproduce with other nearby cells, and when they are black they will die randomly. This part of the game I want to implement and have it run based on the state of the cells (life or death).
Currently, when the cells are white and the mouse is pressed the cell will turn white and stay white until the mouse is unclicked and clicked again. When they are black they will be erased if the mouse is over them. Unfortunately, that also removes the array value from the matrix I use to keep track of the current cells. How can I use matrixes without getting an Array Index out of bounds error? I've tried adding a second if statement to search for nearby cells, but it searches even if the cells aren't drawn. I including the entire code here as I do not want to exclude any parts. For reference, I am using Processing 3 for Java.
int value = 0;
int cols, rows;
int scl = 20;
boolean[][] matrix = new boolean[scl+1][scl+1];
boolean life;
boolean death;
void setup() {
frameRate(25);
size(400, 400);
int w = 400;
int h = 400;
cols = w / scl;
rows = h / scl;
}
void draw() {
background(255);
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
int xpos = x*scl;
int ypos = y*scl;
//cell border color
stroke(55);
if ((mouseX >= xpos && mouseX <= xpos+scl) &&
(mouseY >= ypos && mouseY <= ypos+scl)) {
if (mousePressed == true) {
//println("Clicked at: " + xpos + " and " + ypos);
if (!matrix[xpos/scl][ypos/scl]) {
matrix[xpos/scl][ypos/scl] = true;
if (life){
matrix[xpos/scl][ypos/scl] = true;
println("Living at " + xpos + " and " + ypos);
}
if (death){
matrix[xpos/scl][ypos/scl] = false;
println("Dying at " + xpos + " and " + ypos);
}
} else {
matrix[xpos/scl][ypos/scl] = false;
}
//fill(100);
fill(value);
}
//println("Mouse at: " + xpos + " and " + ypos);
} else {
fill(50);
}
if (matrix[x][y]) {
fill(value);
}
rect(xpos, ypos, scl, scl);
}
}
}
void mousePressed() {
if (value == 0) {
life = true;
death = false;
value = 245;
} else {
life = false;
death = true;
value = 0;
}
}

Java HashMap in Hashmap.Store all Coordinates of Pixels

i have a problem regarding pixel analysis for an image.
I am trying to analyse every pixel that is white (R=255,G=255,B=255).
The problem is the storing/ reading of these data.
for (int i = 0; i <= Map.getHeight(); i++) {
for (int j = 0; j <= Map.getWidth(); j++) {
if (Map.getColor(j, i).getBlue() == 255 && Map.getColor(j, i).getRed() == 255
&& Map.getColor(j, i).getGreen() == 255)
{
// coordsX = new HashMap<>();
coordsX.put(j, new Rectangle(j, i, 5, 5));
}
}
coordsY.put(i, coordsX);
}
System.out.println();
}
The reading function is the following:
for (Entry<Integer, HashMap<Integer, Rectangle>> e : coordsY.entrySet()) {
// HashMap<Integer, Rectangle> coordsX = coordsY.get(y);
HashMap<Integer, Rectangle> coordsX = e.getValue();
if (coordsX != null) {
for (Entry<Integer, Rectangle> entry : coordsX.entrySet()) {
Rectangle rect = entry.getValue();
g.setColor(Color.red);
g.draw(rect);
if (this.car2.intersects(rect)) {
intersectsTrack = true;
}
}
}
}
The problem is that when i outline:
coordsX = new HashMap<>();
like done above, i only get all one x value for one y value
example.
If i dont outline this line it is the other way around.
example.
Can you help me fixing this problem?
Kind Regards
You're creating a new coordsX everytime you've discovered a new white pixel. That's probably not what you intended. So for each y there will be one map coordsX with only one entry, any previous entry is discarded.
Also, I like to suggest to create a class for representing a 2D coordinate, let's call it Coordinate, then your algorithm gets much easier to implement. (or maybe there's already such a thing, for instance Point?)
class Coordinate {
private int x, y; // plus getter, setter, etc.
public int hashCode() {
return Objects.hash(x, y);
}
public boolean equals(Object obj) {
if (obj == this)
return true;
else if (!(obj instanceof Coordinate))
return false;
Coordinate that = (Coordinate) obj;
return this.x == that.x && this.y == that.y;
}
public String toString() {
return "(" + x + ", " + y + ")";
}
}
// ...
Map<Coordinate, Rectangle> coords = new HashMap<>();
for (int y = 0; y <= Map.getHeight(); y++) {
for (int x = 0; x <= Map.getWidth(); x++) {
Color color = Map.getColor(x, y);
if (color.getBlue() == 255 && color.getRed() == 255 && color.getGreen() == 255) {
Coordinate coordinate = new Coordinate(x, y);
Rectangle rectangle = new Rectangle(x, y, 5, 5);
coords.put(coordinate, rectangle);
}
}
}
for (Rectangle rectangle : coords.values()) {
g.setColor(Color.red);
g.draw(rect);
}
What you're saying makes sense since your code coordsX.put(j, new Rectangle(j, i, 5, 5)); associates the x coordinate j with a Rectangle, while your code coordsY.put(i, coordsX); associates the final y coordinate i with the coordsX HashMap.
However, due to associating with only an x or y value, each put() call will overwrite if you put to the same coordinate, which you do with j i times. It might be better given your intention to have a single HashMap of an x/y pair class (such as a Point2D) that you map to a rectangle.
You can further optimize this by using some knowledge about a rectangle. If you want to store some x/y pairs for certain colors of an image, you can use a one Dimensional array, knowing you can index each coordinate as x + y * width, or in your case j + i * Map.getWidth(). I would design your array "hashmap" like so:
// put values in the array.
Rectangle[] coords = new Rectangle[Map.getWidth() * Map.getHeight()];
for (int i = 0; i <= Map.getHeight(); i++) {
for (int j = 0; j <= Map.getWidth(); j++) {
int index = j + i * map.getWidth();
if (/* i,j has the correct color */) {
coords[index] = new Rectangle(j, i, 5, 5);
}
}
}
// read values from the array
for (int i = 0; i <= Map.getHeight(); i++) {
for (int j = 0; j <= Map.getWidth(); j++) {
Rectangle rect = coords[j + i * Map.getWidth()];
if(rect == null) continue;
//Your read logic here
g.setColor(Color.red);
g.draw(rect);
if (this.car2.intersects(rect)) {
intersectsTrack = true;
}
}
}

Tile change color on mouse hover - prevent selecting more than one tile at once

I am creating a battle ship game, where each round the player can select 4 ship routes by selecting the 4 closest tiles to him, with a mouse.
Now in the following gif, you can see if I will hover on the very edge of a tile, it will select two tiles at once, or even 4 at once if you're holding your mouse in middle of 4 tiles:
(source: gyazo.com)
This should not really be happening in the game, how can I prevent that?
This is the updating code:
/**
*
* #param x mouse X
* #param y mouse Y
*/
public void checkHover(int x, int y) {
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[1].length; j++) {
// get coordinates from tile
int x1 = i * (sprite.getWidth() - 1);
int y1 = j * (sprite.getHeight() - 1);
// If we have screen translate, we can cancel it for
// this situation
int realX = x - this.translate.getX();
int realY = y - this.translate.getY();
// checking if mouse inside tile
if (realX >= x1 && realX <= x1 + sprite.getWidth() &&
realY >= y1 && realY <= y1 + sprite.getHeight()) {
// set tile to hovered sprite
this.tiles[i][j].setHover(true);
}
else {
// cancel hovered sprite
this.tiles[i][j].setHover(false);
}
}
}
}
How can I prevent that?
Instead of the two loops I would calculate the index of the 2d array based off the x and y position of the mouse, by dividing the x and y position by how much space there is from one tile to the next (which looks to be the tile width plus 1, the empty border). This will cause border hovers to favor a certain side, but it looks like you don't want to select two tiles anyways.
Once you have the index of the tile, switch the highlight on, and keep a reference to it with an instance variable like lastHighlightedTile, but before you do that, also call lastHighlightedTile.setHover(false);.
If the index of the tile is invalid, as if the mouse is out of the tile area, don't access the array and still call lastHighlightedTile.setHover(false);.
This should fix highlighting multiple tiles, and will also allow you to turn off the previous highlight (the solution of breaking out of the current loop might not turn off the previous highlight).
Assuming width of sprite is equal to 50, I see that first title has x1 to x2 coordinate (0 - 50), second title has (49 - 100), third has (99 - 150)... Same for y dimension.
So when pointer is at (x) = (50), it hovers (x1 - x2) (0-50) and (49-100) titles.
Why are you subtracting 1 from sprite width / height?
try with this:
// get coordinates from tile
int x1 = i * (sprite.getWidth());
int y1 = j * (sprite.getHeight());
Edit: I wrote sample app, which shoved me solution:
public class App {
Tile[][] tiles;
Sprite sprite;
public static void main(String[] args) {
App app = new App();
app.init();
app.checkHover(50, 50);
app.printHovers();
}
private void init() {
sprite = new Sprite();
tiles = new Tile[10][10];
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[1].length; j++) {
tiles[i][j] = new Tile();
}
}
}
public void checkHover(int x, int y) {
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[i].length; j++) {
// get coordinates from tile
int x1 = i * (sprite.getWidth());
int y1 = j * (sprite.getHeight());
// If we have screen translate, we can cancel it for
// this situation
// int realX = x - this.translate.getX();
// int realY = y - this.translate.getY();
int realX = x;
int realY = y;
// checking if mouse inside tile
if (realX >= x1 && realX < x1 + sprite.getWidth()
&& realY >= y1 && realY < y1 + sprite.getHeight()) {
// set tile to hovered sprite
this.tiles[i][j].setHover(true);
} else {
// cancel hovered sprite
this.tiles[i][j].setHover(false);
}
}
}
}
public void printHovers() {
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[i].length; j++) {
System.out.print((tiles[i][j].isHover() ? "Y" : "O") + " ");
}
System.out.println();
}
}
public class Sprite {
public int getWidth() {
return 50;
}
public int getHeight() {
return 50;
}
}
public class Tile {
private boolean hover;
public boolean isHover() {
return hover;
}
public void setHover(boolean hover) {
this.hover = hover;
}
}
}
It works fine for (49, 49), (49, 50), (50, 49) and (50, 50) coords.
So, first you have to remove -1 subtractions, and change <= test to <.
EDIT: Different solution:
Simply declare a boolean at the beginning of the method, and set only if that boolean is still false. Once you set the tile on, also set the boolean to true, which will prevent others from being set on.
public void checkHover(int x, int y) {
boolean hasTurnedOneOn = false;
for (int i = 0; i < tiles[0].length; i++) {
for (int j = 0; j < tiles[1].length; j++) {
// get coordinates from tile
int x1 = i * (sprite.getWidth() - 1);
int y1 = j * (sprite.getHeight() - 1);
// If we have screen translate, we can cancel it for
// this situation
int realX = x - this.translate.getX();
int realY = y - this.translate.getY();
// checking if mouse inside tile
if (realX >= x1 && realX <= x1 + sprite.getWidth() &&
realY >= y1 && realY <= y1 + sprite.getHeight() && !hasTurnedOneOn) {
// set tile to hovered sprite
this.tiles[i][j].setHover(true);
hasTurnedOneOn = true;
}
else {
// cancel hovered sprite
this.tiles[i][j].setHover(false);
}
}
}
}

Moving Shapes along an arc path with java graphics

Pardon my naivety, its my first time here and first time dealing with animation of graphics in java. I'm trying to accomplish an animation of star shapes that moves along a sort of arc(trying to simulate an orbit on a 2d wise). The orbit Action is used with a Timer to animate the stars.
Simply put, I have drawn several stars at various positions in a jpanel. The translation of the stars y position depends on how far that star is away from the the x axis of decline which is initialized to 300(the center of the jpanel). The closer a star is to the point of decline, the less their y position is going to change. When a star reaches or passes the right side of the panel(or goes out of view), reset to the left side at its original y position(ugly, i know). I chose to do it this way since the stars are placed at random. I cant have all the stars start with the same dy, if it were so, all the stars would move along their own arc instead.
However, when I run this, after the third pass, the x positions of all the stars become smaller(into the negative ranges and out of view). Any suggestions for a better way to accomplish the original task are welcome. Thanks.
private Action orbit = new AbstractAction() {
int declineAxis = 300; //if a stars top left x is greater than this, move downwards
double distFromDecline;
AffineTransform at = new AffineTransform();
#Override
public void actionPerformed(ActionEvent ae) {
for (int i = 0; i < 12; i++) {
distFromDecline = Math.abs(declineAxis - stars.getStar(i).getBounds().getCenterX());
if (distFromDecline <= 50) {
if (stars.getStar(i).getBounds().getX() < declineAxis) {
at.translate(5, -2);
} else {
at.translate(5, 2);
}
} else if (distFromDecline <= 100 && distFromDecline > 50) {
if (stars.getStar(i).getBounds().getX() < declineAxis) {
at.translate(5, -3);
} else {
at.translate(5, 3);
}
} else if (distFromDecline <= 200 && distFromDecline > 100) {
if (stars.getStar(i).getBounds().getX() < declineAxis) {
at.translate(5, -4);
} else {
at.translate(5, 4);
}
} else if (distFromDecline >200) {
if (stars.getStar(i).getBounds().getX() < declineAxis) {
at.translate(5, -5);
} else {
at.translate(5, 5);
}
}
stars.move(at, i);
}
}
};
public class Stars {
private int[] yOrigins;
private Path2D[] stars;
private Random rand = new Random();
public Stars(int n) {
stars = new Path2D[n];
yOrigins = new int[n];
int dx = 700 / n;
int x = 0;
for (int i = 0; i < n; i++) {
int y = rand.nextInt(401);
generateStar(i, x, y);
yOrigins[i] = y;
x += dx;
}
}
private void generateStar(int i, int x, int y) {
stars[i] = new Path2D.Double();
Path2D.Double cur = (Path2D.Double) stars[i];
cur.moveTo(x, y);
cur.lineTo(cur.getCurrentPoint().getX() + 6, y - 2);
cur.lineTo(cur.getCurrentPoint().getX() + 2, cur.getCurrentPoint().getY() - 6);
cur.lineTo(cur.getCurrentPoint().getX() + 2, cur.getCurrentPoint().getY() + 6);
cur.lineTo(cur.getCurrentPoint().getX() + 6, cur.getCurrentPoint().getY() + 2);
cur.lineTo(cur.getCurrentPoint().getX() - 6, cur.getCurrentPoint().getY() + 2);
cur.lineTo(cur.getCurrentPoint().getX() - 2, cur.getCurrentPoint().getY() + 6);
cur.lineTo(cur.getCurrentPoint().getX() - 2, cur.getCurrentPoint().getY() - 6);
cur.closePath();
}
public void paintStars(Graphics2D g) {
//super.paintComponent(g);
g.setColor(new Color(246, 246, 255));
for (int i = 0; i < stars.length; i++) {
g.fill(stars[i]);
}
}
public Shape getStar(int i) {
return stars[i];
}
void move(AffineTransform at, int i) {
stars[i] = (Path2D) stars[i].createTransformedShape(at);
System.out.println(i+": " + stars[i].getBounds());
if(stars[i].getBounds().getX()>700){
at.translate(-(stars[i].getBounds().x+stars[i].getBounds().getWidth()), yOrigins[i]);
stars[i] = (Path2D) at.createTransformedShape(stars[i]);
}
}
}
java.awt.geom.FlatteningPathIterator
http://docs.oracle.com/javase/6/docs/api/java/awt/geom/FlatteningPathIterator.html
You pass your arc (or any another Shape) and use the points to position star.
You can use stars frm here
http://java-sl.com/shapes.html
WarpImage from the Sun/Oracle Java2D demo, java2d/demos/Images/WarpImage.java, is an appealing example of an animation that follows a CubicCurve2D using PathIterator. You might see if it offers any guidance.

Categories

Resources