0

In my program I'm adding a JPannel that I'm using as canvas to a JFrame. The problem is that the size of the JPannel#setSize() is not what it is displayed. I first had a problem more or less like this one and I asked here Java questions about coordinates with Graphics They toldme to add the JPannel and use the pack() method and it worked but I can't get it to work now because the JFrame is larger than the JPannel where I am drawing. I have look at some threads to see if I coud fiand an answer but I wasn't succesfull. Here are the threads I looked:

I create a JFrame with setSize(600, 400); and I add the pane. Here is the code

import java.awt.Dimension;

import javax.swing.JFrame;

import me.nemo_64.particlessimulation.util.Canvas;

public class Frame extends JFrame {

    private Frame(String title) {
        super(title);
        setResizable(false);
        setSize(600, 400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(null);
        init();
    }

    private void init() {
        canvas = new Canvas();
        canvas.setSize(400, 400);
        canvas.setLocation(0, 0);
        canvas.setPreferredSize(new Dimension(400, 400));
        canvas.startDrawing();
        add(canvas);
    }

    private Canvas canvas;

    public static Frame getInstance() {
        if (instance == null)
            throw new IllegalAccessError("instanceFrame must be callen at least ones before this method");
        return instance;
    }

    public static Frame instanceFrame(String title) {
        if (instance != null)
            return getInstance();
        instance = new Frame(title);
        return getInstance();
    }

    private static Frame instance;

    private static final long serialVersionUID = 1L;
}

And here is the canvas class


import java.awt.Graphics2D;
import java.awt.event.MouseEvent;

public class Canvas extends ACanvas {

    Vector p, dir;
    float v;

    public Canvas() {
        p = new Vector(0, getHeight() / 2);
        dir = new Vector(50f, 50f);
        v = 50;
    }

    @Override
    public void update(float delta) {
        if (p.x <= 0 && dir.x < 0)
            dir.x *= -1;
        if (p.y <= 0 && dir.y < 0)
            dir.y *= -1;

        if (p.x + 100 >= getWidth() && dir.x > 0)
            dir.x *= -1;
        if (p.y + 100 >= getWidth() && dir.y > 0)
            dir.y *= -1;

        Vector a = dir.clone().multiply(delta);
        p.add(a);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println(e.getX() + " " + e.getY());
    }

    @Override
    public void draw(Graphics2D g) {
        System.out.println(g);
        fill(255, 0, 0);
        fillRectangle(p.x, p.y, 100, 100);
    }

}

And here is the ACanvas class

package me.nemo_64.particlessimulation.util;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;

public abstract class ACanvas extends JPanel
        implements KeyListener, MouseInputListener, MouseWheelListener, Drawable, Updateable, FigureDrawer {

    private Graphics2D g;

    private Thread drawThread;

    private Color backgroundColor;
    private Color actualColor;

    private long lastTime = 0;
    private float delta = 0;

    public ACanvas() {
        setFocusable(true);
        addKeyListener(this);
        addMouseListener(this);
        addMouseWheelListener(this);
        lastTime = System.currentTimeMillis();
        drawThread = new Thread(() -> {
            while (true)
                repaint();
        }, "Drawing thread");
    }

    @Override
    /**
     * Used to update all the components to be drawn
     */
    public abstract void update(float delta);

    /**
     * Draws all the comsponents
     */
    public void draw(Graphics2D g) {}

    @Override
    public Graphics2D getGraphics2D() {
        return this.g;
    }

    @Override
    /**
     * Draws all the comsponents
     */
    public void draw(Graphics g) {
        this.g = (Graphics2D) g;
        draw(this.g);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        delta = (System.currentTimeMillis() - lastTime) * 0.001f;
        lastTime = System.currentTimeMillis();
        clearBackground(g);
        update(delta);
        g.setColor(actualColor);
        draw(g);
    }

    public void clearBackground(Graphics g) {
        g.setColor(backgroundColor);
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    public void startDrawing() {
        if (!drawThread.isAlive())
            drawThread.start();
    }

    public void stopDrawing() {
        if (drawThread.isAlive())
            drawThread.interrupt();
    }

    public void background(Color c) {
        backgroundColor = c;
    }

    public void background(int c) {
        backgroundColor = new Color(c);
    }

    public void background(int r, int g, int b) {
        backgroundColor = new Color(r, g, b);
    }

    public void background(float r, float g, float b) {
        backgroundColor = new Color(r, g, b);
    }

    public void fill(Color c) {
        actualColor = c;
        g.setColor(c);
    }

    public void fill(int c) {
        fill(new Color(c));
    }

    public void fill(float r, float g, float b) {
        fill(new Color(r, g, b));
    }

    public void fill(int r, int g, int b) {
        fill(new Color(r, g, b));
    }

    @Override
    public void mouseClicked(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseDragged(MouseEvent e) {}

    @Override
    public void mouseMoved(MouseEvent e) {}

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {}

    @Override
    public void keyReleased(KeyEvent e) {}

    @Override
    public void keyTyped(KeyEvent e) {}

}

It doen't seem to work in the height. In the canvas class I have a mouse click event that displays the position of the click. The width I click and the output is a coordenade like (0, ?) for the left and (400, ?) for the right. The problem is the height, at the top I get (?, 0) but at the bottom the highest value I could get is (?, 370) and not (?, 400). My question is: why the bottom doesn't go to (?, 400)? Thanks for the help, if something is not clear just ask it

EDIT: Other clases of the program are: A vector class:

public class Vector {

    public float x, y, z;

    public Vector(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector(float x, float y) {
        this(x, y, 0);
    }

    public Vector() {
        this(0f, 0f, 0f);
    }

    public Vector multiply(float... values) {
        for (float f : values) {
            x *= f;
            y *= f;
            z *= f;
        }
        return this;
    }

    public Vector divide(float... values) {
        for (float f : values) {
            x /= f;
            y /= f;
            z /= f;
        }
        return this;
    }

    public Vector add(Vector... vectors) {
        for (Vector v : vectors)
            add(v.x, v.y, v.z);
        return this;
    }

    public Vector add(float x, float y, float z) {
        this.x += x;
        this.y += y;
        this.z += z;
        return this;
    }

    public Vector add(float x, float y) {
        return add(x, y, 0);
    }

    public Vector remove(Vector... vectors) {
        for (Vector v : vectors)
            remove(v.x, v.y, v.z);
        return this;
    }

    public Vector remove(float x, float y, float z) {
        this.x -= x;
        this.y -= y;
        this.z -= z;
        return this;
    }

    public Vector remove(float x, float y) {
        return remove(x, y, 0);
    }

    public Vector normalize() {
        double mod = module();
        this.x /= mod;
        this.y /= mod;
        this.z /= mod;
        return this;
    }

    public double module() {
        return Math.sqrt(moduleSquared());
    }

    public double moduleSquared() {
        return (double) (x * x + y * y + z * z);
    }

    public Vector clone() {
        return new Vector(x, y, z);
    }

    @Override
    public String toString() {
        return "[ " + x + ", " + y + ", " + z + "]";
    }

}

A drawing interface:

package me.nemo_64.particlessimulation.util;

import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

public interface FigureDrawer {

    default public void fillRectangle(float x, float y, float width, float height) {
        getGraphics2D().fill(new Rectangle2D.Float(x, y, width, height));
    }

    default public void drawRectangle(float x, float y, float width, float height) {
        getGraphics2D().draw(new Rectangle2D.Float(x, y, width, height));
    }

    default public void fillCircle(float x, float y, float r) {
        fillCircle(x, y, r, r);
    }

    default public void drawCircle(float x, float y, float r) {
        drawCircle(x, y, r, r);
    }

    default public void fillCircle(float x, float y, float r1, float r2) {
        getGraphics2D().fill(new Ellipse2D.Float(x, y, r1, r2));
    }

    default public void drawCircle(float x, float y, float r1, float r2) {
        getGraphics2D().draw(new Ellipse2D.Float(x, y, r1, r2));
    }

    public Graphics2D getGraphics2D();

}

And the Drawable and Updateable interfaces:

package me.nemo_64.particlessimulation.util;

public interface Updateable {

    /**
     * Called when the component must be updated
     * @param delta The seconds that past between calls
     */
    public void update(float delta);

}
package me.nemo_64.particlessimulation.util;

import java.awt.Graphics;

public interface Drawable {

    /**
     * Called when the component must be drawn
     */
    public void draw(Graphics g);

}
VGR
  • 40,506
  • 4
  • 48
  • 63
Nemo_64
  • 15
  • 1
  • 5
  • It's confusing when you use class names from the JDK for your own classes. For example class `Vector` in the code you posted is not `java.util.Vector`, is it? So where is the code for your class `Vector`? – Abra Jun 01 '20 at 21:46
  • You request the size of a JPanel with the setPreferredSize method. – Gilbert Le Blanc Jun 01 '20 at 22:00
  • No it is not the java.util.Vector, I didn't think that posting the Vector class would be necesary, I'll put the code. If you need any more clases just tell me – Nemo_64 Jun 01 '20 at 22:00
  • @GilbertLeBlanc could you explain? – Nemo_64 Jun 01 '20 at 22:04
  • In the JPanel class, there's a method called setPreferredSize. When you're creating the JPanel, you call the setPreferredSize method with the JPanel size you want. When you've finished creating all the Swing components, you call the JFrame pack method. – Gilbert Le Blanc Jun 01 '20 at 22:11
  • @GilbertLeBlanc I removed the setPreferredSize and still not working – Nemo_64 Jun 01 '20 at 22:13
  • Then your code has additional problems. Use a debugger or lots of System.out.println statements to narrow down your problems. – Gilbert Le Blanc Jun 01 '20 at 22:18
  • @GilbertLeBlanc what could it be? I have already and I didn't figure it out, thats why I'm asking here – Nemo_64 Jun 01 '20 at 22:19
  • Your code is way too confusing. All the class names map to other JDK class names as has already been pointed out. You have looping code with repaint. Don't know why that is necessary. You have all kinds of listeners you add to your components. They are not relevant to the problem. You problem is about the size of a panel. So start over with a frame and a panel that you want to do custom painting on. Then you override the `getPreferredSize()` method of this panel to the size your want. Then you use frame.pack(). – camickr Jun 02 '20 at 01:06
  • 1
    So first get the panel the correct size. Once that works then you start adding in your painting logic. Coding is about one step at a time. Write a piece of code and test. Then add more and test. Don't try to test the basic sizing of a panel with all that extra code. If you still have a problem then you have an [mre] to post. – camickr Jun 02 '20 at 01:07
  • 1) Java GUIs have to work on different OS', screen size, screen resolution etc. using different PLAFs in different locales. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556) along with layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). **BTW - setting a `null` layout makes most 'set size' calls redundant.** 2) See [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/q/7229226/418556) (Yes.) – Andrew Thompson Jun 02 '20 at 01:36
  • `private Graphics2D g;` ← Remove that. A Graphics object is only valid for the duration of the painting method to which it was passed. If you try to store it and use it after the painting method has returned, it will not work as expected. – VGR Jun 02 '20 at 13:57

1 Answers1

0

I see a lot of comments here (all valid) but the question you're asking is why the canvas is showing something like ~ 370 instead of 400 on the right hand side (if I read this correctly).

Your canvas is probably x,y(400,400) but the parent container is only 400px wide based on your initial call. There's a good chance your window border and other operating system specific elements are adding quite a few pixels (on Windows Vista/7 I seem to remember this was ~30px) so there's a really good chance that's the problem.

As a side note (having written some particle simulations in Java/Processing) I can say you might want to look at the OpenGL style of positioning using floats from -1.0f/1.0f representing your canvas rather than trying to do everything in integers. First, you're going to have a lot of choppy "bounces" working with integers as you're going to want to represent values between pixels. Secondly, this makes resizing your simulation to an arbitrary x/y width very easy.

https://learnopengl.com/Getting-started/Coordinate-Systems

Good luck with it, I stopped writing GUIs in Java five or six years ago so I'm a bit rusty. Null layouts can be problematic, I would recommend picking a layout manager and using setPreferredSize (GridBag was my favorite).

Daniel B. Chapman
  • 4,647
  • 32
  • 42