I am trying to make a puzzle game but the problem is that I can't do the EventHandler setOnMouseClicked.Also I want to know how to make possible that to re-arrange the puzzle so it shows random images in every-coordinate. This is how far I got.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class eliss extends Application{
#Override
public void start(Stage primaryStage) {
ImageView view[][] = new ImageView[5][5];
Image imag = new Image("http://images.cdn.autocar.co.uk/sites/autocar.co.uk/files/styles/gallery_slide/public/ferrari-laferrari-zfye-059_1.jpg?itok=hfLNxUD9",600,600,false,true);
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setVgap(2);
pane.setHgap(2);
PuzzleImage(view, imag, pane);
Scene scene = new Scene(pane,1100,1100);
primaryStage.setTitle("Elis");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] x)
{
launch(x);
}
public void PuzzleImage( ImageView view[][],Image imag,GridPane pane)
{
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
{
if(j==4 && i==4) view[i][j]=null;
else{
view[i][j]=new ImageView(imag);
Rectangle2D rect = new Rectangle2D(120*i,120*j,120,120);
view[i][j].setViewport(rect);
pane.add(view[i][j], i, j);
}
}
}
}
}
What you are asking is too much for a question on StackOverflow. However, I got a little bit of spare time, so I quickly drafted up some code to demonstrate what you need:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PuzzleGame extends Application {
private Image image = new Image("http://images.cdn.autocar.co.uk/sites/autocar.co.uk/files/styles/gallery_slide/public/ferrari-laferrari-zfye-059_1.jpg?itok=hfLNxUD9",600,600,false,true);
private static double SCENE_WIDTH = 1024;
private static double SCENE_HEIGHT = 768;
public static int TILE_ROW_COUNT = 5;
public static int TILE_COLUMN_COUNT = 5;
public static double TILE_SIZE = 120;
public static double offsetX = (SCENE_WIDTH - TILE_ROW_COUNT * TILE_SIZE) / 2;
public static double offsetY = (SCENE_HEIGHT - TILE_COLUMN_COUNT * TILE_SIZE) / 2;
List<Cell> cells = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
// create grid
for (int x = 0; x < TILE_ROW_COUNT; x++) {
for (int y = 0; y < TILE_COLUMN_COUNT; y++) {
// create tile
ImageView tile = new ImageView(image);
Rectangle2D rect = new Rectangle2D(TILE_SIZE * x, TILE_SIZE * y, TILE_SIZE, TILE_SIZE);
tile.setViewport(rect);
// consider empty cell, let it remain empty
if (x == (TILE_ROW_COUNT - 1) && y == (TILE_COLUMN_COUNT - 1)) {
tile = null;
}
cells.add(new Cell(x, y, tile));
}
}
// shuffle cells
shuffle();
// create playfield
Pane pane = new Pane();
// put tiles on playfield, assign event handler
for (int i = 0; i < cells.size(); i++) {
Cell cell = cells.get(i);
Node imageView = cell.getImageView();
// consider empty cell
if (imageView == null)
continue;
// click-handler: swap tiles, check if puzzle is solved
imageView.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseEvent -> {
moveCell((Node) mouseEvent.getSource());
});
// position images on scene
imageView.relocate(cell.getLayoutX(), cell.getLayoutY());
pane.getChildren().add(cell.getImageView());
}
Scene scene = new Scene(pane, SCENE_WIDTH, SCENE_HEIGHT);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* Swap images of cells randomly
*/
public void shuffle() {
Random rnd = new Random();
for (int i = 0; i < 1000; i++) {
int a = rnd.nextInt(cells.size());
int b = rnd.nextInt(cells.size());
if (a == b)
continue;
// skip bottom right cell swap, we want the empty cell to remain there
if( cells.get(a).isEmpty() || cells.get(b).isEmpty())
continue;
swap( cells.get(a), cells.get(b));
}
}
public void swap( Cell cellA, Cell cellB) {
ImageView tmp = cellA.getImageView();
cellA.setImageView(cellB.getImageView());
cellB.setImageView(tmp);
}
public boolean checkSolved() {
boolean allSolved = true;
for (Cell cell : cells) {
if (!cell.isSolved()) {
allSolved = false;
break;
}
}
System.out.println("Solved: " + allSolved);
return allSolved;
}
public void moveCell(Node node) {
// get current cell using the selected node (imageview)
Cell currentCell = null;
for (Cell tmpCell : cells) {
if (tmpCell.getImageView() == node) {
currentCell = tmpCell;
break;
}
}
if (currentCell == null)
return;
// get empty cell
Cell emptyCell = null;
for (Cell tmpCell : cells) {
if (tmpCell.isEmpty()) {
emptyCell = tmpCell;
break;
}
}
if (emptyCell == null)
return;
// check if cells are swappable: neighbor distance either x or y must be 1 for a valid move
int steps = Math.abs(currentCell.x - emptyCell.x) + Math.abs(currentCell.y - emptyCell.y);
if (steps != 1)
return;
System.out.println("Transition: " + currentCell + " -> " + emptyCell);
// cells are swappable => create path transition
Path path = new Path();
path.getElements().add(new MoveToAbs(currentCell.getImageView(), currentCell.getLayoutX(), currentCell.getLayoutY()));
path.getElements().add(new LineToAbs(currentCell.getImageView(), emptyCell.getLayoutX(), emptyCell.getLayoutY()));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(100));
pathTransition.setNode(currentCell.getImageView());
pathTransition.setPath(path);
pathTransition.setOrientation(PathTransition.OrientationType.NONE);
pathTransition.setCycleCount(1);
pathTransition.setAutoReverse(false);
final Cell cellA = currentCell;
final Cell cellB = emptyCell;
pathTransition.setOnFinished(actionEvent -> {
swap( cellA, cellB);
checkSolved();
});
pathTransition.play();
}
private static class Cell {
int x;
int y;
ImageView initialImageView;
ImageView currentImageView;
public Cell(int x, int y, ImageView initialImageView) {
super();
this.x = x;
this.y = y;
this.initialImageView = initialImageView;
this.currentImageView = initialImageView;
}
public double getLayoutX() {
return x * TILE_SIZE + offsetX;
}
public double getLayoutY() {
return y * TILE_SIZE + offsetY;
}
public ImageView getImageView() {
return currentImageView;
}
public void setImageView(ImageView imageView) {
this.currentImageView = imageView;
}
public boolean isEmpty() {
return currentImageView == null;
}
public boolean isSolved() {
return this.initialImageView == currentImageView;
}
public String toString() {
return "[" + x + "," + y + "]";
}
}
// absolute (layoutX/Y) transitions using the pathtransition for MoveTo
public static class MoveToAbs extends MoveTo {
public MoveToAbs(Node node) {
super(node.getLayoutBounds().getWidth() / 2, node.getLayoutBounds().getHeight() / 2);
}
public MoveToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
// absolute (layoutX/Y) transitions using the pathtransition for LineTo
public static class LineToAbs extends LineTo {
public LineToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static void main(String[] args) {
launch(args);
}
}
You can use Collections.shuffle to generate a random permutation of a List This can be used to place the tiles in a random order:
private static final int COLUMN_COUNT = 5;
private static final int ROW_COUNT = 5;
private static void fillGridPane(GridPane pane, ImageView[][] view, Image imag) {
List<ImageView> images = new ArrayList<>(24);
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
ImageView iv = new ImageView(imag);
images.add(iv);
view[i][j] = iv;
Rectangle2D rect = new Rectangle2D(120 * i, 120 * j, 120, 120);
iv.setViewport(rect);
}
}
Collections.shuffle(images);
Iterator<ImageView> iter = images.iterator();
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
pane.add(iter.next(), i, j);
}
}
}
The onMouseClicked event handler can be attached to the GridPane and MouseEvent.getTarget can be used to get the node that was clicked:
pane.setOnMouseClicked(event -> {
if (move(event)) {
if (checkWin()) {
new Alert(Alert.AlertType.INFORMATION, "You Win!").show();
}
}
});
private int emptyTileX = 4;
private int emptyTileY = 4;
private boolean move(MouseEvent event) {
Object target = event.getTarget();
if (target instanceof ImageView) {
ImageView iv = (ImageView) target;
int row = GridPane.getRowIndex(iv);
int column = GridPane.getColumnIndex(iv);
int dx = Math.abs(column - emptyTileX);
int dy = Math.abs(row - emptyTileY);
if ((dx == 0 && dy == 1) || (dx == 1 && dy == 0)) {
// swap image and empty tile, if they are next to each other
GridPane.setConstraints(iv, emptyTileX, emptyTileY);
emptyTileX = column;
emptyTileY = row;
return true;
}
}
return false;
}
To check the winning condition, you can simply check the positions of the ImageViews in the GridPane
private boolean checkWin() {
for (int i = 0; i < COLUMN_COUNT; i++) {
for (int j = 0, end = i == (COLUMN_COUNT - 1) ? ROW_COUNT - 1 : ROW_COUNT; j < end; j++) {
ImageView iv = view[i][j];
if (GridPane.getColumnIndex(iv) != i || GridPane.getRowIndex(iv) != j) {
return false;
}
}
}
return true;
}
Related
I'm currently trying to implement Conway's Game of Life in Java. On a first glance the application seems to work as it either stalls in the states that are expected as stalling states (e.g. block, bee-hive and tub) or changes infinitely.
However, as I took a closer look I never actually saw a Blinker or Beacon nor a Loaf (for reference images of these states see here). So I decided to implement a drawing mode and a one-step-update method that gives me the possibility to analyze exactly what's going on.
The strange thing that I found was the following:
When entering Drawing Mode I reset all cells and all buffered cells on the board to dead. Then, I drew a vertical Blinker somewhere in the middle of the board and did one update step and checked for the update mechanism of all cells that are alive (which are only the 3 cells belonging to the Blinker). In the output however, all of them returned that the value to their bottom left was alive (which actually could not be the case because nothing is being logged for these cells).
I'm really confused about where I went wrong in my code because this just doesn't make any sense for me. The values for the next generation are being stored in a buffer and only written back after the next generation has been calculated entirely, so I don't think that this should be the issue.
For reproducibility, here is the code:
Main.java
import java.awt.EventQueue;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
createAndShowUI();
});
}
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel();
JFrame frame = new JFrame("Game of Life");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
super.keyReleased(e);
if(e.getKeyCode() == e.VK_R) {
panel.reset();
panel.startAnimation();
}
if(e.getKeyCode() == e.VK_C) {
panel.clear();
}
if(e.getKeyCode() == e.VK_S) {
panel.startAnimation();
}
if(e.getKeyCode() == e.VK_SPACE) {
panel.stepAnimation();
}
}
});
frame.setVisible(true);
panel.populate();
panel.startAnimation();
}
}
AnimationPanel.java:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JPanel;
import javax.swing.Timer;
public class AnimationPanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 4929258196354814855L;
private int width = 1920;
private int height = 1080;
private int gridSize = 10;
private Cell[][] cells;
private Cell[][] buffer;
private Timer timer;
private boolean drawingMode = false;
public AnimationPanel() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if(drawingMode) {
int x = e.getX();
int y = e.getY();
int xIndex = (int)x / gridSize;
int yIndex = (int)y / gridSize;
cells[xIndex][yIndex].live = !cells[xIndex][yIndex].live;
repaint();
}
}
});
}
public void reset() {
timer.stop();
cells = null;
buffer = null;
populate();
}
public void clear() {
timer.stop();
for(Cell[] row : cells) {
for(Cell cell: row) {
cell.live = false;
}
}
for(Cell[] row : buffer) {
for(Cell cell: row) {
cell.live = false;
}
}
repaint();
drawingMode = true;
}
public void populate() {
Random random = new Random();
int horizontalCellCount = width / gridSize;
int verticalCellCount = height / gridSize;
cells = new Cell[horizontalCellCount][verticalCellCount];
buffer = new Cell[horizontalCellCount][verticalCellCount];
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
double randVal = random.nextDouble();
boolean live = (randVal < 0.05) ? true : false;
int x = i * gridSize;
int y = j * gridSize;
cells[i][j] = new Cell(live, x, y, gridSize);
buffer[i][j] = new Cell(live, x, y, gridSize);
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for(Cell[] row : cells) {
for(Cell cell : row) {
cell.draw(g);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public void startAnimation() {
drawingMode = false;
timer = new Timer(250, this);
timer.start();
}
public void stepAnimation() {
update();
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
private void update() {
int horizontalCellCount = width / gridSize;
int verticalCellCount = height / gridSize;
boolean log = false;
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
if(cells[i][j].live) {
log = true;
System.out.println("Cell: (" + i + "|" + j + ") " + cells[i][j].toString());
System.out.println("Row length: " + cells[i].length);
System.out.println("Column length: " + cells.length);
} else {
log = false;
}
int liveNeighours = 0;
for(int x = i - 1; x <= i + 1; x++) {
if(x < 0 || x == horizontalCellCount) {
continue;
}
for(int y = j - 1; y <= j + 1; y++) {
if(y < 0 || y == verticalCellCount || y == j && x == i) {
continue;
}
if(cells[x][y].live) {
liveNeighours++;
}
if(log) {
System.out.println("Cell at " + x + "|" + y + ": " + cells[x][y].live + " - liveNeighbours: " + liveNeighours);
}
}
}
Cell cell = cells[i][j];
if(liveNeighours > 3 || liveNeighours < 2) {
buffer[i][j].live = false;
} else if(!cell.live && liveNeighours == 3) {
buffer[i][j].live = true;
}
}
}
for(int i = 0; i < horizontalCellCount; i++) {
for(int j = 0; j < verticalCellCount; j++) {
cells[i][j] = buffer[i][j];
}
}
}
}
Cell.java
import java.awt.Color;
import java.awt.Graphics;
public class Cell {
public boolean live;
public int x;
public int y;
public int size;
public Cell(boolean live, int x, int y, int size) {
this.live = live;
this.x = x;
this.y = y;
this.size = size;
}
public void draw(Graphics g) {
if(live) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.WHITE);
}
g.fillRect(x, y, size, size);
}
#Override
public String toString() {
return "(" + x + " " + y + ") - Size: " + size;
}
}
Any help is really appreciated on this as I'm really confused what is wrong with the code.
This is implementation Lee's algorithm in Java. The problem is the deviation in the backtrace(on screenshots):
.
Please, any help?
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.JComboBox;
public class Main {
JFrame frame;
//GENERAL VARIABLES
private int cells = 20;
private int delay = 30;
private int startx = -1;
private int starty = -1;
private int finishx = -1;
private int finishy = -1;
private int tool = 0;
private int checks = 0;
private int length = 0;
private int WIDTH = 850;
private final int HEIGHT = 650;
private final int MSIZE = 600;
private int CSIZE = MSIZE/cells;
//UTIL ARRAYS
private String[] tools = {"Start","Finish","Wall", "Eraser"};
//BOOLEANS
private boolean solving = false;
//UTIL
Node[][] map;
Algorithm Alg = new Algorithm();
Random r = new Random();
//SLIDERS
JSlider size = new JSlider(1,5,4);
JSlider speed = new JSlider(0,500,delay);
//LABELS
JLabel toolL = new JLabel("Toolbox");
JLabel sizeL = new JLabel("Size:");
JLabel cellsL = new JLabel(cells+"x"+cells);
JLabel delayL = new JLabel("Delay:");
JLabel msL = new JLabel(delay+"ms");
JLabel checkL = new JLabel("Checks: "+checks);
JLabel lengthL = new JLabel("Path Length: "+length);
//BUTTONS
JButton searchB = new JButton("Search");
JButton resetB = new JButton("Reset");
JButton clearMapB = new JButton("Clear");
//DROP DOWN
JComboBox toolBx = new JComboBox(tools);
//PANELS
JPanel toolP = new JPanel();
//CANVAS
Map canvas;
public static void main(String[] args) { //MAIN METHOD
new Main();
}
public Main() { //CONSTRUCTOR
clearMap();
initialize();
}
public void clearMap() { //CLEAR MAP
finishx = -1; //RESET THE START AND FINISH
finishy = -1;
startx = -1;
starty = -1;
map = new Node[cells][cells]; //CREATE NEW MAP OF NODES
for(int x = 0; x < cells; x++) {
for(int y = 0; y < cells; y++) {
map[x][y] = new Node(3,x,y); //SET ALL NODES TO EMPTY
}
}
reset(); //RESET SOME VARIABLES
}
public void resetMap() { //RESET MAP
for(int x = 0; x < cells; x++) {
for(int y = 0; y < cells; y++) {
Node current = map[x][y];
if(current.getType() == 4 || current.getType() == 5) //CHECK TO SEE IF CURRENT NODE IS EITHER CHECKED OR FINAL PATH
map[x][y] = new Node(3,x,y); //RESET IT TO AN EMPTY NODE
}
}
if(startx > -1 && starty > -1) { //RESET THE START AND FINISH
map[startx][starty] = new Node(0,startx,starty);
map[startx][starty].setHops(0);
}
if(finishx > -1 && finishy > -1)
map[finishx][finishy] = new Node(1,finishx,finishy);
reset(); //RESET SOME VARIABLES
}
private void initialize() { //INITIALIZE THE GUI ELEMENTS
frame = new JFrame();
frame.setVisible(true);
frame.setResizable(false);
frame.setSize(WIDTH,HEIGHT);
frame.setTitle("Lee Algorithm");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
int space = 25;
int buff = 45;
toolP.setLayout(null);
toolP.setBounds(10,10,210,600);
searchB.setBounds(40,space, 120, 25);
toolP.add(searchB);
space+=buff;
resetB.setBounds(40,space,120,25);
toolP.add(resetB);
space+=buff;
clearMapB.setBounds(40,space, 120, 25);
toolP.add(clearMapB);
space+=40;
toolL.setBounds(40,space,120,25);
toolP.add(toolL);
space+=25;
toolBx.setBounds(40,space,120,25);
toolP.add(toolBx);
space+=buff;
sizeL.setBounds(15,space,40,25);
toolP.add(sizeL);
size.setMajorTickSpacing(10);
size.setBounds(50,space,100,25);
toolP.add(size);
cellsL.setBounds(160,space,40,25);
toolP.add(cellsL);
space+=buff;
delayL.setBounds(15,space,50,25);
toolP.add(delayL);
speed.setMajorTickSpacing(5);
speed.setBounds(50,space,100,25);
toolP.add(speed);
msL.setBounds(160,space,40,25);
toolP.add(msL);
space+=buff;
checkL.setBounds(15,space,100,25);
toolP.add(checkL);
space+=buff;
lengthL.setBounds(15,space,100,25);
toolP.add(lengthL);
frame.getContentPane().add(toolP);
canvas = new Map();
canvas.setBounds(230, 10, MSIZE+1, MSIZE+1);
frame.getContentPane().add(canvas);
searchB.addActionListener(new ActionListener() { //ACTION LISTENERS
#Override
public void actionPerformed(ActionEvent e) {
reset();
if((startx > -1 && starty > -1) && (finishx > -1 && finishy > -1))
solving = true;
}
});
resetB.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
resetMap();
Update();
}
});
clearMapB.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
clearMap();
Update();
}
});
toolBx.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
tool = toolBx.getSelectedIndex();
}
});
size.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
cells = size.getValue()*5;
clearMap();
reset();
Update();
}
});
speed.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
delay = speed.getValue();
Update();
}
});
startSearch(); //START STATE
}
public void startSearch() { //START STATE
if(solving)
{
Alg.LeeAlg();
}
pause(); //PAUSE STATE
}
public void pause() { //PAUSE STATE
int i = 0;
while(!solving) {
i++;
if(i > 500)
i = 0;
try {
Thread.sleep(1);
} catch(Exception e) {}
}
startSearch(); //START STATE
}
public void Update() { //UPDATE ELEMENTS OF THE GUI
CSIZE = MSIZE/cells;
canvas.repaint();
cellsL.setText(cells+"x"+cells);
msL.setText(delay+"ms");
lengthL.setText("Path Length: "+length);
checkL.setText("Checks: "+checks);
}
public void reset() { //RESET METHOD
solving = false;
length = 0;
checks = 0;
}
public void delay() { //DELAY METHOD
try {
Thread.sleep(delay);
} catch(Exception e) {}
}
class Map extends JPanel implements MouseListener, MouseMotionListener{ //MAP CLASS
public Map() {
addMouseListener(this);
addMouseMotionListener(this);
}
public void paintComponent(Graphics g) { //REPAINT
super.paintComponent(g);
for(int x = 0; x < cells; x++) { //PAINT EACH NODE IN THE GRID
for(int y = 0; y < cells; y++) {
switch(map[x][y].getType()) {
case 0:
g.setColor(Color.GREEN);
break;
case 1:
g.setColor(Color.BLUE);
break;
case 2:
g.setColor(Color.BLACK);
break;
case 3:
g.setColor(Color.WHITE);
break;
case 4:
g.setColor(Color.GRAY);
break;
case 5:
g.setColor(Color.YELLOW);
break;
}
g.fillRect(x*CSIZE,y*CSIZE,CSIZE,CSIZE);
g.setColor(Color.BLACK);
g.drawRect(x*CSIZE,y*CSIZE,CSIZE,CSIZE);
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
try {
int x = e.getX()/CSIZE;
int y = e.getY()/CSIZE;
Node current = map[x][y];
if((tool == 2 || tool == 3) && (current.getType() != 0 && current.getType() != 1))
current.setType(tool);
Update();
} catch(Exception z) {}
}
#Override
public void mouseMoved(MouseEvent e) {}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
resetMap(); //RESET THE MAP WHENEVER CLICKED
try {
int x = e.getX()/CSIZE; //GET THE X AND Y OF THE MOUSE CLICK IN RELATION TO THE SIZE OF THE GRID
int y = e.getY()/CSIZE;
Node current = map[x][y];
switch(tool) {
case 0: { //START NODE
if(current.getType()!=2) { //IF NOT WALL
if(startx > -1 && starty > -1) { //IF START EXISTS SET IT TO EMPTY
map[startx][starty].setType(3);
map[startx][starty].setHops(-1);
}
current.setHops(0);
startx = x; //SET THE START X AND Y
starty = y;
current.setType(0); //SET THE NODE CLICKED TO BE START
}
break;
}
case 1: {//FINISH NODE
if(current.getType()!=2) { //IF NOT WALL
if(finishx > -1 && finishy > -1) //IF FINISH EXISTS SET IT TO EMPTY
map[finishx][finishy].setType(3);
finishx = x; //SET THE FINISH X AND Y
finishy = y;
current.setType(1); //SET THE NODE CLICKED TO BE FINISH
}
break;
}
default:
if(current.getType() != 0 && current.getType() != 1)
current.setType(tool);
break;
}
Update();
} catch(Exception z) {} //EXCEPTION HANDLER
}
#Override
public void mouseReleased(MouseEvent e) {}
}
class Algorithm { // ALGORITHM CLASS
public void LeeAlg() {
ArrayList<Node> priority = new ArrayList<Node>(); //CREATE A PRIORITY QUE
priority.add(map[startx][starty]); //ADD THE START TO THE QUE
while (solving) {
if (priority.size() <= 0) { //IF THE QUE IS 0 THEN NO PATH CAN BE FOUND
solving = false;
break;
}
int hops = priority.get(0).getHops() + 1; //INCREMENT THE HOPS VARIABLE
ArrayList<Node> explored = exploreNeighbors(priority.get(0), hops); //CREATE AN ARRAYLIST OF NODES THAT WERE EXPLORED
if (explored.size() > 0) {
priority.remove(0); //REMOVE THE NODE FROM THE QUE
priority.addAll(explored); //ADD ALL THE NEW NODES TO THE QUE
Update();
delay();
} else { //IF NO NODES WERE EXPLORED THEN JUST REMOVE THE NODE FROM THE QUE
priority.remove(0);
}
}
}
public ArrayList<Node> exploreNeighbors(Node current, int hops) { //EXPLORE NEIGHBORS
ArrayList<Node> explored = new ArrayList<Node>(); //LIST OF NODES THAT HAVE BEEN EXPLORED
for(int a = -1; a <= 1; a++) {
for(int b = -1; b <= 1; b++) {
int xbound = current.getX()+a;
int ybound = current.getY()+b;
if((xbound > -1 && xbound < cells) && (ybound > -1 && ybound < cells)) { //MAKES SURE THE NODE IS NOT OUTSIDE THE GRID
Node neighbor = map[xbound][ybound];
if((neighbor.getHops()==-1 || neighbor.getHops() > hops) && neighbor.getType()!=2) { //CHECKS IF THE NODE IS NOT A WALL AND THAT IT HAS NOT BEEN EXPLORED
explore(neighbor, current.getX(), current.getY(), hops); //EXPLORE THE NODE
explored.add(neighbor); //ADD THE NODE TO THE LIST
}
}
}
}
return explored;
}
public void explore(Node current, int lastx, int lasty, int hops) { //EXPLORE A NODE
if(current.getType()!=0 && current.getType() != 1) //CHECK THAT THE NODE IS NOT THE START OR FINISH
current.setType(4); //SET IT TO EXPLORED
current.setLastNode(lastx, lasty); //KEEP TRACK OF THE NODE THAT THIS NODE IS EXPLORED FROM
current.setHops(hops); //SET THE HOPS FROM THE START
checks++;
if(current.getType() == 1) { //IF THE NODE IS THE FINISH THEN BACKTRACE TO GET THE PATH
backtrace(current.getLastX(), current.getLastY(),hops);
}
}
public void backtrace(int lx, int ly, int hops) { //BACKTRACE
length = hops;
while(hops > 1) { //BACKTRACE FROM THE END OF THE PATH TO THE START
Node current = map[lx][ly];
current.setType(5);
lx = current.getLastX();
ly = current.getLastY();
hops--;
}
solving = false;
}
}
class Node {
// 0 = start, 1 = finish, 2 = wall, 3 = empty, 4 = checked, 5 = finalpath
private int cellType = 0;
private int hops;
private int x;
private int y;
private int lastX;
private int lastY;
public Node(int type, int x, int y) { //CONSTRUCTOR
cellType = type;
this.x = x;
this.y = y;
hops = -1;
}
public int getX() {return x;} //GET METHODS
public int getY() {return y;}
public int getLastX() {return lastX;}
public int getLastY() {return lastY;}
public int getType() {return cellType;}
public int getHops() {return hops;}
public void setType(int type) {cellType = type;} //SET METHODS
public void setLastNode(int x, int y) {lastX = x; lastY = y;}
public void setHops(int hops) {this.hops = hops;}
}
}
That is, the shortest path on the map goes somewhere to the side, and should be in a straight line. What should be corrected in the code?
The problem is that according to the algorithm the answer is gave is as good as a strait line. Both the weird diagonal path and the strait desired path take 4 steps to reach the goal so they are both equal. To fix this you should add a weight. Instead of always incrementing hops by 1, increment it by 1.4 (sqrt(2)) when you move on a diagonal. This way there is a weighting for moving on a diagonal and the desired strait path would be the fastest (as it is in the real world).
Can someone explain how to solve this? DeleteRows() method is not working as intended.
It's a Tetris game and I'm trying to delete rows. When the blocks move down, the blocks are still counted as 0 so new blocks go through them. But this occurs only in the top row of the moved blocks:
package application;
import java.util.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.input.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Tetris extends Application {
public static final int MOVE_AMOUNT = 25;
public static final int SIZE = 25;
public static int XLIMIT = SIZE * 10;
public static int YLIMIT = SIZE * 24;
public static int[][] GRID = new int[XLIMIT/SIZE][YLIMIT/SIZE];
private static Pane group = new Pane();
private static Shape object;
private static Scene scene = new Scene(group, XLIMIT, YLIMIT);
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) throws Exception {
for(int[] a: GRID){
Arrays.fill(a, 0);
}
for(int i = 0; i <= XLIMIT; i+= SIZE){
Line a = new Line(i, 0, i, YLIMIT);
group.getChildren().add(a);
}
for(int i = 0; i <= YLIMIT; i+= SIZE){
Line a = new Line(0, i, XLIMIT, i);
group.getChildren().add(a);
}
for(int i = 0; i <= YLIMIT; i+= SIZE){
Text a = new Text("" + i);
a.setY(i);
group.getChildren().add(a);
}
for(int i = SIZE; i < XLIMIT; i+= SIZE){
Text a = new Text("" + i);
a.setY(10);
a.setX(i);
group.getChildren().add(a);
}
Shape a = TetrisHolder.createRect();
group.getChildren().addAll(a.a, a.b, a.c, a.d);
moveOnKeyPress(scene, a.a, a.b, a.c, a.d);
object = a;
stage.setScene(scene);
stage.show();
Timer myTimer=new Timer();
TimerTask task =new TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable(){
public void run(){
CheckDown(object);
}
});
}
};
myTimer.schedule(task,0,300);
}
private void moveOnKeyPress(Scene scene, Rectangle rect, Rectangle rect2, Rectangle rect3, Rectangle rect4) {
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent event) {
Shape shape = new Shape(rect, rect2, rect3, rect4);
switch (event.getCode()) {
case RIGHT:
TetrisHolder.CheckRight(shape);
break;
case DOWN:
CheckDown(shape);
break;
case LEFT:
TetrisHolder.CheckLeft(shape);
break;
case UP:
//TetrisHolder.CheckTurn(shape);
break;
}
}
});
}
private void CheckTurn(Shape shape){
}
private void DeleteRows(Pane pane){
ArrayList<Node> rects = new ArrayList<Node>();
ArrayList<Integer> lines = new ArrayList<Integer>();
int full = 0;
for(int i = 0; i < GRID[0].length; i++){
for(int j = 0; j < GRID.length; j++){
if(GRID[j][i] == 1)
full++;
}
if(full == GRID.length)
lines.add(i/*+lines.size()*/);
full = 0;
}
for(Node node: pane.getChildren()) {
if(node instanceof Rectangle) {
rects.add(node);
}
}
if(lines.size() > 0)
do{
for(Node node: rects){
Rectangle a = (Rectangle)node;
if(a.getY() == lines.get(0)*SIZE){
GRID[(int)a.getX()/SIZE][(int)a.getY()/SIZE] = 0;
pane.getChildren().remove(node);
}
if(a.getY() < lines.get(0)*SIZE){
GRID[(int)a.getX()/SIZE][(int)a.getY()/SIZE] = 0;
a.setY(a.getY() + SIZE);
GRID[(int)a.getX()/SIZE][(int)a.getY()/SIZE] = 1;
}
}
lines.remove(0);
rects.clear();
for(Node node: pane.getChildren()) {
if(node instanceof Rectangle) {
rects.add(node);
}
}
} while(lines.size() > 0);
}
private void CheckDown(Shape shape){
if((shape.c.getY() == YLIMIT - SIZE) || checkA(shape) || checkB(shape) || checkC(shape) || checkD(shape)){
GRID[(int)shape.a.getX()/SIZE][(int)shape.a.getY()/SIZE] = 1;
GRID[(int)shape.b.getX()/SIZE][(int)shape.b.getY()/SIZE] = 1;
GRID[(int)shape.c.getX()/SIZE][(int)shape.c.getY()/SIZE] = 1;
GRID[(int)shape.d.getX()/SIZE][(int)shape.d.getY()/SIZE] = 1;
DeleteRows(group);
Shape a = TetrisHolder.createRect();
object = a;
group.getChildren().addAll(a.a, a.b, a.c, a.d);
moveOnKeyPress(shape.a.getScene(), a.a, a.b, a.c, a.d);
}
if(shape.c.getY() + MOVE_AMOUNT < YLIMIT){
int checka = GRID[(int)shape.a.getX()/SIZE][((int)shape.a.getY()/SIZE) + 1];
int checkb = GRID[(int)shape.b.getX()/SIZE][((int)shape.b.getY()/SIZE) + 1];
int checkc = GRID[(int)shape.c.getX()/SIZE][((int)shape.c.getY()/SIZE) + 1];
int checkd = GRID[(int)shape.d.getX()/SIZE][((int)shape.d.getY()/SIZE) + 1];
if(checka == 0 && checka == checkb && checkb == checkc && checkc == checkd){
shape.a.setY(shape.a.getY() + MOVE_AMOUNT);
shape.b.setY(shape.b.getY() + MOVE_AMOUNT);
shape.c.setY(shape.c.getY() + MOVE_AMOUNT);
shape.d.setY(shape.d.getY() + MOVE_AMOUNT);
}
}
}
private boolean checkA(Shape shape){
return (GRID[(int)shape.a.getX()/SIZE][((int)shape.a.getY()/SIZE) + 1] == 1);
}
private boolean checkB(Shape shape){
return (GRID[(int)shape.b.getX()/SIZE][((int)shape.b.getY()/SIZE) + 1] == 1);
}
private boolean checkC(Shape shape){
return (GRID[(int)shape.c.getX()/SIZE][((int)shape.c.getY()/SIZE) + 1] == 1);
}
private boolean checkD(Shape shape){
return (GRID[(int)shape.d.getX()/SIZE][((int)shape.d.getY()/SIZE) + 1] == 1);
}
}
This is the code. The problem is at DeleteRows() method.
I'm building a custom ASCII terminal java display for make game or application.
I currently have a problem with Swing repaint processing. Some parts of the view is paint but after a little time, they disappear from the screen. This happens quickly when you click anywhere on the screen.
A example of error :
I read the Painting in AWT and Swing but I don't find the solution.
This is the current state of the AsciiPanel state.
package ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
/**
* JPanel with a ASCII render system
*
* #author julien MAITRE
*
*/
public class AsciiPanel extends JPanel {
private Dimension size;
private BufferedImage[] character;
private Color defaultCharacterColor;
private Color defaultCharacterBackgroundColor;
private Dimension characterSize;
private AsciiTerminalDataCell[][] terminal;
private AsciiTerminalDataCell[][] oldTerminal;
private Image image;
private Graphics2D graphics;
private int scale;
public AsciiPanel(Dimension dimension, String tilesetFile, int characterWidth, int characterHeight) {
this(dimension, tilesetFile, characterWidth, characterHeight, 1);
}
public AsciiPanel(Dimension dimension, String tilesetFile, int characterWidth, int characterHeight, int scale) {
this.size = dimension;
this.characterSize = new Dimension(characterWidth, characterHeight);
this.scale = scale;
this.defaultCharacterColor = Color.WHITE;
this.defaultCharacterBackgroundColor = Color.BLACK;
terminal = new AsciiTerminalDataCell[size.height][size.width];
oldTerminal = new AsciiTerminalDataCell[size.height][size.width];
for(int i = 0; i < size.height; i++){
for(int j = 0; j < size.width; j++){
terminal[i][j] = new AsciiTerminalDataCell();
oldTerminal[i][j] = new AsciiTerminalDataCell();
}
}
this.setPreferredSize(new Dimension(size.width*characterSize.width*scale, size.height*characterSize.height*scale));
try {
character = new BufferedImage[256];
BufferedImage tilesets = ImageIO.read(getClass().getResource(tilesetFile));
// Recuperation of the background color
BufferedImage imageBackgroundColor = tilesets.getSubimage(0, 0, 1, 1);
int color = imageBackgroundColor.getRGB(0, 0);
Color m_characterBackgroundColor = Color.getColor(null, color);
// Modification of characters background
Image characterBackgroundColorModified = createImage(new FilteredImageSource(tilesets.getSource(), new AsciiBackgroundFilter(m_characterBackgroundColor)));
// Creation of tileset with a modification of the background color
BufferedImage tilesetsModified = new BufferedImage(tilesets.getWidth(), tilesets.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics graphicsTilesetsModified = tilesetsModified.getGraphics();
graphicsTilesetsModified.setColor(Color.BLACK);
graphicsTilesetsModified.fillRect(0, 0, tilesetsModified.getWidth(), tilesetsModified.getHeight());
// Draw in a BufferedImage for characters recuperation
graphicsTilesetsModified.drawImage(characterBackgroundColorModified, 0, 0, this);
for(int i = 0; i < 256; i++){
int x = (i%16)*characterSize.width;
int y = (i/16)*characterSize.height;
character[i] = new BufferedImage(characterSize.width, characterSize.height, BufferedImage.TYPE_INT_ARGB);
character[i].getGraphics().drawImage(tilesetsModified, 0, 0, characterSize.width, characterSize.height, x, y, x+characterSize.width, y+characterSize.height, this);
}
}
catch (IOException ex) {
Logger.getLogger(AsciiTerminal.class.getName()).log(Level.SEVERE, null, ex);
}
this.setLayout(null);
}
public void write(int positionX, int positionY, char character, Color characterColor){
this.write(positionX, positionY, character, characterColor, defaultCharacterBackgroundColor);
}
public void write(int positionX, int positionY, AsciiTerminalDataCell character){
this.write(positionX, positionY, character.data, character.dataColor, character.backgroundColor);
}
public void write(int positionX, int positionY, char character, Color characterColor, Color characterBackgroundColor){
if(positionX < 0 || positionX > size.width - 1){
throw new IllegalArgumentException("X position between [0 and "+size.width+"]");
}
if(positionY < 0 || positionY > size.height - 1){
throw new IllegalArgumentException("Y position between [0 and "+size.height+"]");
}
terminal[positionY][positionX].data = character;
terminal[positionY][positionX].dataColor = characterColor;
terminal[positionY][positionX].backgroundColor = characterBackgroundColor;
}
public void writeString(int positionX, int positionY, String string, Color characterColor){
writeString(positionX, positionY, string, characterColor, defaultCharacterBackgroundColor);
}
public void writeString(int positionX, int positionY, String string, Color characterColor, Color characterBackgroundColor){
for(char c : string.toCharArray()){
this.write(positionX, positionY, c, characterColor, characterBackgroundColor);
positionX++;
}
}
public AsciiTerminalDataCell readCurrent(int x, int y){
return this.oldTerminal[y][x];
}
public AsciiTerminalDataCell readNext(int x, int y){
return this.terminal[y][x];
}
public void clear(){
clear(0, 0, size.width, size.height);
}
public void clear(int x, int y, int width, int height){
if(x < 0 || x > size.width - 1){
throw new IllegalArgumentException("X position between [0 and "+(size.width-1)+"]");
}
if(y < 0 || y > size.height - 1){
throw new IllegalArgumentException("Y position between [0 and "+(size.height-1)+"]");
}
if(width < 1){
throw new IllegalArgumentException("Width under 1");
}
if(height < 1){
throw new IllegalArgumentException("Height under 1");
}
if(width+x > size.width || height+y > size.height){
throw new IllegalArgumentException("Clear over the terminal");
}
for(int i = y; i < y + height; i++){
for(int j = x; j < x + width; j++) {
write(j, i, (char)0, defaultCharacterColor, defaultCharacterBackgroundColor);
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(image == null) {
image = this.createImage(this.getPreferredSize().width, this.getPreferredSize().height);
graphics = (Graphics2D)image.getGraphics();
graphics.setColor(defaultCharacterBackgroundColor);
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
}
for(Component component : getComponents()) {
component.paint(graphics);
}
for(int i = 0; i < size.height; i++){
for(int j = 0; j < size.width; j++){
if( terminal[i][j].data == oldTerminal[i][j].data &&
terminal[i][j].dataColor.equals(oldTerminal[i][j].dataColor) &&
terminal[i][j].backgroundColor.equals(oldTerminal[i][j].backgroundColor)) {
continue;
}
LookupOp lookupOp = setColorCharacter(terminal[i][j].backgroundColor, terminal[i][j].dataColor);
graphics.drawImage(lookupOp.filter(character[terminal[i][j].data], null), j*characterSize.width*scale, i*characterSize.height*scale, characterSize.width*scale, characterSize.height*scale, this);
oldTerminal[i][j].data = terminal[i][j].data;
oldTerminal[i][j].dataColor = terminal[i][j].dataColor;
oldTerminal[i][j].backgroundColor = terminal[i][j].backgroundColor;
}
}
g.drawImage(image, 0, 0, this);
}
private LookupOp setColorCharacter(Color bgColor, Color fgColor){
short[] red = new short[256];
short[] green = new short[256];
short[] blue = new short[256];
short[] alpha = new short[256];
// Recuperation of compound colors of foreground character color
short dcr = (short) fgColor.getRed();
short dcg = (short) fgColor.getGreen();
short dcb = (short) fgColor.getBlue();
// Recuperation of compound colors of background character color
short bgr = (short) bgColor.getRed();
short bgg = (short) bgColor.getGreen();
short bgb = (short) bgColor.getBlue();
for(short j = 0; j < 256; j++){
// if is foreground color
if(j != 0){
/**
* Calculation of j*255/dcr .
* Cross product
* dcr = 180 255
* j = ? X
* Distribute the requested color [0 to 255] on the character color [0 to X]
*/
// Red
if(dcr != 0){
red[j] = (short)(j*dcr/255);
}
else{
red[j] = 0;
}
// green
if(dcg != 0){
green[j] = (short)(j*dcg/255);
}
else{
green[j] = 0;
}
// Blue
if( dcb != 0){
blue[j] = (short)(j*dcb/255);
}
else{
blue[j] = 0;
}
// Alpha
alpha[j] = 255;
}
// else is background color
else {
red[j] = bgr;
green[j] = bgg;
blue[j] = bgb;
alpha[j] = 255;
}
}
short[][] data = new short[][]{red, green, blue, alpha};
LookupTable lookupTable = new ShortLookupTable(0, data);
LookupOp lookupOp = new LookupOp(lookupTable, null);
return lookupOp;
}
public Color getDefaultCharacterColor(){
return this.defaultCharacterColor;
}
public void setDefaultCharacterColor(Color color){
this.defaultCharacterColor = color;
}
public Color getDefaultCharacterBackgroundColor() {
return defaultCharacterBackgroundColor;
}
public void setDefaultCharacterBackgroundColor(Color defaultCharacterBackgroundColor) {
this.defaultCharacterBackgroundColor = defaultCharacterBackgroundColor;
}
public Dimension getCharacterSize() {
return characterSize;
}
public int getScale() {
return scale;
}
public AsciiTerminalDataCell getCell(int x, int y) {
return terminal[y][x];
}
}
ChiptuneTracker main class :
package main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URISyntaxException;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import ui.AsciiPanel;
import ui.AsciiTerminalDataCell;
import ui.CustomAsciiTerminal;
public class ChiptuneTracker {
public static final String TITLE = "ChiptuneTracker";
public static final int WINDOW_WIDTH = 29;
public static final int WINDOW_HEIGHT = 18;
public static final String TILESET_FILE = "/assets/wanderlust.png";
public static final String ICON_FILE = "/assets/icon.png";
public static final int CHARACTER_WIDTH = 12;
public static final int CHARACTER_HEIGHT = 12;
public static final int TARGET_FPS = 60;
public static final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
public static final int SCALE = 3;
public static final boolean CUSTOM_WINDOW = true;
private static ChiptuneTracker instance = new ChiptuneTracker();
private CustomAsciiTerminal asciiTerminal;
private AsciiPanel asciiPanel;
private boolean initSampleView = true;
private boolean initPatternView = true;
private Data data = new Data();
private DataManager dataManager;
private boolean changeData = false;
private Chanels chanels = new Chanels();
private View currentView;
private MenuView menuView;
private SampleView sampleView;
private PatternView patternView;
private ChiptuneTracker() {
asciiTerminal = new CustomAsciiTerminal(TITLE, new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT), TILESET_FILE, CHARACTER_WIDTH, CHARACTER_HEIGHT, SCALE, ICON_FILE, CUSTOM_WINDOW);
asciiPanel = asciiTerminal.getAsciiPanel();
asciiPanel.setDefaultCharacterBackgroundColor(Color.DARK_GRAY);
asciiPanel.setDefaultCharacterColor(Color.WHITE);
dataManager = new DataManager();
}
public void init() {
menuView = new MenuView(this);
sampleView = new SampleView(this);
patternView = new PatternView(this);
changeView(sampleView);
}
public void run() {
long lastLoopTime = System.nanoTime();
boolean stopRender = true;
while(true) {
long now = System.nanoTime();
double updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ChiptuneTracker.OPTIMAL_TIME;
// Screenshot
KeyEvent event = asciiTerminal.getEvent();
if(event != null) {
if(event.getKeyCode() == KeyEvent.VK_F12) {
try {
BufferedImage image = new BufferedImage(WINDOW_WIDTH * CHARACTER_WIDTH * SCALE, WINDOW_HEIGHT * CHARACTER_HEIGHT * SCALE, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
asciiPanel.paint(graphics);
boolean save = false;
int i = 0;
do {
File file = new File("screenshot-" + i + ".png");
if(!file.exists()) {
ImageIO.write(image, "PNG", file);
save = true;
}
i++;
} while(!save);
} catch (Exception e) {
JOptionPane.showMessageDialog(ChiptuneTracker.getInstance().getAsciiTerminal(), e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
finally {
asciiTerminal.setEvent(null);
}
}
}
// Update
currentView.update(delta);
chanels.update();
// Paint
asciiPanel.clear();
currentView.paint();
asciiTerminal.revalidate();
asciiTerminal.repaint();
try {
long value = (lastLoopTime - System.nanoTime() + ChiptuneTracker.OPTIMAL_TIME) / 1000000;
if(value > 0) {
Thread.sleep(value);
}
else {
Thread.sleep(5);
}
} catch (InterruptedException e) {
}
}
}
public void changeView(View nextView) {
if(currentView != null) {
currentView.quit();
}
currentView = nextView;
asciiPanel.clear();
currentView.init();
}
public static ChiptuneTracker getInstance() {
return instance;
}
public CustomAsciiTerminal getAsciiTerminal() {
return asciiTerminal;
}
public AsciiPanel getAsciiPanel() {
return asciiPanel;
}
public boolean isInitPatternView() {
return initPatternView;
}
public boolean isInitSampleView() {
return initSampleView;
}
public void setInitPatternView(boolean initPatternView) {
this.initPatternView = initPatternView;
}
public void setInitSampleView(boolean initSampleView) {
this.initSampleView = initSampleView;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public DataManager getDataManager() {
return dataManager;
}
public boolean isChangeData() {
return changeData;
}
public void setChangeData(boolean changeData) {
this.changeData = changeData;
}
public Chanels getChanels() {
return chanels;
}
public MenuView getMenuView() {
return menuView;
}
public SampleView getSampleView() {
return sampleView;
}
public PatternView getPatternView() {
return patternView;
}
public static void main(String[] args) {
ChiptuneTracker chiptuneTracker = ChiptuneTracker.getInstance();
chiptuneTracker.init();
chiptuneTracker.run();
}
}
Is there any solution to resolve that?
Thanks!
I have extended JTable so it can sort the data alphabetically and separate the data by sections according to their first letter.
See this question and marked answer for more clarification
The extended table works fine. However when sorted, by clicking on the table's header, I am facing a problem when scrolling down the table (using mouse-wheel).
Here is the code to the extended table. (Note: you can also find this code in the hyperlink above).
import java.awt.BorderLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableModel;
import javax.swing.JScrollPane;
import javax.swing.table.DefaultTableModel;
public class SectionedTable
extends JTable {
private static final long serialVersionUID = 1;
private final NavigableMap<Integer, String> sectionHeadings
= new TreeMap<>();
private final NavigableMap<Integer, Integer> rowTopEdges
= new TreeMap<>();
// Used when calling SwingUtilities.layoutCompoundLabel.
private final Rectangle iconBounds = new Rectangle();
private final Rectangle textBounds = new Rectangle();
public SectionedTable() {
init();
}
public SectionedTable(TableModel model) {
super(model);
init();
}
private void init() {
setShowGrid(false);
setAutoCreateRowSorter(true);
recomputeSections();
recomputeRowPositions();
}
private void recomputeSections() {
if (sectionHeadings == null) {
return;
}
sectionHeadings.clear();
RowSorter<? extends TableModel> sorter = getRowSorter();
if (sorter == null) {
return;
}
for (RowSorter.SortKey key : sorter.getSortKeys()) {
SortOrder order = key.getSortOrder();
if (order != SortOrder.UNSORTED) {
int sortColumn = key.getColumn();
String lastSectionStart = "";
int rowCount = getRowCount();
for (int row = 0; row < rowCount; row++) {
System.out.println("row er : " + row);
System.out.println("rowcount er : " + rowCount);
System.out.println("sortColumn er : " + sortColumn);
Object value = getValueAt(row, sortColumn);
if (value == null) {
value = "?";
}
String s = value.toString();
if (s.isEmpty()) {
s = "?";
}
String sectionStart = s.substring(0,
s.offsetByCodePoints(0, 1));
sectionStart = sectionStart.toUpperCase();
if (!sectionStart.equals(lastSectionStart)) {
sectionHeadings.put(row, sectionStart);
lastSectionStart = sectionStart;
}
}
break;
}
}
}
private void recomputeRowPositions() {
if (rowTopEdges == null) {
return;
}
rowTopEdges.clear();
int y = getInsets().top;
int rowCount = getRowCount();
int rowHeight = getRowHeight();
for (int row = 0; row < rowCount; row++) {
rowTopEdges.put(y, row);
y += getRowHeight(row);
if (sectionHeadings.containsKey(row)) {
y += rowHeight;
}
}
}
#Override
public void tableChanged(TableModelEvent event) {
//super.tableChanged(event);
recomputeSections();
recomputeRowPositions();
super.tableChanged(event);
}
#Override
public void sorterChanged(RowSorterEvent event) {
recomputeSections();
recomputeRowPositions();
super.sorterChanged(event);
}
#Override
public void validate() {
super.validate();
recomputeRowPositions();
}
#Override
public int rowAtPoint(Point location) {
Map.Entry<Integer, Integer> entry = rowTopEdges.floorEntry(location.y);
if (entry != null) {
int row = entry.getValue();
return row;
}
return -1;
}
#Override
public Rectangle getCellRect(int row,
int column,
boolean includeSpacing) {
Rectangle rect = super.getCellRect(row, column, includeSpacing);
int sectionHeadingsAbove = sectionHeadings.headMap(row, true).size();
rect.y += sectionHeadingsAbove * getRowHeight();
return rect;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
boolean ltr = getComponentOrientation().isLeftToRight();
int rowHeight = getRowHeight();
FontMetrics metrics = g.getFontMetrics();
int ascent = metrics.getAscent();
for (Map.Entry<Integer, String> entry : sectionHeadings.entrySet()) {
int row = entry.getKey();
String heading = entry.getValue();
Rectangle bounds = getCellRect(row, 0, true);
bounds.y -= rowHeight;
bounds.width = getWidth();
bounds.grow(-6, 0);
iconBounds.setBounds(0, 0, 0, 0);
textBounds.setBounds(0, 0, 0, 0);
String text = SwingUtilities.layoutCompoundLabel(this,
metrics, heading, null,
SwingConstants.CENTER, SwingConstants.LEADING,
SwingConstants.CENTER, SwingConstants.CENTER,
bounds, iconBounds, textBounds, 0);
g.drawString(text, textBounds.x, textBounds.y + ascent);
int lineY = textBounds.y + ascent / 2;
if (ltr) {
g.drawLine(textBounds.x + textBounds.width + 12, lineY,
getWidth() - getInsets().right - 12, lineY);
} else {
g.drawLine(textBounds.x - 12, lineY,
getInsets().left + 12, lineY);
}
}
}
public static void main(String[] args) {
DefaultTableModel model;
Object[][] data = new Object[50][5];
String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "Amy";
}
}
for (int i = 10; i < 20; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "Bob";
}
}
for (int i = 20; i < 30; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "Joe";
}
}
for (int i = 30; i < 50; i++) {
for (int j = 0; j < 5; j++) {
data[i][j] = "M";
}
}
SectionedTable table = new SectionedTable();
model = new DefaultTableModel(data, columnNames);
table.setModel(model);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new SectionedTable();
table.setModel(model);
JScrollPane scrollPane = new JScrollPane(table);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setSize(500, 500);
frame.setVisible(true);
}
});
}
}
I've been struggling to find out what might cause this problem and I'm not near any solution. I think it might be at the rowAtPoint() or getCellRect(), but I'm not sure. Can someone spot where this problem might lie in the code?
I noticed that the getScrollableUnitIncrement(...) method is returning 0 when the scrolling stops functioning.
As a quick hack I added the following to your table:
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
{
int increment = super.getScrollableUnitIncrement(visibleRect, orientation, direction);
if (increment == 0)
increment = 16;
return increment;
}
Don't know if it will cause other problems.
From this I was able to determine that 0 is returned and therefore no scrolling is done.