I noticed that one of my methods is being very slow, with profiling I noticed it took up like 95% of the total execution time.
The class:
package gui;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
/**
*
* #author Frank
*/
public abstract class CustomRectangle {
protected final BufferedImage bufferedImage;
protected final int width;
protected final int height;
protected final int xOffset;
protected final int yOffset;
protected final int borderSize;
protected final boolean defaultOrientation;
protected Color color;
public CustomRectangle(final BufferedImage bufferedImage, final int width, final int height, final int xOffset, final int yOffset, final int borderSize, final boolean defaultOrientation) {
this.bufferedImage = bufferedImage;
this.width = width;
this.height = height;
if (defaultOrientation) {
this.xOffset = xOffset;
this.yOffset = yOffset;
}
else {
this.xOffset = bufferedImage.getWidth() - 1 - xOffset;
this.yOffset = bufferedImage.getHeight() - 1 - yOffset;
}
this.borderSize = borderSize;
this.defaultOrientation = defaultOrientation;
}
abstract public void inBorder(final int dx, final int dy);
abstract public void outBorder(final int dx, final int dy);
public void draw() {
if (defaultOrientation) {
drawDefaultOrientation();
}
else {
drawOppositeOrientation();
}
}
private void drawDefaultOrientation() {
int[] pixelArray = ((DataBufferInt)bufferedImage.getRaster().getDataBuffer()).getData();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int dx = Math.min(x, width - 1 - x);
int dy = Math.min(y, height - 1 - y);
if (dx < borderSize || dy < borderSize) {
inBorder(dx, dy);
}
else {
outBorder(dx, dy);
}
pixelArray[(xOffset + x) + ((yOffset + y) * bufferedImage.getWidth())] = color.getRGB();
}
}
}
private void drawOppositeOrientation() {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int dx = Math.min(x, width - 1 - x);
int dy = Math.min(y, height - 1 - y);
if (dx < borderSize || dy < borderSize) {
inBorder(dx, dy);
}
else {
outBorder(dx, dy);
}
bufferedImage.setRGB(xOffset - x, yOffset - y, color.getRGB());
}
}
}
public void setColor(final Color color) {
this.color = color;
}
}
The slow method is the drawDefaultOrientation() method.
The scary part however is that even if I leave out all image-modifying code, then it is still slow.
The callee code:
new CustomRectangle(bufferedImage, 440, 180, 30, 490, 10, defaultOrientation) {
#Override
public void inBorder(final int dx, final int dy) {
setColor(new Color(red, green, blue, 255 - (int)Math.round(0.5 * Math.min(dx, dy))));
}
#Override
public void outBorder(final int dx, final int dy) {
setColor(new Color(red, green, blue, 128 - (int)Math.round(0.5 * Math.min(dx, dy))));
}
}.draw();
There must be something going on as in any way this code should not take this long, even if it loops over x and y coordinates.
Some thought I had was that it could have to do with the Anonymous inner class + Override...
But hopefully someone with more knowledge can answer the question.
Try this: If red, green, blue don't change, then stop using an anonymous class, and in your implemented subclass do: (note, this might not work, but the basic idea should, if you can avoid constantly calling new Color by caching all the colors you might need)
private final Color[] colors;
public ImplementedCustomRectangle(...) {
super(...); // ... is not real code, I just don't want to copy/paste
colors = new Color[256];
for (i = 0; i < 256; i++) {
colors[i] = new Color(red, green, blue, i);
}
}
#Override
public void inBorder(final int dx, final int dy) {
int value = 255 - (int)Math.round(0.5 * Math.min(dx, dy));
setColor(colors[value]);
}
#Override
public void outBorder(final int dx, final int dy) {
int value = 128 - (int)Math.round(0.5 * Math.min(dx, dy);
setColor(colors[value]);
}
Related
I asked this question on Math.se a few days ago, and got the following answer in pseudocode:
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let radians = 2.0 * 3.14159265358979323846 * Random()
Let deviation = (radius - newRadius) * Sqrt(Random())
Let newX = centerX + deviation * Cos(radians)
Let newY = centerY + deviation * Sin(radians)
Return (newX, newY, newRadius)
End Function
I changed the pseudocode to Java and added my own changes to fit my needs. The new code looks like this:
Circle createNewCircle(int centerX, int centerY, int radius, int newR, Color newColor) {
int newRadius = radius * Random();
double radians = 2.0 * 3.141592653589793 * Random();
double deviation = (radius - newRadius) * Math.sqrt(Random());
System.out.println(radius + " - " + newRadius + " * sqrt(0 or 1) = " + (radius-newRadius) + " * (0 or 1) = " + deviation);
double newX = centerX + deviation * Math.cos(radians);
System.out.println(centerX + " + " + deviation + " * cos(" + radians + ") = " + (centerX + deviation) + " * " + Math.cos(radians));
double newY = centerY + deviation * Math.sin(radians);
int newCirX = (int) newX;
int newCirY = (int) newY;
Circle newCir = new Circle(newCirX, newCirY, newR*2, newR*2, newR, newColor, true);
return newCir;
}
The code itself is supposed to create a new Circle inside of a preexisting one. I created a circle class that looks like this:
import java.awt.Color;
import java.awt.Graphics;
public class Circle {
public int X, Y, Width, Height, radius;
public Color color;
public boolean toFill;
public Circle(int x, int y, int width, int height, int radius, Color color, boolean fill) {
X = x;
Y = y;
Width = width;
Height = height;
this.radius = radius;
this.color = color;
toFill = fill;
}
public void render(Graphics g) {
g.setColor(color);
for(int i=-5; i<5; i++) {
if(toFill) {
g.fillOval(X+i, Y+i, Width-i, Height-i);
} else {
g.drawOval(X+i, Y+i, Width-i, Height-i);
}
}
}
public boolean contains(Circle pBound) {
int pBoundCenterX = pBound.X+pBound.radius;
int cirCenterX = X+radius;
int diffBetweenCentersX = Math.abs(pBoundCenterX-cirCenterX);
int pBoundCenterY = pBound.Y+pBound.radius;
int cirCenterY = Y+radius;
int diffBetweenCentersY = Math.abs(pBoundCenterY-cirCenterY);
if(diffBetweenCentersX<= (pBound.radius+radius) && diffBetweenCentersX>=Math.abs(pBound.radius-radius)) { // X
if(diffBetweenCentersY>=Math.abs(pBound.radius-radius)) { // Y
return true;
}
}
return false;
}
public int getX() {
return X;
}
public int getWidth() {
return Width;
}
public int getRadius() {
return radius;
}
public void setWidth(int width) {
Width = width;
}
public int getHeight() {
return Height;
}
public void setHeight(int height) {
Height = height;
}
public void setX(int x) {
X = x;
}
public int getY() {
return Y;
}
public void setY(int y) {
Y = y;
}
}
My way of creating the new circle is this:
if(secInGame==timesForCircle[X] && !hasChanged) { // circle 2
Circle cir1 = cir;
cir = createNewCircle(cir1.X+(cir1.Width/2), cir1.Y+(cir1.Height/2), cir1.getRadius(), 135, Color.cyan);
hasChanged = true;
circleOn++;
circ++;
}
Where cir1 is the preexisting Circle and cir is the new circle.
Is there anything I didn't code correctly? I've tried a few different variations, but they all give the same result.
Before I implemented the pseudocode, my circles looked like this:
but now it looks like this:
All of my code can be found on github at: link
I think there are several issues in your code.
1. First of all it is not clear why your Circle has radius, Width and Height. For a circle all 3 things should be the same. Also your render in case toFill is true looks strange. Here is a simplified version (note: I didn't compile it so there might be some bugs):
public class Circle {
public int X, Y, radius;
public Color color;
public boolean toFill;
public Circle(int x, int y, int radius, Color color, boolean fill) {
X = x;
Y = y;
this.radius = radius;
this.color = color;
toFill = fill;
}
public void render(Graphics g) {
g.setColor(color);
final int r2 = 2*radius;
if(toFill) {
g.fillOval(X, Y, r2, r2);
}
else {
for(int i=-5; i<5; i++) {
g.drawOval(X+i, Y+i, r2-i, r2-i);
}
}
}
public boolean contains(Circle pBound) {
int pBoundCenterX = pBound.X+pBound.radius;
int cirCenterX = X+radius;
int diffBetweenCentersX = Math.abs(pBoundCenterX-cirCenterX);
int pBoundCenterY = pBound.Y+pBound.radius;
int cirCenterY = Y+radius;
int diffBetweenCentersY = Math.abs(pBoundCenterY-cirCenterY);
if(diffBetweenCentersX<= (pBound.radius+radius) && diffBetweenCentersX>=Math.abs(pBound.radius-radius)) { // X
if(diffBetweenCentersY>=Math.abs(pBound.radius-radius)) { // Y
return true;
}
}
return false;
}
public int getX() {
return X;
}
public int getRadius() {
return radius;
}
public void setX(int x) {
X = x;
}
public int getY() {
return Y;
}
public void setY(int y) {
Y = y;
}
}
I didn't check your code, but I'd consider as a good practice:
renaming x and y into leftX and topY to avoid confusion with centerX/centerY meaning. Or change meaning to more typical center one.
declaring all your fields as private (see encapsulation);
declaring all your fields as final and remove all the setXyz methods (see immutability)
2. I don't understand why your createNewCircle has newR parameter and at the same time you generate a random newRadius in the first line. One of these definitely should be removed. Given that the parameter is always a constant 135 I think it should be removed.
3. Now I believe the main bug in your translation is in the lines
int newCirX = (int) newX;
int newCirY = (int) newY;
It probably should be something like
int newCirX = (int) newX - newRadius;
int newCirY = (int) newY - newRadius;
It looks like you messed with center vs top-left. Actually I think the fact that you made such a bug is an argument that supports renaming x and y I suggested in item #1.
So I'm working on a paint program in school and now I have to double buffer it. After a week of searching and using tutorials online I am still unable to double buffer my program properly... I have written a double buffer function but then you can't paint anything... When i disable the double buffer you can paint fine but the screen flickers. Please help
(Yes I am aware that some of my code is deprecated and I should fix that)
Code:
import java.applet.*;
import java.awt.*;
public class PaintRun extends Applet
{
static int posx = 0;
static int posy = 0;
static int posxOld = 0;
static int posyOld = 0;
static int size = 20;
static int toolNum = 0;
static boolean ready = false;
static Color col = new Color(120,120,120);
static Button clear = new Button(0,0,20,'C');
static Button sizeInc = new Button(20,0,20,'+');
static Button sizeDec = new Button(40,0,20,'-');
static Tools tool = new Tools(posx,posy);
public void paint(Graphics g)
{
drawButtons(g);
detectButtons(g);
}
private static void detectButtons(Graphics g)
{
if (clear.detect(posx,posy) == true)
{
g.setColor(Color.WHITE);
g.fillRect(0, 0, 800000, 800000);
drawButtons(g);
}
else if (sizeInc.detect(posx, posy))
{
size+=2;
drawButtons(g);
}
else if (sizeDec.detect(posx, posy))
{
size-=2;
if (size <= 2)
size = 2;
drawButtons(g);
}
else
{
if (ready == true)
{
g.setColor(Color.BLACK);
g.drawLine(posxOld, posyOld, posx, posy);
drawButtons(g);
}
}
}
private static void drawButtons(Graphics g)
{
clear.setColor(col, Color.BLACK);
sizeInc.setColor(col, Color.BLACK);
sizeDec.setColor(col, Color.BLACK);
clear.drawButton(g);
sizeInc.drawButton(g);
sizeDec.drawButton(g);
}
public boolean mouseDown(Event e, int x, int y)
{
posx = x;
posy = y;
posxOld = x;
posyOld = y;
ready = true;
repaint();
return true;
}
public boolean mouseDrag(Event e, int x, int y)
{
posxOld = posx;
posyOld = posy;
posx = x;
posy = y;
repaint();
return true;
}
// Normal Update function
public void update(Graphics g)
{
paint(g);
}
// Double buffered Update function
/*public void update(Graphics g)
{
Graphics offgc;
Image offscreen = null;
Dimension d = size();
offscreen = createImage(d.width, d.height);
offgc = offscreen.getGraphics();
offgc.setColor(getBackground());
offgc.fillRect(0, 0, d.width, d.height);
offgc.setColor(getForeground());
paint(offgc);
g.drawImage(offscreen, 0, 0, this);
}
*/
}
class Button extends Applet
{
int minX; // X coord where the button is drawn
int minY; // Y coord where the button is drawn
int maxX; // X size
int maxY; // Y size
char letter;
Color back;
Color line;
// Constructor block
public Button(char desc)
{
minX = 0;
minY = 0;
maxX = 40;
maxY = 40;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
public Button(int x, int y, char desc)
{
minX = x;
minY = y;
maxX = 40;
maxY = 40;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
public Button(int x, int y, int size, char desc)
{
minX = x;
minY = y;
maxX = size;
maxY = size;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
public Button(int x, int y, int sizeX, int sizeY, char desc)
{
minX = x;
minY = y;
maxX = sizeX;
maxY = sizeY;
back = new Color(255,255,255);
line = new Color(0,0,0);
letter = desc;
}
// Draws the button
public void drawButton(Graphics g)
{
g.setColor(back);
g.fillRect(minX, minY, maxX, maxY);
g.setColor(line);
g.drawRect(minX, minY, maxX, maxY);
g.drawString(Character.toString(letter), (minX+maxX/2)-4, (minY+maxY/2)+4);
}
// Sets the button colors
public void setColor(Color b, Color l)
{
back = b;
line = l;
}
// Detects if those coordinates are in the button location or not
public boolean detect(int x, int y)
{
boolean flag = false;
if (x > minX && x < (minX + maxX) && y > minY && y < (minY + maxY))
flag = true;
else
flag = false;
return flag;
}
}
I'm working on a Java Game using LWJGL. I made a particle system and would like to obtain same result as found on this website: http://play.cubedpixels.de
This is what I got so far:
I tried my best and the problem is it's stopping and they are only moving in one direction. Also the lines are not like on this website. On the website it's smoother and better. They are using OpenGL too.
How to solve these problems and make my implementation better, more like it is one the website?
The code below shows two classes: ParticleGalaxy and Particle. Latter one is inner class of the former, but for clarity I've split them into separate snippets.
ParticleGalaxy:
public class ParticleGalaxy
{
private int count;
private int width;
private int height;
private int mousex;
private int mousey;
public ArrayList<Particle> particles = new ArrayList();
private Random random = new Random();
private TimerUtil timer = new TimerUtil();
int state = 0;
int a = 255;
int r = 255;
int g = 0;
int b = 0;
public ParticleGalaxy(int count, int width, int height)
{
this.count = count;
this.width = width;
this.height = height;
for(int i = 0; i < count; i++)
{
this.particles.add(
new Particle(this.random.nextInt(width),
this.random.nextInt(height)));
}
}
public void drawParticles(int mousex, int mousey)
{
this.mousex = mousex;
this.mousey = mousey;
for(Particle p : this.particles)
{
if(p.reset)
{
p.resetPosSize();
p.reset = false;
}
int x = Math.abs(p.getX() - mousex);
int y = Math.abs(p.getY() - mousey);
if((x < 40 && x > -40) && (y<35 && y>-40))
{
p.setConnect(true);
}
else
{
p.setConnect(false);
}
p.draw();
}
}
}
Particle:
public class Particle
{
private int x;
private int y;
private int k;
private int movey;
private int movex;
private int starty;
private int startx;
private int locationy;
private int locationx;
private float size;
private boolean reset;
private boolean connect;
private Random random = new Random();
private TimerUtil timer = new TimerUtil();
private boolean moving = false;
private boolean start = false;
public Particle(int x, int y)
{
this.x = x;
this.y = y;
this.startx = x;
this.starty = y;
this.size = genRandom(0.57F, 0.71F);
}
public void setConnect(boolean bool)
{
connect = bool;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
private void drawLine(int xto, int yto, int xfrom, int yfrom)
{
GL11.glLineWidth(1.2f);
GL11.glColor3f(1.0f, 1.0f, 1.0f);
GL11.glBegin(GL11.GL_LINE_STRIP);
GL11.glVertex2d(xto, yto);
GL11.glVertex2d(xfrom, yfrom);
GL11.glEnd();
}
public void draw()
{
if(!start)
{
start = true;
movex = height - random.nextInt(height);
movey = width - random.nextInt(width);
move(startx, starty, movex, movey);
}
float speed = 1;
float elapsed = 0.01f;
// On starting movement
float distance = (float)Math.sqrt(Math.pow(movex - startx, 2) +
Math.pow(movey - starty, 2));
float directionX = (movex - startx) / distance;
float directionY = (movey - starty) / distance;
if(moving == true)
{
x += directionX * speed * elapsed;
y += directionY * speed * elapsed;
if(Math.sqrt(Math.pow(x - startx, 2) + Math.pow(y - starty, 2)) >= distance)
{
x = (int)movex;
y = (int)movey;
resetPosSize();
this.reset = true;
moving = false;
}
}
this.k += 1;
int xx = 0;
int yy = 0;
this.locationx = this.x + xx;
this.locationy = this.y + yy;
if(locationx < 0 || locationy < 0)
this.reset = true;
if(locationx > width || locationy > height)
this.reset = true;
GuiUtils.drawCircle(this.x + xx, this.y + yy, this.size, -1);
if(connect)
{
for(Particle p : particles)
{
if(p.connect)
drawLine(locationx, locationy, p.locationx, p.locationy);
}
}
}
public void move(int startX, int startY, int endX, int endY)
{
x = (int)startX;
y = (int)startY;
moving = true;
}
public void resetPosSize()
{
this.x = this.random.nextInt(ParticleGalaxy.this.width);
this.y = this.random.nextInt(ParticleGalaxy.this.height);
startx = x;
starty = y;
movex = height - random.nextInt(height);
movey = width - random.nextInt(width);
move(startx, starty, movex, movey);
}
public float genRandom(float min, float max)
{
return (float)(min + Math.random() * (max - min + 1.0F));
}
}
Hey if i have a simple rectangle class, how can i make it so that it creates that rectangle next to each other in a grid like pattern? maybe like 10 rows 10 columns?
public class Vak {
private int posX = 0;
private int posY = 0;
private int width = 50;
private int height = 50;
private Color colour;
public Vak(Color c, int x, int y){
this.colour = c;
this.posX = x;
this.posY = y;
}
public int vakPosY(){
return this.posY;
}
public int vakPosX(){
return this.posX;
}
public void draw (Graphics g){
g.setColor(this.colour);
g.drawRect(posX, posY, width, height);
}
public void move(int numberPixelsX, int numberPixelsY){
this.posX = this.posX + numberPixelsX;
this.posY = this.posY + numberPixelsY;
}
}
this is my code for rectangle "vak"
Is this what you are looking for?
int mapWidth = 10;
int mapHeight = 10;
// tileWidth and tileHeight should probably be public static const fields or static readonly properties of some class, but I put them here for now.
int tileWidth = 50; // Pixels
int tileHeight = 50; // Pixels
// tiles should probably be a field of a Map class (if you have one)
Vak[][] tiles = new Vak[mapWidth][mapHeight];
for(int x = 0; x < mapWidth; x++)
{
for(int y = 0; y < mapHeight; y++)
{
tiles[x][y] = new Vak(Color.white, x*tileWidth, y*tileHeight);
}
}
And then in the drawing part of the main loop:
for(Vak[] row : tiles)
{
for(Vak tile : row)
{
tile.draw(g);
}
}
I am trying to convert some (ugly looking) code into functions, such that everything is better managable. However I have come to a problem, current code:
int red = card.getColor().getRed();
int green = card.getColor().getGreen();
int blue = card.getColor().getBlue();
Color color = new Color(red, green, blue);
int width;
int height;
int xOffset;
int yOffset;
int border;
//Whole card
width = CARD_DIMENSION.width;
height = CARD_DIMENSION.height;
xOffset = 0;
yOffset = 0;
border = 5;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int dx = Math.min(x, width - 1 - x);
int dy = Math.min(y, height - 1 - y);
if (dx < border || dy < border) {
g2d.setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
}
else {
g2d.setColor(new Color(red, green, blue, 192 - Math.min(dx, dy)));
}
g2d.drawLine(x + xOffset, y + yOffset, x + xOffset, y + yOffset);
}
}
I would have to copy this everytime I want to draw some new rectangle. At first sight it isn't hard to put it in a function, however these lines:
if (dx < border || dy < border) {
g2d.setColor(new Color(red, green, blue, 255 - Math.min(dx, dy)));
}
else {
g2d.setColor(new Color(red, green, blue, 192 - Math.min(dx, dy)));
}
They make it hard since I use dx and dy there.
Now I have come up with a proposed solution which I would want to use with anonymous inner classes and overriding the two functions:
private abstract class CustomRectangle {
protected final int width;
protected final int height;
protected final int xOffset;
protected final int yOffset;
protected final int borderSize;
public CustomRectangle(final int width, final int height, final int xOffset, final int yOffset, final int borderSize) {
this.width = width;
this.height = height;
this.xOffset = xOffset;
this.yOffset = yOffset;
this.borderSize = borderSize;
}
abstract void inBorder();
abstract void outBorder();
public void draw(Graphics2D g2d) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int dx = Math.min(x, width - 1 - x);
int dy = Math.min(y, height - 1 - y);
if (dx < borderSize || dy < borderSize) {
inBorder();
}
else {
outBorder();
}
g2d.drawLine(x + xOffset, y + yOffset, x + xOffset, y + yOffset);
}
}
}
}
But I am still afraid that it will be hard if not impossible to put in a formula using dx and dy (two local variables to the for loops).
I would appreciate it if anyone has any suggestions on either the general problem or on my own approach, because having several codeblocks that do essentially the same is just not acceptable.
Regards.