Edge Detection code for BufferedImage - java

I am trying to create an Object that given an Image and Point it will trace the inside edge of that Image.
For simplicity the edges will always be of RGB Color Black.
i Define enums of RIGHT DOWN LEFT UP (Clockwise)
I start at Point p.
I move through the Pixels in the Image based on the current Direction starting with RIGHT.
if this is not a border Pixel i move my Direction back one step anti Clockwise. e.g (Left->DOWN)
if i cannot move my chosen direction i move to next direction.
i add the Point to my border array.
i do this till we return to the first border Pixel.
Thats the Plan...
So far i hit a snag when i have to go from UP to RIGHT but back to RIGHT again immediately after in order to keep the direction focused on the edge rather than turn back into the Image.
Ive tried using a boolean flag if UP is used, Directing the Next direction for right to be UP and Not DOWN.
Any guidance would be much appreciated.
I have the full Code below.
CODE:
package edgedection;
import static edgedection.EdgeDection.testImage;
import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
*
* #author Fionán
*/
public class EdgeDection {
/**
* #param args the command line arguments
*/
static BufferedImage testImage = null;
{
try {
testImage = ImageIO.read(this.getClass().getResourceAsStream("testImage2.png"));
} catch (IOException ex) {
Logger.getLogger(EdgeDection.class.getName()).log(Level.SEVERE, null, ex);
}
}
static enum DIRECTION {
RIGHT, DOWN, LEFT, UP, NOMOVE
}
BufferedImage bi;
int borderColor = Color.black.getRGB();
DIRECTION facing;
Point p;
ArrayList<Point> borders;
boolean upFlag = false;
int x = p.x;
int y = p.y;
public static void main(String[] args) {
int x = 150;
int y = 60;
//forcing instance for loading Images only.
EdgeDection test= new EdgeDection();
JFrame show = new JFrame();
show.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel picLabel = new JLabel(new ImageIcon(testImage));
show.add(picLabel);
show.pack();
show.setVisible(true);
EdgeDection dector = new EdgeDection(testImage, new Point(x, y));
dector.start();
dector.highLightEdge();
show.repaint();
}
boolean canMove(DIRECTION d, Point p) {
switch (d) {
case RIGHT:
return bi.getRGB(p.x + 1, p.y) != borderColor;
case DOWN:
return bi.getRGB(p.x, p.y + 1) != borderColor;
case LEFT:
return bi.getRGB(p.x - 1, p.y) != borderColor;
//Deafult is up
case UP:
return bi.getRGB(p.x, p.y - 1) != borderColor;
default:
return false;
}
}
public EdgeDection(BufferedImage bi, Point p) {
this.facing = DIRECTION.RIGHT;
this.bi = bi;
this.p = p;
this.borders = new ArrayList<>();
}
public EdgeDection() {
}
DIRECTION getDirection() {
return null;
}
void addBorder(Point p) {
if(borders.isEmpty()){
x = p.x;
y = p.y;
}
borders.add(p);
}
void start() {
do {
System.out.println("Checking " + p.x + " " + p.y + facing);
if (canMove(facing, p)) {
if (upFlag) {
facing = DIRECTION.UP;
// p =new Point(p.x+1,p.y);
}
p = NextPointByDirection();
if(!upFlag) stepBackDirection();
if(upFlag)upFlag=false;
} else {
addBorder(p);
setNextDirection();
System.out.println("going " + facing + " border array size = "+ borders.size());
System.out.println("Up Flag status "+upFlag);
}
} while (facing != DIRECTION.NOMOVE && (p.x != x || p.y != y));
}
private void stepBackDirection() {
switch (facing) {
case RIGHT:
if(upFlag) {facing = DIRECTION.UP;}else{
facing = DIRECTION.RIGHT;
}
break;
case DOWN:
facing = DIRECTION.RIGHT;
break;
case LEFT:
facing = DIRECTION.DOWN;
break;
case UP:
facing = DIRECTION.LEFT;
}
}
private void setNextDirection() {
switch (facing) {
case RIGHT:
facing = DIRECTION.DOWN;
if (upFlag) {
facing = DIRECTION.UP;
upFlag = false;
}
return;
case DOWN:
facing = DIRECTION.LEFT;
return;
case LEFT:
facing = DIRECTION.UP;
return;
case UP:
upFlag = true;
facing = DIRECTION.RIGHT;
// upFlag = true;
// if (canMove(facing, new Point(p.x + 1, p.y - 1))){
// p = new Point(p.x + 1, p.y - 1);
//
// } ;
//
// if (upFlag) {
// facing = DIRECTION.RIGHT;
// }
}
}
private Point NextPointByDirection() {
// if (upFlag) {
// facing = DIRECTION.UP;
// upFlag = !upFlag;
// }
switch (facing) {
case RIGHT:
return new Point(p.x + 1, p.y);
case DOWN:
return new Point(p.x, p.y + 1);
case LEFT:
return new Point(p.x - 1, p.y);
default:
return new Point(p.x, p.y - 1);
}
}
private void print() {
for (Point p : borders) {
System.out.print(p.x + " " + p.y + " ");
}
}
void highLightEdge() {
for (Point p : borders) {
bi.setRGB(p.x, p.y, Color.RED.getRGB());
}
}
}

For anyone interested , i solved this problem with the use of a Stack.
pre-populate the stack with the order of directions to take.
Start moving in a direction.
if it can move direction is popped from stack
else
Hit a border
push that direction to the stack
add The border to the Set of borders
if border is already in set break loop else
turn 90 degree relative to the direction

Related

Robot.getPixelColor vs BufferedImage.getRGB

I am asking this today for the purpose of comparing the two.
What I am trying to do is make a bot that is able to click the space-bar at a certain time.
Anyway, let me show what I am trying to compare.
Robot robot = new Robot();
This will be out Robot. (java.awt.Robot;)
My question is, which is faster for the purpose of try to constantly read one (or more) pixels on the screen at once?
My current (in progress) programs that are the same with the exception of one using
Robot.getPixelColor(int, int)
and the other uses
BufferedImage image Robot.createScreenCapture(Rectangle)
imaage.getRGB
These are being run at many times a second, and I am simply trying to figure out which is more consistently faster. They both seem random at the speed at which they operate, so any advice is appreciated.
In case you wish for an example to put these into, here is the setup for it.
Here is the example using Robot.getPixelColor(int, int)
Robot robot = new Robot();
Rectangle rect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
Point prompt = new Point(913, 506, robot);
BufferedImage screenImage = robot.createScreenCapture(rect);
for(int i = 0; i < 360; i++) {
zones.add(new Point(radius * Math.cos(Math.toRadians(i)) + centerX, radius * Math.sin(Math.toRadians(i)) + centerY, robot));
}
System.out.println("Resolution: " + width + " * " + height/* + " " + robot.getPixelColor(20, 20)/* + " " + screenImage.getRGB((int) prompt.getX(), (int) prompt.getY())*/);
while(true) {
tempZone = null;
while(prompt.hasColor(promptColor)) {
System.out.println("Found prompt, trying to find zone.");
int count = 0;
for(int i = 0; i < zones.size(); i++) {
if(zones.get(i).hasColor(zoneColor)){
tempZone = zones.get(i);
break;
}
}
if(tempZone != null) {
System.out.println("Found zone at: " + tempZone.getX() + ", " + tempZone.getY() + ". Looking for ticker...");
tempZone.whenHasColor(zoneColor);
}else{
System.out.println("Unable to find zone.");
}
while(prompt.hasColor(promptColor)) {}
}
}
With its Point Class:
public class Point {
private double x;
private double y;
private final Robot robot;
public Point(double x, double y, Robot rob) {
this.x = x;
this.y = y;
this.robot = rob;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean hasColor(Color color) {
return (robot.getPixelColor((int )x, (int) y).equals(color));
}
public void whenHasColor(Color color) throws IOException {
Rectangle rect = new Rectangle(889 + (int) x, 449 + (int) y, 1, 1);
while(hasColor(color)) {
//Nothing
}
BufferedImage cap = robot.createScreenCapture(rect);
if(Math.abs(Math.abs(cap.getRGB(0, 0)) - 15657182) < 350000) {
System.out.println("Missed Zone, failure.");
}else {
robot.keyPress(Main.promptKey);
System.out.println("Found ticker pressing key. ");
}
}
}
For anyone wondering where could something this specific come from, I am using a modified version of this website as my medium for making this bot.
Now for the example with using Robot.createScreenCapture(Rectangle) and BufferedImage.getRGB(int, int)
Robot robot = new Robot();
Rectangle rect = new Rectangle(889, 449, 149, 149);
Point prompt = new Point(24, 57, robot);
String format = "png";
String fileName = "fullDebug1." + format;
BufferedImage screenImage = robot.createScreenCapture(rect);
for(int i = 0; i < 360; i++) {
zones.add(new Point(radius * Math.cos(Math.toRadians(i)) + centerX, radius * Math.sin(Math.toRadians(i)) + centerY, robot));
}
System.out.println("Resolution: " + width + " * " + height/* + " " + screenImage.getRGB((int) prompt.getX(), (int) prompt.getY())*/);
while(true) {
tempZone = null;
int zoneFinal = 0;
while(prompt.hasColor(promptColor)) {
System.out.println("Found prompt, trying to find zone.");
BufferedImage zoneImage = robot.createScreenCapture(rect);
int count = 0;
for(int i = 0; i < zones.size(); i++) {
if(zones.get(i).hasColorZones(zoneColor, zoneImage)){
tempZone = zones.get(i);
break;
}
}
if(tempZone != null) {
System.out.println("Found zone at: " + tempZone.getX() + ", " + tempZone.getY() + ". Looking for ticker...");
tempZone.whenHasColor(zoneColor);
}else{
System.out.println("Unable to find zone.");
}
while(prompt.hasColor(promptColor)) {}
}
and its Point class:
public class Point {
private double x;
private double y;
private final Robot robot;
public Point(double x, double y, Robot rob) {
this.x = x;
this.y = y;
this.robot = rob;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean hasColor(int color) {
Rectangle rect = new Rectangle(889, 449, 149, 149);
BufferedImage screenImage = robot.createScreenCapture(rect);
return (Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(color))
}
public boolean hasColorPixel(int color, Rectangle rect) throws IOException {
BufferedImage cap = robot.createScreenCapture(rect);
return Math.abs(cap.getRGB(0, 0)) == Math.abs(color);
}
public boolean hasColorZones(int color, BufferedImage screenImage) {
return Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(color);
}
public boolean hasColorList(int[] colors, BufferedImage screenImage) {
for(int i : colors) {
if(Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(i)) {
return true;
}
}
return false;
}
public void whenHasColor(int color) throws IOException {
Rectangle rect = new Rectangle(889 + (int) x, 449 + (int) y, 1, 1);
while(hasColorPixel(color, rect)) {
//Nothing
}
BufferedImage cap = robot.createScreenCapture(rect);
if(Math.abs(Math.abs(cap.getRGB(0, 0)) - 15657182) < 350000) {
System.out.println("Missed Zone, failure.");
}else {
robot.keyPress(Main.promptKey);
System.out.println("Found ticker pressing key. ");
}
}
}
So my question is, which will run faster? Sometimes the latter option (which I more thoroughly debugged) gets 10 in a row, but then will fail randomly too. At first I thought it was that my laptop could not handle it, but upon running it on my desktop I had the same results. I would rather not have the bot be inconsistent with working properly or not.
I do hope I was able to give enough information for anyone to be able to help me. If there is anything I need to add or change, please tell me! This is my first question and I would like to learn to be better with this site.
Another apologies to if 1) the pixels are not the same on your screen, therefore you would not be able to run this to simulate my situation, and 2) if this post was long. I wanted to make a good "Minimal, Complete, and Verifiable example," but I wasn't sure how to shorten it any more.
EDIT
To make the code put in more relevant, as Ben pointed out, I could have cut it. But I thought of something that makes it more useful.
What can be done to that code to optimize it to run faster, so the bot will be 100% consistently working, or at least closer to 100% than it is now (~60%)

My Zombie won't find itself.

That sounds extremely philosophical doesn't it?
Anyways, I have a rather complex problem.
My main_activity class gathers all of the Zombies like so:
//Testing Runnable (used to compare the first zombie with the player)
private Runnable updateLocations = new Runnable(){
#Override
public void run(){
try {
while(true) {
image_player.getLocationInWindow(pLoc);
Zombie zoms = zombieCollection.next();
if(!zoms.equals(null)){
zoms.getZombieImage().getLocationInWindow(zLoc);
}
System.out.println("Zombie: x = " + zLoc[0] + "; y = " + zLoc[1]);
System.out.println("Player: x = " + pLoc[0] + "; y = " + pLoc[1]);
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
My zombie class gathers information like so:
public class Zombie{
float X, Y;
int Width, Height;
Direction fdirc;
ImageView zImage;
Player player;
boolean dead;
int[] zLoc;
public Zombie(ImageView zImage, Player player){
zLoc = new int[2];
zImage.getLocationOnScreen(zLoc);
this.zImage = zImage;
this.X = zLoc[0];
this.Y = zLoc[1];
this.Width = zImage.getWidth();
this.Height = zImage.getHeight();
this.fdirc = Direction.EAST;
this.player = player;
this.dead = false;
Thread thread = new Thread(this.startZombieChase);
thread.start();
}
public ImageView getZombieImage(){
return zImage;
}
private Runnable startZombieChase = new Runnable() {
#Override
public void run() {
try {
while(!dead) {
moveTowardsPlayer();
Thread.sleep(10);
updateZombie.sendEmptyMessage(0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
private Handler updateZombie = new Handler(Looper.getMainLooper()) {
public void handleMessage(android.os.Message msg) {
/** Because the zombie should always be on top! **/
zImage.getLocationOnScreen(zLoc);
zImage.bringToFront();
zImage.setX(X);
zImage.setY(Y);
}
};
private void moveTowardsPlayer(){
int player_x = player.getPosition()[0];
int player_y = player.getPosition()[1];
l("Where is it in zombie class : player - " + player_x + " " + player_y + "zombie - " + X + " " + Y);
float compareX = player_x - (int)X;
float compareY = player_y - (int)Y;
// Y is closer, so we're moving horizontally.
if(Math.abs(compareX) < Math.abs(compareY)){
//Moving North
if(player_y > Y){
Y+=1;
}
//Moving South
else if(player_y < Y){
Y-=1;
}
}
// X is closer, so we're moving vertically.
else{
//Moving East
if(player_x > X){
X+=1;
}
//Moving West
else if(player_x < X){
X-=1;
}
}
}
public void l(Object string){
System.out.println("Log - " + string);
}
}
The problem I'm having is that it will move relative to a number (So, it does move relative to something) but, not the correct thing.
logcat tells me this:
Where is it in zombie class: player - 750 451 zombie - 750 451
Where it is in main_activity: player - 750 451 zombie - 792 619
Could anyone help me understand what I'm doing wrong?
The entire project is located here.
Zombies that moves away from the Brainz, must be an ill Zombie. We can't have that, now can we?
To have a function that moves the Zombie towards the non-zombie, you use a function, but that function makes use of variables that are not arguments, and there fore hard to find out where they come from. I'd go with something like this: (this is a bit verbose, but clearly shows what is happening)
/*
* Function to update the position of the Zombie, aka walk to the Player.
* #player_pos - Where's the Brainz at?
* #zombie_pos - Where am I?
* Might want to build it overloaded with an option for the speed.
*
* #return - We return the new Zombie pos.
*/
private double [] moveTowardsPlayer(double [] player_pos, double [] zombie_pos) {
// To make sure we don't override the old position, we copy values. (Java stuff)
double [] player_pos_old = player_pos.clone();
double [] zombie_pos_old = zombie_pos.clone();
// Let's get the new X pos for the Zombie
double left_or_right = player_pos_old[0] - zombie_pos_old[0]; // pos number is right, neg is left
double zombie_pos_new_x;
if (left_or_right > 0) { // Right
zombie_pos_new_x = player_pos_old[0] + zombie_speed;
} else { // Left - this way we make sure we are always getting nearer to the Brainz.
zombie_pos_new_x = player_pos_old[0] - zombie_speed;
}
// TODO: do the same for the Y pos.
// Bring it together
double [] zombie_pos_new = {zombie_pos_new_x, zombie_pos_new_y};
// One step closer to the Brainz!
return zombie_pos_new;
}
And use like:
double [] zombie_pos = moveTowardsPlayer([2, 2], [5, 4]);
this.X = zombie_pos[0]; // I'd advice to keep these together as one var.
this.Y = zombie_pos[1]; // But it's your game.
And then figure out when the Zombie gets the Brainz (or the Bullet)
this here is not correct:
if(!zoms.equals(null)){
if you are trying to check that zoms is not pointing to a null reference then do
if(zoms != null ){

Libgdx Directional Collision Detection for Pacman Clone

I'm using Libgdx to make a Pacman clone and I am having an issue with collision.
I I'm bringing in square objects made in the tile editor to determine where the walls are. Here is my map code that handles that.
public void getMapColliders()
{
MapObjects collisionObjects = new MapObjects();
collisionObjects = map.getLayers().get("Level Boundries").getObjects();
int tileWidth = 16; // whatever your tile width is
int tileHeight = 16; // whatever your tile height is
for (int i = 0; i < collisionObjects.getCount(); i++)
{
RectangleMapObject obj = (RectangleMapObject) collisionObjects.get(i);
Rectangle rect = obj.getRectangle();
collisionRects.add(new Rectangle((int)rect.x+2,(int) rect.y+16, rect.width , rect.height));
}
}
I then have a rectangle around my Pacman sprite to act as a collision box and using the overlap function to detect collision. Initially I had it save the last position of Pacman before moving forward and if it detected a collision it reset the position. This didn't work and I got stuck in the walls, instead I ended up just reversing the movement the same amount it moved forward and that worked, but because how perfectly aligned the sprite and map are it would always put Pacman in a position where he couldn't go down because he is slightly off to one side or the other. Here is the code for that:
package Components;
import Math.Point2D;
import com.badlogic.gdx.math.Rectangle;
import java.util.ArrayList;
public class Controller
{
/* Variable to store amount entitie position should increase */
private Input input;
private String keyPressed;
private String lastKeyPressed;
private boolean isMoving;
private double speed;
private ArrayList<Rectangle> mapCollisionBoxes;
private Collider pCol;
private boolean rBlocked;
private boolean lBlocked;
private boolean uBlocked;
private boolean dBlocked;
private Point2D prevPos;
//private Point2D movementP;
public Controller(ArrayList<Rectangle> collisions, Collider pCol)
{
input = new Input();
keyPressed = "N";
lastKeyPressed = "N";
isMoving = false;
speed = 40.0;
mapCollisionBoxes = collisions;
this.pCol = pCol;
prevPos = new Point2D();
}
/**
* Check if a movement input is pressed and move accordingly
*
* #param curPos Player's current position
* #param t Delta time
*/
public void move(Point2D curPos, int t)
{
System.out.println("rBlocked: " + rBlocked);
System.out.println("lBlocked: " + lBlocked);
System.out.println("uBlocked: " + uBlocked);
System.out.println("dBlocked: " + dBlocked);
keyPressed = input.checkKeys();
switch (keyPressed)
{
case "RIGHT":
lastKeyPressed = "R";
break;
case "LEFT":
lastKeyPressed = "L";
break;
case "UP":
lastKeyPressed = "U";
break;
case "DOWN":
lastKeyPressed = "D";
break;
case "NONE":
break;
}
switch (lastKeyPressed)
{
case "R":
if (!rBlocked)
{
if (checkForCollision())
{
curPos.setX(curPos.getX() - speed * t / 1000);
rBlocked = true;
}
else
{
curPos.setX(curPos.getX() + speed * t / 1000);
lBlocked = false;
dBlocked = false;
uBlocked = false;
}
}
break;
case "L":
if (!lBlocked)
{
prevPos = curPos;
if (checkForCollision())
{
curPos.setX(Math.round(curPos.getX() + speed * t / 1000));
lBlocked = true;
}
else
{
curPos.setX(curPos.getX() - speed * t / 1000);
rBlocked = false;
uBlocked = false;
dBlocked = false;
}
}
break;
case "U":
if (!uBlocked)
{
if (checkForCollision())
{
curPos.setY(curPos.getY() - speed * t / 1000);
uBlocked = true;
}
else
{
curPos.setY(curPos.getY() + speed * t / 1000);
dBlocked = false;
lBlocked = false;
rBlocked = false;
}
}
break;
case "D":
if (!dBlocked)
{
if (checkForCollision())
{
curPos.setY(Math.round(curPos.getY() + speed * t / 1000));
dBlocked = true;
}
else
{
curPos.setY(curPos.getY() - speed * t / 1000);
uBlocked = false;
lBlocked = false;
rBlocked = false;
}
}
break;
}
}
public String lastKeyPressed()
{
return lastKeyPressed;
}
private boolean checkForCollision()
{
boolean collided = false;
for (Rectangle rect : mapCollisionBoxes)
{
if (pCol.hBox.overlaps(rect))
{
collided = true;
}
}
return collided;
}
}
This is all on top of the fact that I have to use booleans to check which direction he was going when he collided otherwise he would never move again after hitting something because collision isn't directional. This creates and old glitch I had in another game where if I'm stopped on the left side and hit right then left again quickly I will pass through the wall slightly.
Any help with this would be greatly appreciated.

Numbering A Crossword Java ACM Graphics

The problem asks for an acm graphics program that reads a txt file like this:
R
FUN
SALES
RECEIPT
MERE#FARM
DOVE###RAIL
MORE#####DRAW
HARD###TIED
LION#SAND
EVENING
EVADE
ARE
D
and makes a crossword puzzle, with blank squares on letters, black squares on '#', and nothing on empty spaces. The problem also asks that "if the square is at the beginning of a word running across, down, or both, the square should contain a number that is assigned sequentially through the puzzle."
I have the square drawing working, but I'm stuck on drawing the numbers correctly. There is something wrong with how I'm detecting null space and black squares. Can someone tell me what I'm doing wrong, please?
Here is the code:
import acm.program.*;
import java.io.*;
import java.util.*;
import acm.graphics.*;
import java.awt.*;
public class Crossword extends GraphicsProgram {
public void run() {
String fileName = "crosswordfile.txt";
makeCrosswordPuzzle(fileName);
}
private static final int sqCon = 15; // constant for square x and y dimensions
private int y = 0;
public void makeCrosswordPuzzle(String fileName) {
BufferedReader rd;
int y = 0; // y value for the square being added during that loop. increments by sqCon after every line
int wordNumber = 1; // variable for numbers added to certain boxes. increments every time the program adds a number
try {
rd = new BufferedReader(new FileReader(fileName));
String line = rd.readLine(); //reads one line of the text document at a time and makes it a string
while (line != null) {
int x = 0;
for (int i = 0; i < line.length(); i++) {
char lineChar = line.charAt(i);// the character being examined for each loop
GRect whiteSq = new GRect(sqCon,sqCon); //GRect for blank squares
GRect blackSq = new GRect(sqCon,sqCon);//GRect for black squares
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
if (lineChar == '#'){
add (blackSq,x,y);
}
if (Character.isLetter(lineChar)) {
add (whiteSq, x, y);
// if the element above or to the left of the current focus is null or blackSq, place the number and then increment wordNumber
GObject above = getElementAt(x+sqCon/2,y-sqCon/2);
GObject left = getElementAt(x-sqCon/2, y+sqCon/2);
GLabel wordNumberLabel = new GLabel(Integer.toString(wordNumber));
if (above == null || left == null || above == blackSq || left == blackSq) {
add(wordNumberLabel,x,y+sqCon);
wordNumber++;
}
}
x += sqCon;
}
line = rd.readLine();
y += sqCon;
}
rd.close();
}
catch (IOException e) {
throw new ErrorException(e);
}
}
}
Edited to add:
I copied your code over to my Eclipse and ran it. Here's the result.
You did fine on the upper half, but you missed the down numbers on the lower half.
Here's the same code, reformatted so it's easier to read.
import java.awt.Color;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import acm.graphics.GLabel;
import acm.graphics.GObject;
import acm.graphics.GRect;
import acm.program.GraphicsProgram;
import acm.util.ErrorException;
public class Crossword extends GraphicsProgram {
private static final long serialVersionUID = -7971434624427958742L;
public void run() {
// String fileName = "crosswordfile.txt";
String fileName = "C:/Eclipse/eclipse-4.2-work/com.ggl.testing/crosswordfile.txt";
makeCrosswordPuzzle(fileName);
}
private static final int sqCon = 15; // constant for square x and y
// dimensions
private int y = 0;
public void makeCrosswordPuzzle(String fileName) {
BufferedReader rd;
int y = 0; // y value for the square being added during that loop.
// increments by sqCon after every line
int wordNumber = 1; // variable for numbers added to certain boxes.
// increments every time the program adds a number
try {
rd = new BufferedReader(new FileReader(fileName));
String line = rd.readLine(); // reads one line of the text document
// at a time and makes it a string
while (line != null) {
int x = 0;
for (int i = 0; i < line.length(); i++) {
char lineChar = line.charAt(i);// the character being
// examined for each loop
GRect whiteSq = new GRect(sqCon, sqCon); // GRect for blank
// squares
GRect blackSq = new GRect(sqCon, sqCon);// GRect for black
// squares
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
if (lineChar == '#') {
add(blackSq, x, y);
}
if (Character.isLetter(lineChar)) {
add(whiteSq, x, y);
// if the element above or to the left of the current
// focus is null or blackSq, place the number and then
// increment wordNumber
GObject above = getElementAt(x + sqCon / 2, y - sqCon
/ 2);
GObject left = getElementAt(x - sqCon / 2, y + sqCon
/ 2);
GLabel wordNumberLabel = new GLabel(
Integer.toString(wordNumber));
if (above == null || left == null || above == blackSq
|| left == blackSq) {
add(wordNumberLabel, x, y + sqCon);
wordNumber++;
}
}
x += sqCon;
}
line = rd.readLine();
y += sqCon;
}
rd.close();
} catch (IOException e) {
throw new ErrorException(e);
}
}
}
I followed the advice of my own comment. I created the crossword puzzle answer, numbered the crossword puzzle answer, and finally drew the crossword puzzle answer.
Here's the applet result:
I kept a List of crossword puzzle cells. That way, I could determine the length and the width of the puzzle by the number of characters on a row and the number of rows of the input text file. I didn't have to hard code the dimensions.
For each crossword cell, I kept track of whether or not it was a letter, and whether or not it was a dark space.
When determining where to put the numbers, I followed 2 rules.
An across number is placed where the cell left of the cell is empty or dark, and there are three or more letters across.
A down number is placed where the cell above the cell is empty or dark, there are three or more letters down, and there is no across number.
You can see in the code that I had to do some debug printing to get the crossword puzzle clue numbering correct. I broke the process into many methods to keep each method as simple as possible.
Finally, I drew the crossword puzzle answer from the information in the List.
Here's the code:
import java.awt.Color;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import acm.graphics.GLabel;
import acm.graphics.GRect;
import acm.program.GraphicsProgram;
import acm.util.ErrorException;
public class Crossword extends GraphicsProgram {
private static final boolean DEBUG = false;
private static final long serialVersionUID = -7971434624427958742L;
private List<CrosswordCell> crosswordCellList;
#Override
public void run() {
this.crosswordCellList = new ArrayList<CrosswordCell>();
// String fileName = "crosswordfile.txt";
String fileName = "C:/Eclipse/eclipse-4.2-work/" +
"com.ggl.testing/crosswordfile.txt";
try {
readCrosswordAnswer(fileName);
if (DEBUG) printCrosswordAnswer();
numberCrosswordCells();
if (DEBUG) printCrosswordAnswer();
drawCrosswordAnswer();
} catch (FileNotFoundException e) {
throw new ErrorException(e);
} catch (IOException e) {
throw new ErrorException(e);
}
}
private void readCrosswordAnswer(String fileName)
throws FileNotFoundException, IOException {
BufferedReader reader =
new BufferedReader(new FileReader(fileName));
String line = "";
int row = 0;
while ((line = reader.readLine()) != null) {
for (int column = 0; column < line.length(); column++) {
CrosswordCell cell = new CrosswordCell(column, row);
char lineChar = line.charAt(column);
if (lineChar == '#') {
cell.setDarkCell(true);
} else if (Character.isLetter(lineChar)) {
cell.setLetter(true);
}
crosswordCellList.add(cell);
}
row++;
}
reader.close();
}
public void printCrosswordAnswer() {
for (CrosswordCell cell : crosswordCellList) {
System.out.println(cell);
}
}
private void numberCrosswordCells() {
int clueNumber = 1;
for (CrosswordCell cell : crosswordCellList) {
if (cell.isLetter()) {
clueNumber = testCell(cell, clueNumber);
}
}
}
private int testCell(CrosswordCell cell, int clueNumber) {
Point p = cell.getLocation();
CrosswordCell leftCell = getLeftCell(p.x, p.y);
List<CrosswordCell> acrossList = getRightCells(p.x, p.y);
if (DEBUG) {
System.out.print(p);
System.out.println(", " + leftCell + " " +
acrossList.size());
}
if ((leftCell == null) && (acrossList.size() >= 3)) {
cell.setClueNumber(clueNumber++);
} else {
CrosswordCell aboveCell = getAboveCell(p.x, p.y);
List<CrosswordCell> downList = getBelowCells(p.x, p.y);
if (DEBUG) {
System.out.print(p);
System.out.println(", " + aboveCell + " " +
downList.size());
}
if ((aboveCell == null) && (downList.size() >= 3)) {
cell.setClueNumber(clueNumber++);
}
}
return clueNumber;
}
private CrosswordCell getAboveCell(int x, int y) {
int yy = y - 1;
return getCell(x, yy);
}
private CrosswordCell getLeftCell(int x, int y) {
int xx = x - 1;
return getCell(xx, y);
}
private List<CrosswordCell> getBelowCells(int x, int y) {
List<CrosswordCell> list = new ArrayList<CrosswordCell>();
for (int i = y; i < (y + 3); i++) {
CrosswordCell cell = getCell(x, i);
if (cell != null) {
list.add(cell);
}
}
return list;
}
private List<CrosswordCell> getRightCells(int x, int y) {
List<CrosswordCell> list = new ArrayList<CrosswordCell>();
for (int i = x; i < (x + 3); i++) {
CrosswordCell cell = getCell(i, y);
if (cell != null) {
list.add(cell);
}
}
return list;
}
private CrosswordCell getCell(int x, int y) {
for (CrosswordCell cell : crosswordCellList) {
Point p = cell.getLocation();
if ((p.x == x) && (p.y == y)) {
if (cell.isDarkCell()) {
return null;
} else if (cell.isLetter()){
return cell;
} else {
return null;
}
}
}
return null;
}
private void drawCrosswordAnswer() {
int sqCon = 32;
for (CrosswordCell cell : crosswordCellList) {
Point p = cell.getLocation();
if (cell.isDarkCell()) {
drawDarkCell(p, sqCon);
} else if (cell.isLetter()) {
drawLetterCell(cell, p, sqCon);
}
}
}
private void drawDarkCell(Point p, int sqCon) {
GRect blackSq = new GRect(sqCon, sqCon);
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
add(blackSq, p.x * sqCon, p.y * sqCon);
}
private void drawLetterCell(CrosswordCell cell, Point p, int sqCon) {
GRect whiteSq = new GRect(sqCon, sqCon);
add(whiteSq, p.x * sqCon, p.y * sqCon);
if (cell.getClueNumber() > 0) {
String label = Integer.toString(cell.getClueNumber());
GLabel wordNumberLabel = new GLabel(label);
add(wordNumberLabel, p.x * sqCon + 2, p.y * sqCon + 14);
}
}
class CrosswordCell {
private boolean darkCell;
private boolean isLetter;
private int clueNumber;
private Point location;
public CrosswordCell(int x, int y) {
this.location = new Point(x, y);
this.clueNumber = 0;
this.darkCell = false;
this.isLetter = false;
}
public boolean isDarkCell() {
return darkCell;
}
public void setDarkCell(boolean darkCell) {
this.darkCell = darkCell;
}
public boolean isLetter() {
return isLetter;
}
public void setLetter(boolean isLetter) {
this.isLetter = isLetter;
}
public int getClueNumber() {
return clueNumber;
}
public void setClueNumber(int clueNumber) {
this.clueNumber = clueNumber;
}
public Point getLocation() {
return location;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CrosswordCell [location=");
builder.append(location);
builder.append(", clueNumber=");
builder.append(clueNumber);
builder.append(", darkCell=");
builder.append(darkCell);
builder.append(", isLetter=");
builder.append(isLetter);
builder.append("]");
return builder.toString();
}
}
}

How to fix the movement of 3D object?

I got a 3D spaceship that moves forward and backward in the 3D scene correctly but the rightward and leftward movements are not right and what the A and D buttons do seem to vary with the camera position. This is the listener code which works for buttons W (forward) and S (backward) while buttons A and D don't do exactly what they should.
When I start the 3D space scene the steering of the spaceship is working and the buttons A and D move the spaceship left and right but after a changed the camera and rotated the view, the buttons A and D are still opposite directions but not to the left and right but depend on the camera's position.
public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int movement = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals("moveForward")) {
direction.multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveBackward")) {
direction.multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveRight")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveLeft")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("rotateRight") && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals("rotateLeft") && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);
}
if (name.equals("rotateUp") && rotate) {
ufoNode.rotate(0, 0, -1 * tpf);
}
if (name.equals("rotateDown") && rotate) {
ufoNode.rotate(0, 0, 1 * tpf);
}
}
Can you help me and tell me what should be done to fix the right and left movements? The entire code is
public class SpaceStation extends SimpleApplication implements AnalogListener,
ActionListener {
private PlanetAppState planetAppState;
private Geometry mark;
private Node ufoNode;
private Node spaceStationNode;
private Node jumpgateNode;
private Node jumpgateNode2;
private BetterCharacterControl ufoControl;
CameraNode camNode;
boolean rotate = false;
Vector3f direction = new Vector3f();
private BulletAppState bulletAppState;
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setResolution(1024, 768);
SpaceStation app = new SpaceStation();
app.setSettings(settings);
// app.showSettings = true;
app.start();
}
#Override
public void simpleInitApp() {
// Only show severe errors in log
java.util.logging.Logger.getLogger("com.jme3").setLevel(
java.util.logging.Level.SEVERE);
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
bulletAppState.setDebugEnabled(false);
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(-.1f, 0f, -1f));
sun.setColor(new ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f));
rootNode.addLight(sun);
// Add sky
Node sceneNode = new Node("Scene");
sceneNode.attachChild(Utility.createSkyBox(this.getAssetManager(),
"Textures/blue-glow-1024.dds"));
rootNode.attachChild(sceneNode);
// Create collision test mark
Sphere sphere = new Sphere(30, 30, 5f);
mark = new Geometry("mark", sphere);
Material mark_mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mark_mat.setColor("Color", ColorRGBA.Red);
mark.setMaterial(mark_mat);
// Add planet app state
planetAppState = new PlanetAppState(rootNode, sun);
stateManager.attach(planetAppState);
// Add planet
FractalDataSource planetDataSource = new FractalDataSource(4);
planetDataSource.setHeightScale(900f);
Planet planet = Utility.createEarthLikePlanet(getAssetManager(),
293710.0f, null, planetDataSource);
planetAppState.addPlanet(planet);
rootNode.attachChild(planet);
// Add moon
FractalDataSource moonDataSource = new FractalDataSource(5);
moonDataSource.setHeightScale(300f);
Planet moon = Utility.createMoonLikePlanet(getAssetManager(), 50000,
moonDataSource);
planetAppState.addPlanet(moon);
rootNode.attachChild(moon);
moon.setLocalTranslation(new Vector3f(10f, 10f, 505000f));//-950000f, 0f, 0f);
// add saucer
ufoNode = (Node) assetManager.loadModel("usaucer_v01.j3o");
ufoNode.setLocalScale(100f);
ufoNode.setLocalTranslation((new Vector3f(1000f, -1000f, 328000f)));
jumpgateNode = (Node) assetManager.loadModel("JumpGate.j3o");
jumpgateNode.setLocalScale(10000f);
jumpgateNode.setLocalTranslation((new Vector3f(10f, 10f, 708000f)));
spaceStationNode = (Node) assetManager.loadModel("SpaceStation.j3o");
spaceStationNode.setLocalScale(4000f);
spaceStationNode.setLocalTranslation((new Vector3f(10000f, -10f, 425000f)));
jumpgateNode2 = (Node) assetManager.loadModel("JumpGate.j3o");
jumpgateNode2.setLocalScale(10000f);
jumpgateNode2.setLocalTranslation((new Vector3f(10f, 10f, 798300f)));
/* This quaternion stores a 180 degree rolling rotation */
// Quaternion roll180 = new Quaternion();
// roll180.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1));
/* The rotation is applied: The object rolls by 180 degrees. */
// ufoNode.setLocalRotation(roll180);
rootNode.attachChild(jumpgateNode);
rootNode.attachChild(jumpgateNode2);
rootNode.attachChild(spaceStationNode);
// creating the camera Node
camNode = new CameraNode("CamNode", cam);
// Setting the direction to Spatial to camera, this means the camera
// will copy the movements of the Node
camNode.setControlDir(ControlDirection.SpatialToCamera);
// attaching the camNode to the teaNode
ufoNode.attachChild(camNode);
// setting the local translation of the cam node to move it away a bit
camNode.setLocalTranslation(new Vector3f(-40, 0, 0));
// setting the camNode to look at the teaNode
camNode.lookAt(ufoNode.getLocalTranslation(), Vector3f.UNIT_Y);
// disable the default 1st-person flyCam (don't forget this!!)
ufoControl = new BetterCharacterControl(100000f, 80000f, 5000f);// (2, 4, 0.5f);
// radius (meters), height (meters), gravity (mass)
//ufoNode.addControl(ufoControl);
//rootNode.attachChild(ninjaNode);
//bulletAppState.getPhysicsSpace().add(ufoControl);
//getPhysicsSpace().add(ufoControl);
rootNode.attachChild(ufoNode);
flyCam.setEnabled(false);
registerInput();
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}
public void registerInput() {
inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP),
new KeyTrigger(keyInput.KEY_W));
inputManager.addMapping("moveBackward", new KeyTrigger(
keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
inputManager.addMapping("moveRight",
new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(
keyInput.KEY_D));
inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT),
new KeyTrigger(keyInput.KEY_A));
inputManager.addMapping("toggleRotate", new MouseButtonTrigger(
MouseInput.BUTTON_LEFT));
inputManager.addMapping("rotateRight", new MouseAxisTrigger(
MouseInput.AXIS_X, true));
inputManager.addMapping("rotateLeft", new MouseAxisTrigger(
MouseInput.AXIS_X, false));
inputManager.addMapping("rotateUp", new MouseAxisTrigger(
MouseInput.AXIS_Y, true));
inputManager.addMapping("rotateDown", new MouseAxisTrigger(
MouseInput.AXIS_Y, false));
inputManager.addListener(this, "moveForward", "moveBackward",
"moveRight", "moveLeft");
inputManager.addListener(this, "rotateRight", "rotateLeft", "rotateUp",
"rotateDown", "toggleRotate");
// Toggle mouse cursor
inputManager.addMapping("TOGGLE_CURSOR", new MouseButtonTrigger(
MouseInput.BUTTON_LEFT), new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, "TOGGLE_CURSOR");
// Toggle wireframe
inputManager.addMapping("TOGGLE_WIREFRAME", new KeyTrigger(
KeyInput.KEY_T));
inputManager.addListener(actionListener, "TOGGLE_WIREFRAME");
// Collision test
inputManager.addMapping("COLLISION_TEST", new MouseButtonTrigger(
MouseInput.BUTTON_RIGHT));
inputManager.addListener(actionListener, "COLLISION_TEST");
}
public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int movement = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals("moveForward")) {
direction.multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveBackward")) {
direction.multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveRight")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveLeft")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("rotateRight") && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals("rotateLeft") && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);
}
if (name.equals("rotateUp") && rotate) {
ufoNode.rotate(0, 0, -1 * tpf);
}
if (name.equals("rotateDown") && rotate) {
ufoNode.rotate(0, 0, 1 * tpf);
}
}
public void onAction(String name, boolean keyPressed, float tpf) {
// toggling rotation on or off
if (name.equals("toggleRotate") && keyPressed) {
rotate = true;
inputManager.setCursorVisible(false);
}
if (name.equals("toggleRotate") && !keyPressed) {
rotate = false;
inputManager.setCursorVisible(true);
}
if (name.equals("TOGGLE_CURSOR") && !keyPressed) {
if (inputManager.isCursorVisible()) {
inputManager.setCursorVisible(false);
} else {
inputManager.setCursorVisible(true);
}
}
if (name.equals("TOGGLE_WIREFRAME") && !keyPressed) {
for (Planet planet : planetAppState.getPlanets()) {
planet.toogleWireframe();
}
}
if (name.equals("COLLISION_TEST") && !keyPressed) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// Test collision with closest planet's terrain only
planetAppState.getNearestPlanet().getTerrainNode()
.collideWith(ray, results);
System.out.println("----- Collisions? " + results.size() + "-----");
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of
// geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry().getName();
System.out.println("* Collision #" + i);
System.out.println(" You shot " + hit + " at " + pt + ", "
+ dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
rootNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
rootNode.detachChild(mark);
}
}
}
private ActionListener actionListener = new ActionListener() {
public void onAction(String name, boolean pressed, float tpf) {
if (name.equals("TOGGLE_CURSOR") && !pressed) {
if (inputManager.isCursorVisible()) {
inputManager.setCursorVisible(false);
} else {
inputManager.setCursorVisible(true);
}
}
if (name.equals("TOGGLE_WIREFRAME") && !pressed) {
for (Planet planet : planetAppState.getPlanets()) {
planet.toogleWireframe();
}
}
if (name.equals("COLLISION_TEST") && !pressed) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// Test collision with closest planet's terrain only
planetAppState.getNearestPlanet().getTerrainNode()
.collideWith(ray, results);
System.out.println("----- Collisions? " + results.size()
+ "-----");
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of
// geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry()
.getName();
System.out.println("* Collision #" + i);
System.out.println(" You shot " + hit + " at " + pt + ", "
+ dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
rootNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
rootNode.detachChild(mark);
}
}
}
};
}
Going off your last comment, I am posting this as an answer (although I'm not exactly sure what to use as a cross vector.
When retrieving the cross vector, we are looking to get a perpendicular to the straight line out the front of the craft, and the vertical line that is perpendicular to the straight line, going vertically through the center of the craft.
I assume that direction is our forward-direction vector, in which case (regardless of view) we want to cross this with the vertical line that goes through the center of the craft. The crossLocal of these two vectors would be a perpendicular line to both, either going out of the left or right of the craft (regardless of camera or craft orientation).
for my code fix, I will assume craftSkewer is an imaginary skewer that runs through the center of the craft, vertically.
direction.crossLocal(craftSkewer.UNIT_Y).multLocal(movement * tpf);
I think the reason this works initially is due to the UNIT_Y returning 0 - But after moving craft or camera, it is recalculated incorrectly?

Categories