I'm having some problems whit drawing a sierpinski carpet, and would apreciate any help.
I was able to define the stoping condition, draw the central rectangle, and recursively, draw the next level of the image, all while keeping count.
It just so happens that I can only draw on the top left side. I'd say I'm confusing variables, but I can't seem to figure it out. Would apreciate any help
This is the part of the code where i'm having problems.
int smallerWidth = newWidth / 3;
int smallerHeight = newHeight / 3;
int sX = 0;
int sY = 0;
if (currentDeep > 1) {
for (int i = 0; i < 3; i++) {
sX = width / 9 + (i * 3 * (width / 9));
sY = height / 9;
g.fillRect(sX, sY, smallerWidth, smallerHeight);
for (int j = 0; j < 3; j++) {
sY = height / 9 + (j * 3 * (height / 9));
g.fillRect(sX, sY, smallerWidth, smallerHeight);
}
}
return 1 + printSquares(g, sX, sY, newWidth, newHeight, currentDeep
- 1);
} else
return 1;
}
This is the full code
https://pastebin.com/WPJ5tG8w
In sum my question is. What should I change/create in order for my program to draw the remaining 7 squares?
The issue with your code is, that you are trying to perform actions for multiple layers of the recursion at once. Normally, in the recursion, you would only paint the Quadrado central, calculate the sizes and coordinates of the smaller rectangles, and call the method recursively. That way you ensure that the recursive calls do not influence the stuff that is already there.
private int printSquares(Graphics g, int xi, int yi, int width, int height, int currentDeep) {
//Quadrado central
int newWidth = width / 3;
int newHeight = height / 3;
int x = (width / 3) + xi;
int y = (height / 3) + yi;
g.fillRect(x, y, newWidth, newHeight);
int sX = 0;
int sY = 0;
if (currentDeep > 1) {
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//This is the position of each of the small rectangles
sX = i * (width / 3) + xi;
sY = j * (height / 3) + yi;
// Call the method recursively in order to draw the smaller rectangles
sum += printSquares(g, sX, sY, newWidth, newHeight, currentDeep - 1);
}
}
return 1 + sum;
} else
return 1;
}
I hope, this resolves you issue.
I hope this is ok. This is amazing code. I took the liberty to complete this with the original code provided in the question and added the code that fixed it that Illedhar recommended as a added method. Here it is. Thank you for sharing this.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class sierpinskicarpet {
public static Color BACKGROUNDCOLOR = new Color(0, 0, 150);
public static Color FOREGROUNDCOLOR = new Color(255, 180, 0);
// Padrao = 5, alterado
public static int DEEP = 10;
/**
* Build the frame and shows it
*/
public sierpinskicarpet(int deep) {
// the frame and title
JFrame frame = new JFrame();
frame.setTitle("...: Recursive Squares with deep " + deep + " :...");
// Dispose frame on click on close button
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// set size and center frame on screen
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
// add print area occupying all the frame content area
frame.add(new PrintArea(deep));
// put frame visible
frame.setVisible(true);
}
/**
* Main method
*/
public static void main(String[] args)
{
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
// launch for 1 to DEEP squares frames
for (int i = DEEP; i >= 1; --i) {
// build a new object each time: objects will run
// independently
new sierpinskicarpet(i);
}
}
});
}
}
/**
* Our print area is, in fact, a label extended with the paint squares behavior
*/
class PrintArea extends JLabel {
private static final long serialVersionUID = 1L;
// local deep variable, will keep the registered deep for this the print
// area
int deep;
/**
* constructor
*/
public PrintArea(int deep) {
// call super, that is JLabel, constructor
super();
// set background color and set as well opaque to allow the background
// to be visible
setBackground(sierpinskicarpet.BACKGROUNDCOLOR);
setOpaque(true);
// save the deep
this.deep = deep;
}
/**
* paint method, called by JVM, when it is needed to update the PrintArea
*/
public void paint(Graphics g) {
// call paint from the JLABEL, draws the background of the PrintArea
super.paint(g);
// set drawing color
g.setColor(sierpinskicarpet.FOREGROUNDCOLOR);
// call the amazing print square method
int n = printSquares(g, 0, 0, getWidth(), getHeight(), this.deep);
// put to the world how much squares we printed
System.out.println("Deep = " + deep + ", squares painted: " + n);
}
/**
* Auxiliary method that will to the work. It must print a square with 1/3
* of the length of the frame and at the center and if not the bottom level
* ask to do the same for each of the other 8 square with 1/3 of length but
* called with the new deep
*/
private int printSquares(Graphics g, int xi, int yi, int width, int height, int currentDeep) {
//Quadrado central
int newWidth = width / 3;
int newHeight = height / 3;
int x = (width / 3) + xi;
int y = (height / 3) + yi;
g.fillRect(x, y, newWidth, newHeight);
int sX = 0;
int sY = 0;
if (currentDeep > 1) {
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
//This is the position of each of the small rectangles
sX = i * (width / 3) + xi;
sY = j * (height / 3) + yi;
// Call the method recursively in order to draw the smaller rectangles
sum += printSquares(g, sX, sY, newWidth, newHeight, currentDeep - 1);
}
}
return 1 + sum;
} else
return 1;
}
}
/*
Works Cited:
Recursive changing variables - sierpinski carpet. Stack Overflow. Retrieved May 4, 2022,
from https://stackoverflow.com/questions/49945862/recursive-changing-variables-sierpinski-carpet
*/
Related
My partner and I are trying to remake Tetris for our final project of the year in my Computer Science class we currently have a for loop that draws individual rectangles in an overwritten paint method.
private final int spacer = 30;
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
for(int i = getHeight()/2 - (spacer * 10); i < getHeight()/2 + (spacer * 10); i += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += (spacer)) {
g.drawRect(x, i, (spacer), (spacer));
}
}
setForeground(Color.black);
}
The method basically takes the width and height of the window and makes a 10 x 20 grid of boxes that are 30 units, pixels I think, wide.
We'd like to make a Grid.java class that takes in color, the spacer int, and an x and y int. The constructor for Grid.java should draw the exact same thing as the code above using the for loop, but when we tried it gave us a white screen that would not resize with the window.
private final int spacer = 30;
private static Grid[][] arr = new Grid[10][20];
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
int countY = 0;
int countX = 0;
for(int y = getHeight()/2 - (spacer * 10); y < getHeight()/2 + (spacer * 10); y += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += spacer) {
arr[countX][countY] = new Grid(x, y, spacer, g);
countX++;
}
countY++;
}
setForeground(Color.black);
}
*Grid.java Class*
package Tetris_Shapes;
import javax.swing.*;
import java.awt.*;
public class Grid {
private int x;
private int y;
private int side;
private Graphics g;
public Grid(int x, int y, int side, Graphics g) {
// g.drawRect(x, y, spacer, spacer);
this.x = x;
this.y = y;
this.side = side;
this.g = g;
paint(this.g);
}
private void paint(Graphics g) {
g.drawRect(x, y, side, side);
}
}
When we try and run this we get the white box that doesn't resize. My question is does anyone know of a way to get a constructor to draw shapes. Thank you in advance, this is pretty niche so I'm also going to apologize in advance.
So I found Conway's Game of Life recently, so I'm naturally addicted. It hasn't taken me long to find out that I am very limited by my computer's CPU. I've also found that, for whatever reason, I cannot add a JPanel with many JComponents to a JFrame.
So I have a loop that adds 86,400 JLabels to a JPanel, which happens in ~1 second, but adding this JPanel to a JFrame takes ~2 minutes.
I understand I could use java.awt.Graphics, but I'd prefer to use JLabels because they automatically resize.
So my question: Why does this take so long to add the JPanel to the JFrame, and how do I fix it?
Using java.awt.Graphics, I was able to remove this long period of lag:
public void render(int[][] cells) {
int cellHeight = image.getHeight() / cells.length;
int cellWidth = image.getWidth() / cells[0].length;
for (int y = 0; y < cells.length; y++) {
for (int x = 0; x < cells[y].length; x++) {
int col = colors[cells[y][x]].getRGB();
fillSquare(x * (cellWidth), y * (cellHeight), cellWidth, cellHeight, col);
}
}
}
// Could pass a java.awt.Rectangle here
private void fillSquare(int xPos, int yPos, int width, int height, int col) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixels[(x + xPos) + (y + yPos) * image.getWidth()] = col;
}
}
}
#Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
For this method, it's important to keep the JFrame's size proportional to the number of cells, this way there is no unused space within the JFrame.
Is there any way to bend a BufferedImage in Java?
I thought that if I crop the image into smaller pieces and rotate them then I would essentially bend the image, but it doesn't seem to work.
Here is the method I created:
/**
* This is a recursive method that will accept an image the point where the bending will start and the point where the bending will end, as well as the angle of bending
*
* #param original:the original image
* #param startingPoint: the point where the bending should start
* #param endingPoint: the point where the bending should end
* #param radiands: the angle
* #return the bent image
*/
public static BufferedImage getBentImage(BufferedImage original, int startingPoint, int endingPoint, double radians) {
if (startingPoint >= endingPoint)
return original;
int type = BufferedImage.TYPE_INT_ARGB;
int width = original.getWidth();
int height = original.getHeight();
BufferedImage crop = original.getSubimage(0, 0, startingPoint, height);
BufferedImage crop0 = original.getSubimage(startingPoint, 0, width - startingPoint, height);
BufferedImage bendCrop = new BufferedImage(width, height, type);
BufferedImage image = new BufferedImage(width, height, type);
AffineTransform rotation = new AffineTransform();
rotation.translate(0, 0);
rotation.rotate(radians);
Graphics2D g = bendCrop.createGraphics();
g.drawImage(crop0, rotation, null);
g.dispose();
g = image.createGraphics();
g.drawImage(crop, 0, 0, null);
g.drawImage(bendCrop, startingPoint, 0, null);
g.dispose();
return getBentImage(image, startingPoint + 1, endingPoint, radians);
}
This is the original Image:
And this is the result of this getBentImage(image, 200, 220, Math.toRadians(1)):
I was expecting something closer to:
Any ideas on how to actually implement a getBentImage() method?
As suggested in the comments, a simple approach is to divide the image into 3 parts:
Identical to the original.
Bent according to the bending transformation.
Constant diagonal continuation.
Here is a quick and a bit messy example that shows the original shape and the resulting shape below it. I just used a label icon for the images instead of doing custom painting. (Also I didn't adhere to the Java naming conventions with final variables because it's math and not typical coding.)
Since there are quite a few variables in the calculation code, I added a sketch at the end that shows what the variables represent.
public class Main extends JFrame {
static BufferedImage image;
public static void main(String[] args) {
try {
image = ImageIO.read(ClassLoader.getSystemResource("img.png"));
} catch (IOException e) {
e.printStackTrace();
}
new Main();
}
public Main() {
getContentPane().setLayout(new BorderLayout(5, 10));
BufferedImage img2 = transform(15, 100, 300);
JLabel label1 = new JLabel(new ImageIcon(image));
label1.setHorizontalAlignment(JLabel.LEFT);
label1.setOpaque(true);
label1.setBackground(Color.YELLOW);
add(label1, BorderLayout.NORTH);
JLabel label2 = new JLabel(new ImageIcon(img2));
label2.setHorizontalAlignment(JLabel.LEFT);
label2.setOpaque(true);
label2.setBackground(Color.CYAN);
add(label2);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
static BufferedImage transform(int t, int x1, int x2) {
final double TH = Math.toRadians(t);
final int D = x2 - x1;
final int W = image.getWidth();
final int H = image.getHeight();
final int dD = (int) (D / (2 * TH) * Math.sin(2 * TH));
final int dH = (int) (D / TH * Math.pow(Math.sin(TH), 2));
final int pH = (int) ((W - x2) * Math.tan(2 * TH));
final int width = W - (D - dD);
final int height = (int) (H + dH + pH);
System.out.println(W + " " + H + " -> " + width + " " + height);
BufferedImage img2 = new BufferedImage(width, height, image.getType());
for (int x = 0; x < x1; x++) {
for (int y = 0; y < H; y++) {
int rgb = image.getRGB(x, y);
img2.setRGB(x, y, rgb);
}
}
for (int x = x1; x < x2; x++) {
for (int y = 0; y < H; y++) {
int rgb = image.getRGB(x, y);
int dx = (int) (D / (2 * TH) * Math.sin(2 * (x-x1) * TH / D));
int dy = (int) (D / TH * Math.pow(Math.sin((x-x1) * TH / D), 2));
img2.setRGB(x1 + dx, y + dy, rgb);
}
}
for (int x = x2; x < W; x++) {
for (int y = 0; y < H; y++) {
int rgb = image.getRGB(x, y);
int dp = (int) ((x - x2) * Math.tan(2 * TH));
img2.setRGB(x - (D - dD), y + dH + dp, rgb);
}
}
return img2;
}
}
As for the calculations, I'll leave it for you as homework; it's just geometry/trigonometry which belongs on Math.SE more than on SO. If you can't figure it out I'll give you a direction.
Note that this method might not be fast at all and could certainly be optimized, I'll leave that to you also. Oh, and rounding doubles to ints carelessly, so the result is not pixel-perfect.
I dont know what you mean by bending but essentially you have a rectangle and you break one piece of it and rotate it:
so the algorithm is as follows:
rotate line(x, 0, width-1, 0)
rotate line(x, height-1, width-1, height-1)
connect the pieces
So essentially you are looking for rotate line.
(Sorry for the long post... at least it has pictures?)
I have written an algorithm that creates a mosaic from an image by statistically generating N convex polygons that cover the image with no overlap. These polygons have anywhere between 3-8 sides, and each side has an angle that is a multiple of 45 degrees. These polygons are stored internally as a rectangle with displacements for each corner. Below is an image that explains how this works:
getRight() returns x + width - 1, and getBottom() returns y + height - 1. The class is designed to maintain a tight bounding box around filled pixels so the coordinates shown in this image are correct. Note that width >= ul + ur + 1, width >= ll + lr + 1, height >= ul + ll + 1, and height >= ur + ul + 1, or there would be empty pixels on a side. Note also that it is possible for a corner's displacement to be 0, thus indicating all pixels are filled in that corner. This enables this representation to store 3-8 sided convex polygons, each of whose sides are at least one pixel in length.
While it's nice to mathematically represent these regions, I want to draw them so I can see them. Using a simple lambda and a method that iterates over each pixel in the polygon, I can render the image perfectly. As an example, below is Claude Monet's Woman with a Parasol using 99 polygons allowing all split directions.
The code that renders this image looks like this:
public void drawOnto(Graphics graphics) {
graphics.setColor(getColor());
forEach(
(i, j) -> {
graphics.fillRect(x + i, y + j, 1, 1);
}
);
}
private void forEach(PerPixel algorithm) {
for (int j = 0; j < height; ++j) {
int nj = height - 1 - j;
int minX;
if (j < ul) {
minX = ul - j;
} else if (nj < ll) {
minX = ll - nj;
} else {
minX = 0;
}
int maxX = width;
if (j < ur) {
maxX -= ur - j;
} else if (nj < lr) {
maxX -= lr - nj;
}
for (int i = minX; i < maxX; ++i) {
algorithm.perform(i, j);
}
}
}
However, this is not ideal for many reasons. First, the concept of graphically representing a polygon is now part of the class itself; it is better to allow other classes whose focus is to represent these polygons. Second, this entails many, many calls to fillRect() to draw a single pixel. Finally, I want to be able to develop other methods of rendering these polygons than drawing them as-is (for example, performing weighted interpolation over the Voronoi tessellation represented by the polygons' centers).
All of these point to generating a java.awt.Polygon that represents the vertices of the polygon (which I named Region to differentiate from the Polygon class). No problem; I wrote a method to generate a Polygon that has the corners above with no duplicates to handle the cases that a displacement is 0 or that a side has only one pixel on it:
public Polygon getPolygon() {
int[] xes = {
x + ul,
getRight() - ur,
getRight(),
getRight(),
getRight() - lr,
x + ll,
x,
x
};
int[] yes = {
y,
y,
y + ur,
getBottom() - lr,
getBottom(),
getBottom(),
getBottom() - ll,
y + ul
};
int[] keptXes = new int[8];
int[] keptYes = new int[8];
int length = 0;
for (int i = 0; i < 8; ++i) {
if (
length == 0 ||
keptXes[length - 1] != xes[i] ||
keptYes[length - 1] != yes[i]
) {
keptXes[length] = xes[i];
keptYes[length] = yes[i];
length++;
}
}
return new Polygon(keptXes, keptYes, length);
}
The problem is that, when I try to use such a Polygon with the Graphics.fillPolygon() method, it does not fill all of the pixels! Below is the same mosaic rendered with this different method:
So I have a few related questions about this behavior:
Why does the Polygon class not fill in all these pixels, even though the angles are simple multiples of 45 degrees?
How can I consistently code around this defect (as far as my application is concerned) in my renderers so that I can use my getPolygon() method as-is? I do not want to change the vertices it outputs because I need them to be precise for center-of-mass calculations.
MCE
If the above code snippets and pictures are not enough to help explain the problem, I have added a Minimal, Complete, and Verifiable Example that demonstrates the behavior I described above.
package com.sadakatsu.mce;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Main {
#FunctionalInterface
private static interface PerPixel {
void perform(int x, int y);
}
private static class Region {
private int height;
private int ll;
private int lr;
private int width;
private int ul;
private int ur;
private int x;
private int y;
public Region(
int x,
int y,
int width,
int height,
int ul,
int ur,
int ll,
int lr
) {
if (
width < 0 || width <= ll + lr || width <= ul + ur ||
height < 0 || height <= ul + ll || height <= ur + lr ||
ul < 0 ||
ur < 0 ||
ll < 0 ||
lr < 0
) {
throw new IllegalArgumentException();
}
this.height = height;
this.ll = ll;
this.lr = lr;
this.width = width;
this.ul = ul;
this.ur = ur;
this.x = x;
this.y = y;
}
public Color getColor() {
return Color.BLACK;
}
public int getBottom() {
return y + height - 1;
}
public int getRight() {
return x + width - 1;
}
public Polygon getPolygon() {
int[] xes = {
x + ul,
getRight() - ur,
getRight(),
getRight(),
getRight() - lr,
x + ll,
x,
x
};
int[] yes = {
y,
y,
y + ur,
getBottom() - lr,
getBottom(),
getBottom(),
getBottom() - ll,
y + ul
};
int[] keptXes = new int[8];
int[] keptYes = new int[8];
int length = 0;
for (int i = 0; i < 8; ++i) {
if (
length == 0 ||
keptXes[length - 1] != xes[i] ||
keptYes[length - 1] != yes[i]
) {
keptXes[length] = xes[i];
keptYes[length] = yes[i];
length++;
}
}
return new Polygon(keptXes, keptYes, length);
}
public void drawOnto(Graphics graphics) {
graphics.setColor(getColor());
forEach(
(i, j) -> {
graphics.fillRect(x + i, y + j, 1, 1);
}
);
}
private void forEach(PerPixel algorithm) {
for (int j = 0; j < height; ++j) {
int nj = height - 1 - j;
int minX;
if (j < ul) {
minX = ul - j;
} else if (nj < ll) {
minX = ll - nj;
} else {
minX = 0;
}
int maxX = width;
if (j < ur) {
maxX -= ur - j;
} else if (nj < lr) {
maxX -= lr - nj;
}
for (int i = minX; i < maxX; ++i) {
algorithm.perform(i, j);
}
}
}
}
public static void main(String[] args) throws IOException {
int width = 10;
int height = 8;
Region region = new Region(0, 0, 10, 8, 2, 3, 4, 1);
BufferedImage image = new BufferedImage(
width,
height,
BufferedImage.TYPE_3BYTE_BGR
);
Graphics graphics = image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
region.drawOnto(graphics);
ImageIO.write(image, "PNG", new File("expected.png"));
image = new BufferedImage(
width,
height,
BufferedImage.TYPE_3BYTE_BGR
);
graphics = image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
graphics.setColor(Color.BLACK);
graphics.fillPolygon(region.getPolygon());
ImageIO.write(image, "PNG", new File("got.png"));
}
}
I spent all day working on it, and I seem to have a fix for this. The clue was found in the documentation for the Shape class, which reads:
Definition of insideness: A point is considered to lie inside a Shape if and only if:
it lies completely inside theShape boundary or
it lies exactly on the Shape boundary and the space immediately adjacent to the point in the increasing X direction is entirely inside the boundary or
it lies exactly on a horizontal boundary segment and the space immediately adjacent to the point in the increasing Y direction is inside the boundary.
Actually, this text is a bit misleading; the third case overrides second (i.e., even if a pixel in a horizontal boundary segment on the bottom of a Shape has a filled point to its right, it still will not be filled). Represented pictorially, the Polygon below will not draw the x'ed out pixels:
The red, green, and blue pixels are part of the Polygon; the rest are not. The blue pixels fall under the first case, the green pixels fall under the second case, and the red pixels fall under the third case. Note that all of the rightmost and lowest pixels along the convex hull are NOT drawn. To get them to be drawn, you have to move the vertices to the orange pixels as shown to make a new rightmost/bottom-most portion of the convex hull.
The easiest way to do this is to use camickr's method: use both fillPolygon() and drawPolygon(). At least in the case of my 45-degree-multiple-edged convex hulls, drawPolygon() draws the lines to the vertices exactly (and probably for other cases as well), and thus will fill the pixels that fillPolygon() misses. However, neither fillPolygon() nor drawPolygon() will draw a single-pixel Polygon, so one has to code a special case to handle that.
The actual solution I developed in trying to understand the insideness definition above was to create a different Polygon with the modified corners as shown in the picture. It has the benefit (?) of calling the drawing library only once and automatically handles the special case. It probably is not actually optimal, but here is the code I used for anyone's consideration:
package com.sadakatsu.mosaic.renderer;
import java.awt.Polygon;
import java.util.Arrays;
import com.sadakatsu.mosaic.Region;
public class RegionPolygon extends Polygon {
public RegionPolygon(Region region) {
int bottom = region.getBottom();
int ll = region.getLL();
int lr = region.getLR();
int right = region.getRight();
int ul = region.getUL();
int ur = region.getUR();
int x = region.getX();
int y = region.getY();
int[] xes = {
x + ul,
right - ur + 1,
right + 1,
right + 1,
right - lr,
x + ll + 1,
x,
x
};
int[] yes = {
y,
y,
y + ur,
bottom - lr,
bottom + 1,
bottom + 1,
bottom - ll,
y + ul
};
npoints = 0;
xpoints = new int[xes.length];
ypoints = new int[xes.length];
for (int i = 0; i < xes.length; ++i) {
if (
i == 0 ||
xpoints[npoints - 1] != xes[i] ||
ypoints[npoints - 1] != yes[i]
) {
addPoint(xes[i], yes[i]);
}
}
}
}
I'm working on a problem I'm having lots of trouble with. The concept of the question is to build a pyramid using bricks. The entire pyramid of bricks is centered in the window. I can draw one brick, then two, then three all the way up until 12 which makes up the base of the pyramid but all of the bricks are alined on the left edge on the left of the window instead of being centered in the window.
Using getWidth() and getHeight() i can do (getWidth()-BRICK_WIDTH) / 2; to get the center for the x coordinate of the brick. And then (getHeight() -BRICK_HEIGHT) / 2; for the center of the y coordinate of one brick. The only problem is i don't understand where to enter that code so it applies to all the bricks so each row of bricks is centered in the window.
import acm.program.*;
import acm.graphics.*;
public class Pyramid extends GraphicsProgram {
public void run() {
double xCoordinate = (getWidth() - BRICKWIDTH) / 2;
double yCoordinate = (getHeight() - BRICK_HEIGHT / 2);
for (int i = 0; i < BRICKS_IN_BASE; i++) {
for (int j = 0; j < i; j++) {
double x = j * BRICK_WIDTH;
double y = i * BRICK_HEIGHT;
GRect square = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
add(square);
}
}
}
private static final int BRICK_WIDTH = 50;
private static final int BRICK_HEIGHT = 25;
private static final int BRICKS_IN_BASE = 12;
}
You should try something like this :
import acm.program.*;
import acm.graphics.*;
public class Pyramid extends GraphicsProgram
{
public void run()
{
// We calculate some values in order to center the pyramid vertically
int pyramidHeight = BRICKS_IN_BASE * BRICK_HEIGHT;
double pyramidY = (getHeight() - pyramidHeight) / 2;
// For each brick layer...
for (int i=BRICKS_IN_BASE ; i >= 1; i--)
{
// We calculate some values in order to center the layer horizontally
int layerWidth = BRICKWIDTH * i;
double layerX = (getWidth() - layerWidth) / 2;
double layerY = pyramidY + (i-1) * BRICK_HEIGHT;
// For each brick in the layer...
for(int j=0 ; j<i ; j++)
{
GRect square = new GRect(layerX + j*BRICK_WIDTH, layerY, BRICK_WIDTH, BRICK_HEIGHT);
add(square);
}
}
}
private static final int BRICK_WIDTH = 50;
private static final int BRICK_HEIGHT = 25;
private static final int BRICKS_IN_BASE = 12;
}
In this implementation, we first calculate the global width of the layer (because we already know how many bricks there will be in it) and we use it to find a global "point of start" of the layer, from which we find all the coordinates for all the rectangles.
You mean something like that?
double x = xCoordinate + j * BRICK_WIDTH;
double y = yCoordinate + i * BRICK_HEIGHT;