So I have this little program (or sketch if you like). My problem is, and how should I word this, that the lines, when they are moving, kind of jitter and flutter a bit. Is this to do with the processing power of my computer, or should I code this in a different manner?
This is the code:
int i, j;
void setup() {
size(1440, 900);
background(0);
smooth();
strokeWeight(10);
i = width/2 - (width/2);
j = width;
}
void draw() {
fill(0, 10); // semi-transparent black
stroke(0);
rect(0, 0, width, height); //legger seg lag på lag
if (i < width-200) {
i+=4;
j-=4;
}
else {
i = width/2 - (width/2);
j = width;
}
stroke(255);
line(width/2, height, i, 30);
line(width/2, height, i+40, 30);
line(width/2, height, i+80, 30);
line(width/2, height, i+120, 30);
line(width/2, height, i+160, 30);
line(width/2, height, i+200, 30);
line(width/2, height, j, 30);
line(width/2, height, j-40, 30);
line(width/2, height, j-80, 30);
line(width/2, height, j-120, 30);
line(width/2, height, j-160, 30);
line(width/2, height, j-200, 30);
}
After doing a quick profile with JVisualVM it turns out that there are two culprits:
Rendering lines
Handling anti-aliased transparency
Rending lines:
Behind the scenes Processing is rendering each line as a shape(beginShape()/endShape()), in this case using LINES. You can give Processing a hand, and rather than using multiple beginShape/endShape calls(1 per line), just use one for all your lines:
beginShape(LINES);
for(int k = 0; k < 200; k+= 40){
vertex(hw, height);vertex(i+k, 30);
vertex(hw, height);vertex(j-k, 30);
}
endShape();
Anti-alias and transparency
Using transparency is generally computationally expensive, especially for large images.
Running the snippet bellow, press the mouse button and see how frameRate changes when transparency isn't used.
Anti-aliasing is also computationally expensive. Not as much as transparency, but in addition too, it makes a difference. Press any key to toggle between aliased and anti-aliased graphics
Here are a few tweaks to your code:
int i, j;
int hw;
boolean smooth;
void setup() {
size(1440, 900);
background(0);
strokeWeight(10);
hw = width/2;
i = width/2 - (width/2);//isn't this 0 ?
j = width;
}
void draw() {
fill(0,mousePressed ? 255 : 10); // semi-transparent black
noStroke();
rect(0, 0, width, height); //legger seg lag på lag
if (i < width-200) {
i+=4;
j-=4;
}
else {
i = 0;
j = width;
}
stroke(255);
beginShape(LINES);
for(int k = 0; k < 200; k+= 40){
vertex(hw, height);vertex(i+k, 30);
vertex(hw, height);vertex(j-k, 30);
}
endShape();
frame.setTitle((int)frameRate+" fps, smooth: " + smooth);
}
void keyReleased(){
smooth = !smooth;
if(smooth) smooth();
else noSmooth();
}
Related
When I run this program the green triangles appear all over the place when I click instead I want it to reposition itself to a random place and remove the original green triangle
float x;
void setup()
{
size (800, 600);
background(29, 154, 178);
x=random(width);
frameRate(30);
}
void draw()
{
noStroke();
fill(91, 180, 118 );
triangle(x, 20, 0, height, width, height);
fill(82, 45, 80);
triangle(width/2-200, 120, 0, height, width, height);
fill(82, 45, 60);
triangle(width/2+150, 220, 0, height, width, height);
fill(82, 45, 28);
triangle(width/2-100, 320, 0, height, width, height);
fill(243, 245, 158);
rect(0, 525, 800, 100);
if(mousePressed == true)
{
x=random(width);
}
}
Then add noLoop() in your setup so you're not drawing the same thing 60 times a second, and use mouse click handling instead, with a redraw call so you only draw when there is something new to draw:
float x, y, triangleSideLength;
void setup() {
size(300,300);
noLoop();
triangleSideLength = 30; // or whatever value it needs to be of course
}
void draw() {
x = random(0, width - triangleSideLength);
y = random(0, height - triangleSideLength);
drawTriangleAt(x, y);
}
void drawTriangleAt(float x, float y) {
//...triangle drawing code here...
}
void mouseClicked() {
redraw();
}
Also note that you usually want to constrain your x/y coordinates so that a triangle will always "fit on the screen" instead of getting an x/y coordinate that happens to have x=width and now your triangle is basically 100% out of view.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I've gone through and made classes for my objects and calls to them in my paint component, but I cant actually get them to move. Here is my move and check walls program that is in the object class:
public void move()
{
ballX += dx;
ballY += dy;
checkWalls();
}
//bounce of top/bottom, pass through left/right
public void checkWalls() {
//left-right
if(ballX > w) {
ballX = -diam;
}
else if(ballX < -diam) {
ballX = w;
}
//top-bottom
if(ballY < 0) {
dy *= -1;
}
else if(ballY + diam > h) {
dy *= -1;
}
}
And here is my call to them:
while(true) // infinite loop
{
jelly1.move();
frame.repaint();
try
{
Thread.sleep(10);
}
catch(Exception e){}
}
Also i feel the need to mention i have a background and a background component. The while(true) is in the background component because that's where the objects are created. And the frame is set visible in the background where the main method is.
Paint component is as follows:
public class BackgroundComponent extends JComponent {
Jellyfish jelly1;
Jellyfish jelly2;
Jellyfish jelly3;
Diver diver;
public BackgroundComponent() {
diver = new Diver(100, 300);
jelly1 = new Jellyfish(450, 450);
jelly2 = new Jellyfish(150, 300);
jelly3 = new Jellyfish(350, 75);
diver = new Diver(100, 300);
}
public void paintComponent(Graphics g){
//Drawing instructions go here
//Recover Graphics2D
Graphics2D g2 = (Graphics2D)g;
//Make gradient
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
int w = getWidth();
int h = getHeight();
Color color1 = Color.CYAN;
Color color2 = Color.BLACK;
GradientPaint gp = new GradientPaint(0, 0, color1, 0, h, color2);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
//Constructs rectangles on edge of screen and draws them
Rectangle box = new Rectangle(0,0,75,700);
g.setColor(Color.LIGHT_GRAY);
g2.fill(box);
Rectangle box2 = new Rectangle(625, 0, 75, 700);
g.setColor(Color.LIGHT_GRAY);
g2.fill(box2);
//Draws lines, with a stroke of 5, over rectangles
Line2D.Double segment = new Line2D.Double(10, 0, 10, 700);
g2.setStroke(new BasicStroke(5));
g.setColor(Color.GRAY);
g2.draw(segment);
Line2D.Double segment2 = new Line2D.Double(30, 0, 30, 700);
g.setColor(Color.GRAY);
g2.draw(segment2);
Line2D.Double segment3 = new Line2D.Double(50, 0, 50, 700);
g.setColor(Color.GRAY);
g2.draw(segment3);
Line2D.Double segment4 = new Line2D.Double(70, 0, 70, 700);
g.setColor(Color.GRAY);
g2.draw(segment4);
Line2D.Double segment5 = new Line2D.Double(690, 0, 690, 700);
g.setColor(Color.GRAY);
g2.draw(segment5);
Line2D.Double segment6 = new Line2D.Double(670, 0, 670, 700);
g.setColor(Color.GRAY);
g2.draw(segment6);
Line2D.Double segment7 = new Line2D.Double(650, 0, 650, 700);
g.setColor(Color.GRAY);
g2.draw(segment7);
Line2D.Double segment8 = new Line2D.Double(630, 0, 630, 700);
g.setColor(Color.GRAY);
g2.draw(segment8);
//Draws rectangle around title with thick boarder
Rectangle box3 = new Rectangle(40,40,620,75);
g.setColor(Color.WHITE);
g2.setStroke(new BasicStroke(5));
g2.draw(box3);
//Drawing text
String title = "Through the Depths";
//Sets font, font size, and color
g.setFont(new Font("Purisa", Font.BOLD, 50));
g.setColor(Color.DARK_GRAY);
g2.drawString(title, (50), 100);
//Places same text slightly up and over
g.setFont(new Font("Purisa", Font.BOLD, 50));
g.setColor(Color.WHITE);
g2.drawString(title, 53, 97);
//Draws ellipses with a stroke of 2 (these are the bubbles)
Ellipse2D.Double ellipse = new Ellipse2D.Double(450, 200, 150, 150);
g2.setStroke(new BasicStroke(2));
g2.draw(ellipse);
Ellipse2D.Double ellipse2 = new Ellipse2D.Double(510, 375, 90, 90);
g2.draw(ellipse2);
Ellipse2D.Double ellipse3 = new Ellipse2D.Double(470, 485, 70, 70);
g2.draw(ellipse3);
Ellipse2D.Double ellipse4 = new Ellipse2D.Double(510, 580, 45, 45);
g2.draw(ellipse4);
// Draws curves for bubbles
QuadCurve2D q = new QuadCurve2D.Float();
q.setCurve(548, 210, 607, 240, 590, 295);
g2.setStroke(new BasicStroke(3));
g2.draw(q);
QuadCurve2D q2 = new QuadCurve2D.Float();
q2.setCurve(575, 387, 607, 415, 585, 445);
g2.draw(q2);
QuadCurve2D q3 = new QuadCurve2D.Float();
g2.setStroke(new BasicStroke(2));
q3.setCurve(515, 493, 545, 511, 528, 540);
g2.draw(q3);
QuadCurve2D q4 = new QuadCurve2D.Float();
g2.setStroke(new BasicStroke(1));
q4.setCurve(538, 585, 558, 595, 545, 617);
g2.draw(q4);
// Sets color to pink before drawing jellyfish
g.setColor(Color.PINK);
//draws jellyfish
jelly1.draw(g);
jelly2.draw(g);
jelly3.draw(g);
// Draws diver
diver.draw(g);
while(true) // infinite loop
{
jelly1.move();
repaint();
try
{
Thread.sleep(10);
}
catch(Exception e){}
}
}
}
while(true){ ... } and Thread.Sleep inside a paintComponent implementation is completely the wrong way of doing things. By doing this, you are blocking the Event Dispatching Thread completely which means that your UI will no longer be updated properly and the UI will no longer be responsive.
What you should do:
Remove the while(true){ ... } loop from the paintComponent override
Create a javax.swing.Timer, set it to run every 10 milliseconds
In the ActionListener.actionPerformed implementation - the action that will be performed each 10 ms - move the jelly and call for a repaint
Start this timer after initialization is done, eg at the end of your constructor or initialization method (not paintComponent).
Stop the timer when it is no longer needed
Simplified example for this Timer, based on your snippet:
new javax.swing.Timer( 10, new ActionListener( ) {
#override
public void actionPerformed( ActionEvent e ) {
jelly1.move();
repaint();
}
}).start();
I need to outline in blue 1 rectangle at a time when selected. The code works but I cant see the blue outline very well. what is the easiest way to make the outline thicker. Wither that be drawing another shape over it or something else.
private void displayBlock(Graphics g)
{
for(int i = 0; i < 10; i++)
{
if (occupant[i].equals("Empty"))
{
//Sets the rectangle red to show the room is occupied
g.setColor(emptyColor);
g.fillRect(150+(i*50), 120, aptWidth, aptHeight);
}
else
{
//Sets the rectangle green to show the room is free
g.setColor(Color.red);
g.fillRect(150+(i*50), 120, aptWidth, aptHeight);
}
if (i == selectedApartment)
{
g.setColor(Color.blue);
g.drawRect(150+(i*50), 120, aptWidth, aptHeight);
}
else
{
g.setColor(Color.black);
g.drawRect(150+(i*50), 120, aptWidth, aptHeight);
}
// Draws the numbers in the Corresponding boxes
g.setColor(Color.black);
g.drawString(Integer.toString(i+1), 150+(i*50)+15, 120+25);
}
//Drawing the key allowing the user to see what the colours refer to
g.setFont(new Font("TimesRoman", Font.PLAIN, 14));
g.drawString("Key", 20,120);
g.drawString("Occupied",60,150);
g.drawString("Empty", 60, 170);
// Drawing the boxes to show what the colours mean in the key
g.setColor(Color.red);
g.fillRect(20, 130, 25, 25);
g.setColor(Color.black);
g.drawRect(20, 130, 25, 25);
g.setColor(Color.green);
g.fillRect(20,150,25,25);
g.setColor(Color.black);
g.drawRect(20,150,25,25);
} // End of displayBlock
You can set the thickness of the rectangle by creating a Graphics2D object and setting its stroke.
java.awt.Graphics2D g2 = (java.awt.Graphics2D) g.create();
g2.setStroke(new java.awt.BasicStroke(3)); // thickness of 3.0f
g2.setColor(Color.blue);
g2.drawRect(10,10,50,100); // for example
Maybe you could draw a second rectangle immediately inside the first one. Or multiple of them.
public static void drawRectangle(Graphics g, int x, int y, int width, int height, int thickness) {
g.drawRect(x, y, width, height);
if (thickness > 1) {
drawRectangle(g, x + 1, y + 1, width - 2. height - 2, thickness - 1);
}
}
For a class project I and a few others are working on an isometric game. Everything is being drawn in one JPanel using a buffered image. Each piece of artwork is done in fireworks and saved as a .png.
On Linux, the time it takes to redraw the map every game tick is around 3ms. On Windows (and also OSx) it's around 100ms spiking to 500ms.
This effect has been observed on 4 different computers ranging from typical laptop to an i7-3770K + 660 gaming machine. The CPU usage when this occurs is around 10-20% with RAM usage of the program being around 1GB. The problem has been researched on the internet in many places to no avail and also our section leaders (who are in charge of the project) were stumped. Any ideas?
Here is the paint component in the JPanel and the setEntityImage method. You can see where the time stamps are pulled from at the bottom of the paintComponent. The setEntityImage is being set every 500ms with a new BufferedImage that is pre-drawn on another thread before being passed into this JPanel. I'm including that code as I'm also curious if it's a possible threading issue.
This code is called every 500 ms to consist of a 'game tick', it draws the images (which are all pre-loaded in a static singleton class).
BufferedImage entityImg = ImageUtil.getFromGraphics(map.getWidth() * TILE_WIDTH
+ TILE_WIDTH, map.getHeight() * TILE_HEIGHT + TILE_HEIGHT);
Graphics2D g2 = (Graphics2D) entityImg.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < map.getHeight(); i++)
{
for (int j = 0; j < map.getWidth(); j++)
{
try
{
int x, y;
x = (j * TILE_WIDTH_HALF - i * TILE_WIDTH_HALF)
+ map.getWidth() * TILE_WIDTH_HALF;
y = j * TILE_HEIGHT_HALF + i * TILE_HEIGHT_HALF;
Entity e = map.getTile(i, j).getEntity();
if (e != null)
{
g2.drawImage(e.getImage(), x, y, 32, 32, null);
}
}
catch (Exception e)
{
}
}
}
panel.setEntityImage(entityImg);
This code is in our MapPanel which extends a JPanel.
public void setEntityImage(BufferedImage entityImg)
{
this.entityImg = entityImg;
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics g2 = (Graphics2D) g;
g2.fillRect(0, 0, tileImg.getWidth(), tileImg.getHeight());
// Draw tiles
g2.drawImage(tileImg, 0, 0, null);
// Draw box over square mouse is hovering over
if (hover)
{
g2.setColor(new Color(255, 255, 255, 128));
int screenX = (this.x * 16 - this.y * 16) + Window.WIDTH * 16;
int screenY = (this.x * 8 + this.y * 8) + 16;
g2.drawLine(screenX, screenY + 8, screenX + 16, screenY);
g2.drawLine(screenX + 16, screenY, screenX + 32, screenY + 8);
g2.drawLine(screenX, screenY + 8, screenX + 16, screenY + 16);
g2.drawLine(screenX + 16, screenY + 16, screenX + 32, screenY + 8);
}
long time = System.currentTimeMillis();
g2.drawImage(entityImg, 0, 0, null);
System.out.println(System.currentTimeMillis() - time);
}
There's some kind of internal optimisation caching going, the particulars I'm not aware of. Essentially, if the BufferedImage is not updated, it paints in 0ms.
I did two optimizations, not sure if either work alone or not but it made a difference in the performance, but not in the time delay (ie, When I dragged the map about, it was significantly better)
The first was in Window#tickUpdate. Rather the creating a new BufferedImage every 500ms, this simply re-uses a single BufferedImage only creating a new instance if there wasn't a previous instance or the size has changed...
public void tickUpdate(Map map) {
int width = map.getWidth() * TILE_WIDTH + TILE_WIDTH;
int height = map.getHeight() * TILE_HEIGHT + TILE_HEIGHT;
if (entityImg == null || entityImg.getWidth() != width || entityImg.getHeight() != height) {
entityImg = ImageUtil.getFromGraphics(width, height);
}
Graphics2D g2 = (Graphics2D) entityImg.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g2.setBackground(new Color(255, 255, 255, 0));
g2.clearRect(0, 0, width, height);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
for (int i = 0; i < map.getHeight(); i++) {
for (int j = 0; j < map.getWidth(); j++) {
Entity e = map.getTile(i, j).getEntity();
if (e != null) {
int x, y;
x = (j * TILE_WIDTH_HALF - i * TILE_WIDTH_HALF)
+ map.getWidth() * TILE_WIDTH_HALF;
y = j * TILE_HEIGHT_HALF + i * TILE_HEIGHT_HALF;
g2.drawImage(e.getImage(), x, y, 32, 32, null);
}
}
}
g2.dispose();
panel.setEntityImage(entityImg);
panel.repaint();
}
And because the BufferedImage could be updated while a paint was occurring, in the MapPanel, I created a another BufferedImage onto which the "entity" image was painted.
public void setEntityImage(BufferedImage img) {
entityLock.lock();
try {
if (entityImg == null || entityImg.getWidth() != img.getWidth() || entityImg.getHeight() != img.getHeight()) {
entityImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = entityImg.createGraphics();
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
} finally {
entityLock.unlock();
}
}
entitytLock is just a ReentrantLock which I used to stop the paintComponent method from trying to paint the BufferedImage while it was been updated.
This doesn't change the timing, but made it, visually, better...
An alternative is to use a VolatileImage...
I have an application that extends a Frame. Then, it'll display a few lines of text using:
Font f = new Font("Arial", Font.PLAIN, 10);
g.setFont(f);
g.drawString("Test|great Yes ^.", x, y + 10);
Now what happens is that the text doesn't fit in the box around. E.g. I'm expecting the text to fit in [x,y]-[x+width, y+10] (don't care about the width) but it falls somewhat below the y+10 line. Now for most characters ('T', 'e', etc.) this fits but '|' and 'g' don't! They go below the y+10-line. It seems you can't use: draw at y + characterHeight. But what does work?
To see what I mean, here's some sample code:
import java.awt.*;
public class test extends Frame
{
public test()
{
/* retrieve max window size */
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
GraphicsConfiguration [] gc = gs[0].getConfigurations();
Rectangle r = gc[0].getBounds();
setSize(r.width, r.height);
setVisible(true);
}
public void paint(Graphics g)
{
final int windowWidth = getSize().width;
final int windowHeight = getSize().height;
g.setColor(Color.BLUE);
g.fillRect(0, 0, windowWidth, windowHeight);
g.setColor(Color.WHITE);
g.fillRect(0, 100, windowWidth, 110);
int textHeight = 100;
Font f = new Font("Arial", Font.PLAIN, textHeight);
g.setFont(f);
g.setColor(Color.BLACK);
g.drawString("Test|great Yes ^.", 10, 100 + textHeight);
}
public void guiLoop()
{
for(;;) { try { Thread.sleep(1000); } catch(Exception e) { } }
}
public static void main(String [] args)
{
new test().guiLoop();
}
}
I tried the following code as well:
public void paint(Graphics g)
{
final int windowWidth = getSize().width;
final int windowHeight = getSize().height;
g.setColor(Color.BLUE);
g.fillRect(0, 0, windowWidth, windowHeight);
g.setColor(Color.WHITE);
g.fillRect(0, 100, windowWidth, 110);
int textHeight = 100;
String str = "Test|great Yes ^.";
Font f = new Font("Arial", Font.PLAIN, textHeight);
Rectangle2D boundingRectangle = f.getStringBounds(str, 0, str.length(), new FontRenderContext(null, false, false));
f = f.deriveFont((float)(textHeight * (textHeight / boundingRectangle.getHeight())));
boundingRectangle = f.getStringBounds(str, 0, str.length(), new FontRenderContext(null, false, false));
g.drawString(str, 10, 100 + (int)boundingRectangle.getHeight());
g.setFont(f);
g.setColor(Color.BLACK);
g.drawString(str, 10, 100 + textHeight);
}
This is somewhat better: the text is smaller so it might fit, but there's still the problem that the y-position is incorrect.
All help is appreciated!
What about using FontMetrics? You can obtain it from Graphics object with g.getFontMetrics().
Than you can retrieve max descent or ascent or directly height (using getHeight), so your implementation will be font-indipendent and it should work fine.. check documentation here!
EDIT (to explain comments):
there is no a direct way to tell to a string to draw itself in a manner that can fit a box. You have to do it by yourself.. like start from a max font size and check if width fits the box, otherwise decrement size and try again. For height you should FIRST decide (or obtain) max font height, then you can set how many pixel should the box be.
I think I solved it somewhat:
boundingBoxHeight: height of box in which the text should fit
yOffset where to start drawing the font
Font f = new Font("Arial", Font.PLAIN, boundingBoxHeight);
g.setFont(f);
FontMetrics fm = g.getFontMetrics();
double shrink = ((double)textHeight / (double)fm.getHeight());
double newSize = (double)textHeight * shrink;
double newAsc = (double)fm.getAscent() * shrink;
int yOffset = (int)newAsc - fm.getLeading();
f = f.deriveFont((float)newSize);
g.setFont(f);
g.drawString(str, 10, 100 + yOffset);
There's quite a bit of whitespace above the text though.