How to place the smaller rectangles inside larger rectangle using Java? - java

I stumbled upon a problem which i would like to solve it using Java. User inputs Larger Rectangle dimension (i.e L_width and L_height) and smaller rectangle dimension (i.e S_width and S_height). I would like to place as many smaller rectangle inside the larger rectangle and show it graphically.
for example: When the Larger Rectangle size is 4 x 5 and smaller rectangle size is 2 x 2, then the maximum number of smaller rectangle that i would be able to place it inside the larger rectangle is 4. I would like to show them graphically.
As im new to java, i wanted to know how i can approach this problem from programmatic point of view and what concept i have to use to achieve the same.
Initial code for calculating the maximum number of rectangles. Can any1 help me to show this result graphically using java
// Code Starts
import java.awt.Graphics;
import java.util.Scanner;
import javax.swing.JComponent;
import javax.swing.JFrame;
//Class to store the output of layout
class layout{
private int Cnt_BW_CW=0; // BoardWidth and CardWidth are arranged together
private int Cnt_BW_CH=0;
private int option=0; // Option 1: width-width Option 2: width-height
public int getCnt_BW_CW (){
return Cnt_BW_CW;
}
public int getCnt_BW_CH (){
return Cnt_BW_CH;
}
public int getoption (){
return option;
}
public void setCnt_BW_CW (int newValue){
Cnt_BW_CW = newValue;
}
public void setCnt_BW_CH (int newValue){
Cnt_BW_CH = newValue;
}
public void setoption (int newValue){
option = newValue;
}
}
// Stores the Dimension
class Dimension{
private float w,h;
Scanner input = new Scanner( System.in );
public Dimension(){
System.out.print( "Enter Width: " );
w = input.nextInt();
System.out.print( "Enter Height: " );
h = input.nextInt();
}
public Dimension(float width, float height){
w = width;
h = height;
}
public float getWidth (){
return w;
}
public float getHeight (){
return h;
}
public void setWidth (float newWidth){
w = newWidth;
}
public void setHeight (float newHeight){
h = newHeight;
}
}
class MyCanvas extends JComponent {
private static final long serialVersionUID = 1L;
public void paint(Graphics g) {
g.drawRect (10, 10, 200, 200);
}
}
public class boundedRect {
#SuppressWarnings("unused")
public static void main(String[] a) {
Dimension Board = new Dimension();
Dimension Card = new Dimension();
int Cnt =0;
Cnt = NumOfRect(Board, Card);
System.out.printf( "Number of Cards:%d",Cnt );
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 300,300);
window.getContentPane().add(new MyCanvas());
window.setVisible(true);
}
public static int NumOfRect(Dimension b,Dimension c){
float bw,bh,cw,ch;
int bw_cw,bh_ch,bw_ch,bh_cw;
int SameDimensionCnt,DiffDimensionCnt;
int count;
layout Result = new layout();
bw =b.getWidth(); bh = b.getHeight();
cw =c.getWidth(); ch = c.getHeight();
if (bw < cw || bh < ch){
System.out.println( "Board and Card Dimension mismatch" );
System.exit(0);
}
bw_cw = (int)Math.floor(bw/cw);
bh_ch = (int)Math.floor(bh/ch);
SameDimensionCnt = bw_cw * bh_ch;
Result.setCnt_BW_CW(SameDimensionCnt);
bw_ch = (int)Math.floor(bw/ch);
bh_cw = (int)Math.floor(bh/cw);
DiffDimensionCnt = bw_ch * bh_cw;
Result.setCnt_BW_CH(DiffDimensionCnt);
System.out.printf( "Matching BW x CW: %d\n",SameDimensionCnt );
System.out.printf( "Matching BW x CH: %d\n",DiffDimensionCnt );
if (SameDimensionCnt < DiffDimensionCnt ){
count = DiffDimensionCnt;
System.out.println( "Align Board Width and Card Height" );
Result.setoption(2);
}else {
count = SameDimensionCnt;
System.out.println( "Align Board Width and Card Width" );
Result.setoption(1);
}
return count;
}
}

So you want to tile a large rectangle with a number of smaller rectangles. First define a class to represent the small rectangles, and create a data structure (probably an ArrayList) to hold them. Use a nested for loop to walk over the area of the large rectangle in S_width/S_height steps, and create as many small rectangles as will fit. Add them to the ArrayList as they are created. Search for ArrayList on Google to find the Java docs if you need them.
Then you need to write the code to draw them on the screen. For that, look up the official Java Tutorial on Google and read the section on graphics.
Try writing the code first and if you have problems, post your code here (you can edit the question).

Related

How do I remove the recursion from the drawSquare method and get the exact same result?

I need to remove the recursion from the drawSquare method. There is a lot more I have to do after removing the recursion from that method however the rest I can figure out on my own. I just really need a working solution that does the exact same thing without recursion and I will figure out the rest.
Here is how I made the Square class:
import java.awt.Color;
public class Square {
final int BLACK = Color.BLACK.getRGB();
final int WHITE = Color.WHITE.getRGB();
protected int center_x;
protected int center_y;
protected int side;
protected int color;
protected Square parentSquare;
public Square(){
this.center_x = 0;
this.center_y = 0;
this.side = 0;
this.color = WHITE;
this.parentSquare = null;
}
public Square(int center_x,int center_y,int side,int color){
this.center_x = center_x;
this.center_y = center_y;
this.side = side;
this.color = color;
this.parentSquare = null;
}
public Square(int center_x,int center_y,int side,int color,Square parentSquare){
this.center_x = center_x;
this.center_y = center_y;
this.side = side;
this.color = color;
this.parentSquare = parentSquare;
}
public void setX(int center_x){
this.center_x = center_x;
}
public int getX(){
return center_x;
}
public void setY(int center_y){
this.center_x = center_y;
}
public int getY(){
return center_y;
}
public void setSide(int side){
this.side = side;
}
public int getSide(){
return side;
}
public void setColor(int color){
this.color = color;
}
public int getColor(){
return color;
}
public void setParent(Square parentSquare){
this.parentSquare = parentSquare;
}
public Square getParent(){
return parentSquare;
}
}
This is the original Tsquare.java that produces a fractal of squares branching from each squares 4 corners until the side = 0: (full TSquare.java class modified to use Square objects)
import java.awt.image.*;
import java.awt.Color;
import java.io.*;
import javax.imageio.*;
import java.util.*;
public class TSquare {
static final int SIDE = 1000; // image is SIDE X SIDE
static BufferedImage image = new BufferedImage(SIDE, SIDE, BufferedImage.TYPE_INT_RGB);
static final int WHITE = Color.WHITE.getRGB();
static final int BLACK = Color.BLACK.getRGB();
static Scanner kbd = new Scanner(System.in);
public static void main(String[] args) throws IOException{
String fileOut = "helloSquares.png";
System.out.print("Enter (x,y) coordinates with a space between: ");
int x = kbd.nextInt();
int y = kbd.nextInt();
System.out.println(x+","+y);//TESTLINE TESTLINE TESTLINE TESTLINE
// make image black
for (int i = 0; i < SIDE; i++) {
for (int j = 0; j < SIDE; j++) {
image.setRGB(i, j, BLACK);
}
}
Square square = new Square(SIDE/2,SIDE/2,SIDE/2,WHITE);
drawSquare(square);
// save image
File outputfile = new File(fileOut);
ImageIO.write(image, "jpg", outputfile);
}
private static void drawSquare(Square square){ // center of square is x,y length of side is s
if (square.side <= 0){ // base case
return;
}else{
// determine corners
int left = square.center_x - (square.side/2);
int top = square.center_y - (square.side/2);
int right = square.center_x + (square.side/2);
int bottom = square.center_y + (square.side/2);
int newColor =square.color-100000;
Square newSquareA = new Square(left,top,square.side/2,newColor);
Square newSquareB = new Square(left,bottom,square.side/2,newColor);
Square newSquareC = new Square(right,top,square.side/2,newColor);
Square newSquareD = new Square(right,bottom,square.side/2,newColor);
for (int i = left; i < right; i++){
for (int j = top; j < bottom; j++){
image.setRGB(i, j, square.color);
}
}
// recursively paint squares at the corners
drawSquare(newSquareA);
drawSquare(newSquareB);
drawSquare(newSquareC);
drawSquare(newSquareD);
}
}
}
I'm looking to reproduce the exact actions of this code just minus the recursion and everything I try doesn't seem to work. I cant even get a single white square to display on top of the original black canvas.
If we want readability without compromising speed I suggest first making some additions to Square:
public int half() {
return side/2;
}
public int left() {
return center_x - half();
}
public int top() {
return center_y - half();
}
public int right() {
return center_x + half();
}
public int bottom() {
return center_y + half();
}
public void draw(BufferedImage image) {
int left = left();
int top = top();
int right = right();
int bottom = bottom();
for (int i = left; i < right; i++){
for (int j = top; j < bottom; j++){
image.setRGB(i, j, color);
}
}
} //End Square
Also moving I/O out to enable unit testing.
package com.stackoverflow.candied_orange;
import java.awt.image.*;
import java.awt.Color;
import java.io.*;
import javax.imageio.*;
import java.util.*;
public class FractalSquareIterative {
public static void main(String[] args) throws IOException{
final int SIDE = 1000; // image is SIDE X SIDE
BufferedImage image = new BufferedImage(SIDE,SIDE,BufferedImage.TYPE_INT_RGB);
drawImage(SIDE, image);
saveImage(image);
}
//Removed IO to enable unit testing
protected static void drawImage(final int SIDE, BufferedImage image) {
final int BLACK = Color.BLACK.getRGB();
final int WHITE = Color.WHITE.getRGB();
final int HALF = SIDE / 2;
//Draw background on whole image
new Square(HALF, HALF, SIDE, BLACK).draw(image);
//Draw foreground starting with centered half sized square
Square square = new Square(HALF, HALF, HALF, WHITE);
drawFractal(square, image);
}
Now that Square is dealing with all the square things the fractal code is a little easier on the eyes.
private static void drawFractal(Square square, BufferedImage image){
Queue<Square> squares = new LinkedList<>();
squares.add(square);
while (squares.size() > 0) {
//Consume
square = squares.remove();
//Produce
int half = square.half();
if (half > 2) {
int left = square.left();
int top = square.top();
int right = square.right();
int bottom = square.bottom();
int newColor = square.color - 100000;
squares.add(new Square(left, top, half, newColor));
squares.add(new Square(left, bottom, half, newColor));
squares.add(new Square(right, top, half, newColor));
squares.add(new Square(right, bottom, half, newColor));
}
square.draw(image);
}
}
protected static void saveImage(BufferedImage image) throws IOException {
String fileOut = "helloSquares.png";
File outputfile = new File(fileOut);
ImageIO.write(image, "jpg", outputfile);
}
} //End FractalSquareIterative
Reliably faster than the recursive version but not significantly so at this size.
If you want a peek at my unit tests you'll find them here.
Here's one implementation using an ArrrayDeque ( https://docs.oracle.com/javase/7/docs/api/java/util/ArrayDeque.html ).
It's worth comparing ArrayDeque against some other Java types :
a) Stack is an interface, and the api page (https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html) says
A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class.
b) I originally wrote this using good old familiar ArrayList, with Square square = squares.remove(0); instead of pop. I am quite surprised at how much faster this ArrayDeque implementation appears to be than that ArrayList (not that I have run any formal benchmarks)
private static void drawSquare(Square startSquare){
Deque<Square> squares = new ArrayDeque<Square>(400000);
squares.push(startSquare);
while (!squares.isEmpty()) {
Square square = squares.pop();
System.out.println(square);
// center of square is x,y length of side is s
if (square.side > 0){ // base case
// determine corners
int left = square.center_x - (square.side/2);
int top = square.center_y - (square.side/2);
int right = square.center_x + (square.side/2);
int bottom = square.center_y + (square.side/2);
int newColor =square.color-100000;
addSquare(squares, left,top,square.side/2,newColor);
addSquare(squares, left,bottom,square.side/2,newColor);
addSquare(squares, right,top,square.side/2,newColor);
addSquare(squares, right,bottom,square.side/2,newColor);
}
}
}
private static void addSquare(Deque<Square> squares, int x, int y, int side, int color) {
// STRONGLY recommend having this "if" statement !
// if (side > 0) {
squares.push(new Square(x, y, side, color));
// }
}
As noted in my comment, it is WELL worthwhile to not create squares of size 0 rather than creating them and simply ignoring them when their turn comes around. This would be true for the recursion-based operations as well - but especially so for these non-recursion based ones, since the multitude of squares would be really eating up memory and processing time.

Drawing a two colored checker board with nested for loops where each square being their own object (Java)

I am attempting to draw a checkerboard pattern in java using nested for loops, but I am having trouble doing it with two different colors. I know this question has been asked before, but it hasn't been asked with two different colors on the board that are not just using a background color. I plan on using the individual squares as an array to hold checker positions, so I do need each individual square made. Would it be better to drop the ice of a nested for loop to create each square, or should i stick with that shortcut? And if I were to stick with it, how would the nested loop be formatted (one for each color)?
When creating checker tiles, I would pass in an int for the x coordinate, and y coordinate such as:
import java.awt.Color;
import java.awt.Graphics;
public class CheckerTile {
public static final int WIDTH = 100; //width of each tile
public static final int HEIGHT = 100; //height of each tile, most likely same as width so its a square
public static int currentId = 0; //variable to reference unique id for each tile
private int id; //current id of tile
private int x; //x coordinate
private int y; //y coordinate
private int width; //width of tile
private int height; //height of tile
//Default constructor to take x and y coordinate
public CheckerTile( int x, int y ) {
this.id = currentId++;
this.x = x;
this.y = y;
width = WIDTH;
height = HEIGHT;
}
public int getId()
{
return id;
}
//draws the tile on the panel.
public void draw(Graphics g)
{
//if the checkerTile's id is divisible by 2, draw it red, otherwise draw it black.
g.setColor( id % 2 == 0 ? Color.RED : Color.black);
g.fillRect(x, y, width, height);
}
}
That way we have a way to draw the tile on the board. Now, when creating each object, we increment a currentId variable so that we can color each one individually using the modulus operator later.
I am assuming you are using Swing so I decided to add a draw(Graphics g) method so when repainting in java it would use that Graphics object. If you are using a different library, then you will have to do some research in to how to draw it on the board.
Now in your JPanel, it would look something like this:
//Creates the JPanel, which needs to be added to JFrame object in main
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CheckerBoard extends JPanel {
CheckerTile[][] checkerTiles; //2-dimension array of checkerTiles
public CheckerBoard() {
super();
this.setSize(800,800);
checkerTiles = new CheckerTile[9][9];
//This creates the checkerTiles.
for(int i = 0; i < 9; i++)
{
for( int j = 0; j < 9; j++)
{
checkerTiles[i][j] = new CheckerTile( j * CheckerTile.WIDTH, i * CheckerTile.HEIGHT );
}
}
this.setVisible(true);
//Repaint right away to show results.
repaint();
}
//We need to override this to paint the tiles on the board.
#Override
public void paintComponent(Graphics g)
{
for(int i = 0; i < checkerTiles.length; i++)
{
for(int j = 0; j < checkerTiles[i].length; j++)
{
//call the draw method on each tile.
checkerTiles[i][j].draw(g);
}
}
}
//A demo of adding the panel to a frame and showing the tiles.
public static void main(String[] args)
{
//Create the JFrame and add the CheckerBoard we made to it.
JFrame frame = new JFrame();
frame.setSize(800,800);
frame.setLayout(new BorderLayout());
frame.add(new CheckerBoard(), BorderLayout.CENTER);
frame.setVisible(true);
}
}

Why wont squares show up after repaint()?

I posted this question a bit earlier and was told to make it SSCCE so here goes (if I can make any improvements feel free to let me know):
I'm wondering why when my button "confirm" is clicked the old squares disappear and the redrawn squares do not appear on my GUI (made with swing). The Squares class draws 200 spaced out squares with an ID (0, 1, 2, or 3 as String) inside obtained from a different class (for the purpose of this question, let's assume it is always 0 and not include that class). For clarification: Squares draws everything perfectly the first time (also retrieves the correct IDs), but I want it to redraw everything once the button is clicked with new IDs.
Code for Squares:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.ArrayList;
public class Squares extends JPanel{
private ArrayList<Rectangle> squares = new ArrayList<Rectangle>();
private String stringID = "0";
public void addSquare(int x, int y, int width, int height, int ID) {
Rectangle rect = new Rectangle(x, y, width, height);
squares.add(rect);
stringID = Integer.toString(ID);
if(ID == 0){
stringID = "";
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontMetrics fm = g2.getFontMetrics();
int fontAscent = fm.getAscent();
g2.setClip(new Rectangle(0,0,Integer.MAX_VALUE,Integer.MAX_VALUE));
for (Rectangle rect : squares) {
g2.drawString(stringID, rect.x + 7, rect.y + 2 + fontAscent);
g2.draw(rect);
}
}
}
Code for GUI:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUIReserver extends JFrame implements Runnable{
private int myID;
private JButton confirm = new JButton("Check Availability and Confirm Reservation");
private JFrame GUI = new JFrame();
private Squares square;
public GUIReserver(int i) {
this.myID = i;
}
#Override
public void run() {
int rows = 50;
int seatsInRow = 4;
confirm.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
GUI.getContentPane().remove(square);
square = new Squares();
int spaceNum = 0;
int rowNum = 0;
int offsetX = 200;
int offsetY = 0;
for(int i = 0; i < rows * seatsInRow; i++){
square.addSquare(rowNum * 31 + offsetX,spaceNum * 21 + 50 + offsetY,20,20, 0); //normally the 4th parameter here would retrieve the ID from the main class
rowNum++;
if(rowNum == 10){
rowNum = 0;
spaceNum++;
}
if(spaceNum == 2){
spaceNum = 3;
rowNum = 0;
}
if(spaceNum == 5){
spaceNum = 0;
offsetY += 140;
}
}
GUI.getContentPane().add(square); //this does not show up at all (could be that it wasn't drawn, could be that it is out of view etc...)
GUI.repaint(); //the line in question
}
});
GUI.setLayout(new FlowLayout());
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.setLocation(0,0);
GUI.setExtendedState(JFrame.MAXIMIZED_BOTH);
square = new Squares();
int spaceNum = 0;
int rowNum = 0;
int offsetX = 200;
int offsetY = 0;
for(int i = 0; i < rows * seatsInRow; i++){
square.addSquare(rowNum * 31 + offsetX,spaceNum * 21 + 50 + offsetY,20,20, 0); //normally the 4th parameter here would retrieve the ID from the main class
rowNum++;
if(rowNum == 10){
rowNum = 0;
spaceNum++;
}
if(spaceNum == 2){
spaceNum = 3;
rowNum = 0;
}
if(spaceNum == 5){
spaceNum = 0;
offsetY += 140;
}
}
GUI.getContentPane().add(square); //this shows up the way I wish
GUI.add(confirm);
GUI.pack();
GUI.setVisible(true);
}
}
Code for main:
public class AircraftSeatReservation {
static AircraftSeatReservation me = new AircraftSeatReservation();
private final int rows = 50;
private final int seatsInRow = 4;
private int seatsAvailable = rows * seatsInRow;
private Thread t3;
public static void main(String[] args) {
GUIReserver GR1 = new GUIReserver(3);
me.t3 = new Thread(GR1);
me.t3.start();
}
}
One major problem: Your Squares JPanels preferred size is only 20 by 20, and will likely actually be that size since it seems to be added to a FlowLayout-using container. Next you seem to be drawing at locations that are well beyond the bounds of this component, and so the drawings likely will never be seen. Consider allowing your Squares objects to be larger, and make sure to only draw within the bounds of this component.
Note also there is code that doesn't make sense, including:
private int myID;
private JTextField row, column, instru draft saved // ???
package question2;ction1, instruction2, seatLabel, rowLabel; // ???
I'm guessing that it's
private int myID;
private JTextField row, column, instruction1, instruction2, seatLabel, rowLabel;
And this won't compile for us:
int rows = AircraftSeatReservation.getRows();
int seatsInRow = AircraftSeatReservation.getSeatsInRow(); // and shouldn't this take an int row parameter?
since we don't have your AircraftSeatReservation class (hopefully you don't really have static methods in that class).
And we can't compile or run your current code. We don't want to see your whole program, but rather you should condense your code into the smallest bit that still compiles, has no extra code that's not relevant to your problem, but still demonstrates your problem. So as Andrew Thompson recommends, for better help, please create and post your Minimal, Complete, and Verifiable example or Short, Self Contained, Correct Example.
I would try to OOP-ify your problem as much as possible, to allow you to divide and conquer. This could involve:
Creating a SeatClass enum, one with possibly two elements, FIRST and COACH.
Creating a non-GUI Seat class, one with several fields including possibly: int row, char seat ( such as A, B, C, D, E, F), a SeatClass field to see if it is a first class seat or coach, and a boolean reserved field that is only true if the seat is reserved.
This class would also have a getId() method that returns a String concatenation of the row number and the seat char.
Creating a non-GUI Airplane class, one that holds two arrays of Seats, one for SeatClass.FIRST or first-class seats, and one for SeatClass.COACH.
It would also have a row count field and a seat count (column count) field.
After creating all these, then work on your GUI classes.
I'd create a GUI class for Seats, perhaps GuiSeat, have it contain a Seat object, perhaps have it extend JPanel, allow it to display its own id String that it gets from its contained Seat object, have it override getBackground(...) so that it's color will depend on whether the seat is reserved or not.
etc.....

Having trouble with drawPanel creating grid

Trying to make a grid of 2^n size, asking the user for 'n'. I haven't coded in the 2^n part, which is also a little confusing for me. But right now my board will not display correctly when I get input from the user. My drawLine is a diagonal line going through the whole board.
How do I get the board to be displayed correctly?
Here is my code:
import java.awt.*;
import java.util.*;
public class DrawingPanelTest2{
public static void main(String args[]){
// System.out.println("How big do you want your Tromino grid?");
// System.out.println("Please enter a perfect power of 2.");
// int size = stdin.nextInt();
//create a drawing panel of width=400px and height=400px
DrawingPanel panel = new DrawingPanel(400, 400);
//set the background of the panel to CYAN
panel.setBackground(Color.LIGHT_GRAY);
//create a graphic object for the panel
Graphics g = panel.getGraphics();
//draw square
drawFigure_1(g,0,0);
}
public static void drawFigure_1(Graphics g,int x, int y) {
Scanner stdin = new Scanner(System.in);
System.out.println("How big do you want your Tromino grid?");
System.out.println("Please enter a perfect power of 2.");
int size = stdin.nextInt();
//set your drawing color to red
g.setColor(Color.BLACK);
for (int i = 1; i <= size; i++) {
//draw a rectangle, (x,y) is the top-left cordiante of the rectangle, and ((i*z), (i*z))
//are the width and height of the rectangle
g.drawRect(x, y, i * size, i * size);
g.drawLine(x, y, i *size, i *size);
}
g.setColor(Color.BLACK);
}
}
This Graphics g = panel.getGraphics(); is not how custom painting is done.
This Scanner stdin = new Scanner(System.in); is not how you should interacting with the user from within the context of GUI
Start by taking a look at Creating a GUI with Swing and Performing Custom Painting
Take a look at the Graphics Java Docs
Graphics#drawRect takes 4 parameters, the x, y position (top left corner) and the width and height of the rectenagle, whereas Graphics#drawLine takes x1, y1, which is the start point and x2, y2 which is the end point.
So you want to draw a horizontal line, you need to use something more like g.drawLine(x, y, i * size, i); or for a vertical line, something more like g.drawLine(x, y, i, i * size);
If you are trying to draw a grid, then you will need loops, one horizontal and one vertical. You will also need to update the x/y of each rectangle, so that they are placed corrected, so rather than modifying the size parameters, you should be modifying the position parameters
Try something like this:
import java.util.*;
import java.awt.*;
import javax.swing.*;
class myjava{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
double pw = input.nextDouble();
myPan panel = new myPan(pw);
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(400, 400);
application.setVisible(true);
}
}
class myPan extends JPanel{
public double pow;
public myPan(double p){
pow = p;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
double num = Math.pow(2,pow);
double across;
double up;
if(pow % 2 == 0){ //is a square
System.out.println("square");
across = Math.pow(num,0.5);
up = across;
}
else{
System.out.println("not");
double x = Math.floor(pow/2);
double y = x + 1;
across = Math.pow(2,x);
up = Math.pow(2,y);
}
System.out.println(across);
System.out.println(up);
//
//
double wid = 400/across; //width of one
double hi = 400/up; //height of one
double nowX = 0;
double nowY = 0;
for(int i = 0; i < up; i++){ //top to bottom
nowX = 0;
for(int j = 0; j < across; j++){
//System.out.print("*");
g.setColor(Color.BLACK);
g.drawRect((int)nowX, (int)nowY, (int)wid, (int)hi);
nowX = nowX + wid;
}
nowY = nowY + hi;
//System.out.print("\n");
}
}
}

AffineTransform.rotate() - how do I xlate, rotate, and scale at the same time?

I have the following code which does (the first part of) what I want drawing a chessboard with some pieces on it.
Image pieceImage = getImage(currentPiece);
int pieceHeight = pieceImage.getHeight(null);
double scale = (double)side/(double)pieceHeight;
AffineTransform transform = new AffineTransform();
transform.setToTranslation(xPos, yPos);
transform.scale(scale, scale);
realGraphics.drawImage(pieceImage, transform, this);
that is, it gets a chess piece's image and the image's height, it translates the drawing of that image to the square the piece is on and scales the image to the size of the square.
Llet's say I want to rotate the black pieces 180 degrees. Somewhere I expect to have something like:
transform.rotate(Math.toRadians(180) /* ?, ? */);
But I can't figure out what to put in as X and Y. If I put nothing, the image is nicely rotated around the 0,0 point of its chessboard square, putting the piece upside down in the square to the northeast of where it is supposed to be. I've guessed at various other combinations of x,y, with no luck yet.
I am already using translation to put the piece in the right square, the rotation transform wants another x,y around which to rotate things, but I don't know how to tell the transform to rotate the piece around one x,y and write the image to a different x,y. Can someone help me with the rotation parameters, or point me to something that explains how these things work? I've found examples of things that don't explain how they work, and so far I haven't figured out how to alter them to my situation...
Major edit: addition of working code. Sorry, I don't know how to post images, please substitute your own.
When I run the following I get a 2x2 chess board with a rook at the top left and a knight at the bottom right.
If I go into SmallChessboardComponent and take the comment delims off the first rotation transform statement, I get the rook in its original place upside down and the knight does not appear. If I instead take the comment delims off the second transform statement, neither piece appears at all.
I am looking for a way to turn the pieces upside down on the square on which they would appear anyway. I want to draw each piece onto the board; I don't want code that flips the board.
main program:
package main;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import directredraw.SmallChessboardComponent;
public class SmallChessboardMain
{
private static void dbg (String message) { System.out.println(message); }
public static void main(String[] args)
{
//Create the top-level container and add contents to it.
final JFrame frame = new JFrame("Small Chessboard");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create the chessboard itself and set it in the component
SmallChessboard chessboard = new SmallChessboard();
// create the GUI component that will contain the chessboard
SmallChessboardComponent chessboardComponent = new SmallChessboardComponent();
chessboardComponent.setBoard (chessboard);
frame.getContentPane().add(chessboardComponent, BorderLayout.CENTER);
// pack and display all this
frame.pack();
frame.setVisible(true);
}
}
chessboard class:
package main;
public class SmallChessboard
{
Piece [][] squares = new Piece[2][2];
public SmallChessboard()
{
squares[0][0] = new Piece(Piece.WHITECOLOR, Piece.ROOK);
squares[1][1] = new Piece(Piece.WHITECOLOR, Piece.KNIGHT);
}
/**
* get the piece at the given rank and file; null if
* no piece exists there.
*/
public Piece getPiece(int rank, int file)
{
if (0 > rank || rank > 2 || 0 > file || file > 2) { return null; }
else { return squares[rank][file]; }
}
}
chessboard component class:
package directredraw;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import javax.swing.JPanel;
import main.Piece;
import main.PieceImages;
import main.SmallChessboard;
public class SmallChessboardComponent extends JPanel
{
private static final long serialVersionUID = 1L;
Color whiteSquareColor = Color.yellow;
Color blackSquareColor = Color.blue;
private static void dbg (String msg) { System.out.println(msg); }
private SmallChessboard chessboard = null;
// currently playing with rotating images; this affine transform
// should help
AffineTransform rotationTransform = null;
private final int DEFAULT_PREFERRED_SIDE = 400;
int wholeSide = DEFAULT_PREFERRED_SIDE;
int side = DEFAULT_PREFERRED_SIDE / 8;
public void setBoard (SmallChessboard givenBoard)
{ chessboard = givenBoard;
}
/**
* set either or both colors for this chessboard; if either of
* the arguments are null, they do not change the existing color
* setting.
*/
public void setColors (Color darkSquare, Color lightSquare)
{
if (darkSquare != null) { blackSquareColor = darkSquare; }
if (lightSquare != null) { whiteSquareColor = lightSquare; }
}
/**
* return the preferred size for this component.s
*/
public Dimension getPreferredSize()
{ return new Dimension(wholeSide, wholeSide);
}
/*
* return the image object for the given piece
*/
private Image getImage(Piece piece)
{ return PieceImages.getPieceImage(this, piece);
}
public void paintComponent (Graphics graphics)
{
Graphics2D realGraphics = (Graphics2D) graphics;
// the image container might have been stretched.
// calculate the largest square held by the current container,
// and then 1/2 of that size for an individual square.
int wholeWidth = this.getWidth();
int wholeHeight = this.getHeight();
wholeSide = (wholeWidth / 2) * 2;
if (wholeHeight < wholeWidth) { wholeSide = (wholeHeight / 2) * 2; }
side = wholeSide / 2;
Rectangle clip = realGraphics.getClipBounds();
boolean firstColumnWhite = false;
// for each file on the board:
// set whether top square is white
// set background color according to white/black square
//
for (int fileIndex=0; fileIndex<8; fileIndex++)
{ boolean currentColorWhite = firstColumnWhite;
firstColumnWhite = !firstColumnWhite;
// draw the board and all the pieces
int rankIndex = 2;
for (rankIndex=2; rankIndex>=0; rankIndex--)
{
currentColorWhite = !currentColorWhite;
// x and y position of the top left corner of the square we're drawing,
// and rect becomes the dimensions and position of the square itself.
int xPos = fileIndex * side;
int yPos = rankIndex * side;
Rectangle rect = new Rectangle(xPos, yPos, side, side);
// if this square intersects the clipping rectangle we're drawing,
// then we'll draw the square and the piece on the square.
if (rect.intersects(clip))
{
// this puts down the correct color of square
if (currentColorWhite) { realGraphics.setColor(whiteSquareColor); }
else { realGraphics.setColor(blackSquareColor); }
realGraphics.fillRect(xPos, yPos, side, side);
// if there is a piece on this square and it isn't selected at the
// moment, then draw it.
Piece currentPiece = chessboard.getPiece(rankIndex, fileIndex);
if (currentPiece != null)
{
Image pieceImage = getImage(currentPiece);
int pieceHeight = pieceImage.getHeight(null);
double scalePiece = (double)side/(double)pieceHeight;
AffineTransform transform = new AffineTransform();
// transform.setToRotation(Math.toRadians(180));
transform.setToRotation(Math.toRadians(180), side/2, side/2);
transform.scale(scalePiece, scalePiece);
transform.translate(xPos/scalePiece, yPos/scalePiece);
// if (currentPiece.isBlack())
// {
// transform.translate(xPos + (side+2), yPos + (side+2));
// transform.rotate(Math.toRadians(180) /*, ,*/ );
// }
// else
// {
// transform.translate(xPos, yPos);
// }
realGraphics.drawImage(pieceImage, transform, this);
}
}
}
}
}
}
Piece.java
package main;
public class Piece
{
// piece types; the sum of the piece type and the
// color gives a number unique to both type and color,
// which is used for things like image indices.
public static final int PAWN = 0;
public static final int KNIGHT = 1;
public static final int BISHOP = 2;
public static final int ROOK = 3;
public static final int QUEEN = 4;
public static final int KING = 5;
// one of these is the color of the current piece
public static final int NOCOLOR = -1;
// the sum of the piece type and the
// color gives a number unique to both type and color,
// which is used for things like image indices.
public static final int BLACKCOLOR = 0;
public static final int WHITECOLOR = 6;
int color = NOCOLOR;
int imageIndex;
public Piece(int color, int pieceType)
{
// dbg -- all pieces are white rooks for now...
this.color = color;
imageIndex = color + pieceType;
}
/**
* return the integer associated with this piece's color;
*/
int getPieceColor()
{ return color;
}
/**
* return true if the piece is black
*/
public boolean isBlack()
{
return (color == BLACKCOLOR);
}
/**
* set the color associated with this piece; constants
* found in this class.
*/
public void setPieceColor(int givenColor)
{ color = givenColor;
}
/**
* return the integer designated for the image used for this piece.
*/
int getImageIndex()
{ return imageIndex;
}
}
and PieceImages.java
package main;
import java.awt.Component;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.net.URL;
public class PieceImages
{ static Image images[] = null;
private static void dbg (String msg) { System.out.println(msg); }
public static Image getPieceImage (Component target, Piece piece)
{
if (images == null)
try
{
MediaTracker tracker = new MediaTracker(target);
images = new Image[12];
images[Piece.BLACKCOLOR + Piece.PAWN] = getImage(tracker, "bPawn.gif");
images[Piece.BLACKCOLOR + Piece.KNIGHT] = getImage(tracker, "bKnight.gif");
images[Piece.BLACKCOLOR + Piece.BISHOP] = getImage(tracker, "bBishop.gif");
images[Piece.BLACKCOLOR + Piece.ROOK] = getImage(tracker, "bRook.gif");
images[Piece.BLACKCOLOR + Piece.QUEEN] = getImage(tracker, "bQueen.gif");
images[Piece.BLACKCOLOR + Piece.KING] = getImage(tracker, "bKing.gif");
images[Piece.WHITECOLOR + Piece.PAWN] = getImage(tracker, "wPawn.gif");
images[Piece.WHITECOLOR + Piece.KNIGHT] = getImage(tracker, "wKnight.gif");
images[Piece.WHITECOLOR + Piece.BISHOP] = getImage(tracker, "wBishop.gif");
images[Piece.WHITECOLOR + Piece.ROOK] = getImage(tracker, "wRook.gif");
images[Piece.WHITECOLOR + Piece.QUEEN] = getImage(tracker, "wQueen.gif");
images[Piece.WHITECOLOR + Piece.KING] = getImage(tracker, "wKing.gif");
if (!tracker.waitForAll(10000))
{ System.out.println("ERROR: not all piece main.images loaded");
}
dbg("piece images loaded");
}
catch (Exception xcp)
{ System.out.println("Error loading images");
xcp.printStackTrace();
}
return images[piece.getImageIndex()];
}
private static Image getImage(MediaTracker tracker, String file)
{
URL url = PieceImages.class.getResource("images/" + file);
Image image = Toolkit.getDefaultToolkit().getImage(url);
tracker.addImage(image, 1);
return image;
}
}
Okay, this is a little slight of hand. The example code will only work for 90 degree increments (it was only designed this way), to do smaller increments you to use some trig to calculate the image width and height (there's a answer somewhere for that to ;))
public class ImagePane extends JPanel {
private BufferedImage masterImage;
private BufferedImage renderedImage;
public ImagePane(BufferedImage image) {
masterImage = image;
applyRotation(0);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(renderedImage.getWidth(), renderedImage.getHeight());
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
protected int getVirtualAngle(int angle) {
float fRotations = (float) angle / 360f;
int rotations = (int) (fRotations - (fRotations / 1000));
int virtual = angle - (rotations * 360);
if (virtual < 0) {
virtual = 360 + virtual;
}
return virtual;
}
public void applyRotation(int angle) {
// This will only work for angles of 90 degrees...
// Normalize the angle to make sure it's only between 0-360 degrees
int virtualAngle = getVirtualAngle(angle);
Dimension size = new Dimension(masterImage.getWidth(), masterImage.getHeight());
int masterWidth = masterImage.getWidth();
int masterHeight = masterImage.getHeight();
double x = 0; //masterWidth / 2.0;
double y = 0; //masterHeight / 2.0;
switch (virtualAngle) {
case 0:
break;
case 180:
break;
case 90:
case 270:
size = new Dimension(masterImage.getHeight(), masterImage.getWidth());
x = (masterHeight - masterWidth) / 2.0;
y = (masterWidth - masterHeight) / 2.0;
break;
}
renderedImage = new BufferedImage(size.width, size.height, masterImage.getTransparency());
Graphics2D g2d = renderedImage.createGraphics();
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.rotate(Math.toRadians(virtualAngle), masterWidth / 2.0, masterHeight / 2.0);
g2d.drawImage(masterImage, at, null);
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - renderedImage.getWidth()) / 2;
int y = (height - renderedImage.getHeight()) / 2;
g2d.drawImage(renderedImage, x, y, this);
}
}
Now, you could simply "flip" the image vertically, if that works better for you
public class FlipPane extends JPanel {
private BufferedImage masterImage;
private BufferedImage renderedImage;
public FlipPane(BufferedImage image) {
masterImage = image;
flipMaster();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(renderedImage.getWidth(), renderedImage.getHeight());
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
protected void flipMaster() {
renderedImage = new BufferedImage(masterImage.getWidth(), masterImage.getHeight(), masterImage.getTransparency());
Graphics2D g2d = renderedImage.createGraphics();
g2d.setTransform(AffineTransform.getScaleInstance(1, -1));
g2d.drawImage(masterImage, 0, -masterImage.getHeight(), this);
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - renderedImage.getWidth()) / 2;
int y = (height - renderedImage.getHeight()) / 2;
g2d.drawImage(renderedImage, x, y, this);
}
}
This basically results in:
Original | 180 degree rotation | Vertical inversion...
Now, if you change the flipMaster method to read:
g2d.setTransform(AffineTransform.getScaleInstance(-1, -1));
g2d.drawImage(masterImage, -masterImage.getWidth(), -masterImage.getHeight(), this);
You'll get the same effect as the 180 rotation ;)
Try performing the rotation before translating it into the correct position. Simply reorder the transformations so that first you scale, then you rotate (around the center point of the image), and then you translate:
transform.scale(scale, scale);
transform.rotate(Math.PI, pieceWidth / 2, pieceHeight /2);
transform.translation(xPos, yPos);
By the way, the black pieces on a chess board usually aren't rotated. :)
Update
In what way does it not work? The solution I provided also also differs from your code in that scaling is performed before translating. You can try the rotating, translating, and then scaling.
I strongly suggest that you modify your code so that you can perform the translation last. If you do this, everything will become a lot less complicated. Once you have done so, you only have to scale once to automatically take care of the rotation.
transform.scale(scale, scale); // or transform.scale(scale, -scale); to rotate
transform.translate(xPos, yPos);

Categories

Resources