Hi so I'm writing an simple physics engine to understand object collisions and Java graphics a bit better, and so far I have successfully written the code to add the JPanel to the JFrame and allow them to show up somewhat the correct size, but when I view the actually program, the JPanel seems tobe the right size but it does not start in the upper corner of the window, but rather the upper left of the frame. I seem to have this problem alot where I want something to be at (0, 0) and starts in the upper corner of the frame rather than the panel. Here is my code:
I have an engine class that extends JFrame and contains the main method -->
package io.shparki.PhysicsEngine;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class Engine extends JFrame{
public Engine(){
super("Physics Engine and Simulator");
setLayout(new BorderLayout());
add(new EnginePanel(), BorderLayout.CENTER);
pack();
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args){
new Engine();
}
}
and this is my second class, the EnginePanel which extends JPanel and implements Runnable -->
package io.shparki.PhysicsEngine;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import javax.swing.JPanel;
public class EnginePanel extends JPanel implements Runnable{
private static final int WIDTH = 300;
private static final int HEIGHT = WIDTH / 16 * 9;
private static final int SCALE = 4;
public int getWidth() { return WIDTH * SCALE; }
public int getHeight() { return HEIGHT * SCALE; }
#Override
public Dimension getPreferredSize(){ return new Dimension(WIDTH * SCALE, HEIGHT * SCALE); }
private static final int FPS = 85;
private static final int PERIOD = 1000 / FPS;
private int currentFPS = 0;
private Thread animator;
private boolean running = false;
private Graphics dbg;
private Image dbImage = null;
public EnginePanel(){
setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
setVisible(true);
}
public void addNotify(){
super.addNotify();
startEngine();
}
public void startEngine(){
running = true;
animator = new Thread(this, "Animator");
animator.start();
}
public void stopEngine(){
running = false;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if (dbImage != null){
g.drawImage(dbImage, 0, 0, null);
}
}
public void paintScreen(){
Graphics g;
try{
g = this.getGraphics();
if ( g != null && dbImage != null){
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
} catch(Exception ex) { System.out.println("Graphics Context Error : " + ex); }
}
public void run(){
running = true;
init();
Long beforeTime, timeDiff, sleepTime;
while(running){
beforeTime = System.currentTimeMillis();
updateEngine();
renderEngine();
paintScreen();
timeDiff = System.currentTimeMillis() - beforeTime;
sleepTime = PERIOD - timeDiff;
if (sleepTime <= 0){
sleepTime = 5L;
}
currentFPS = (int) (1000 / (sleepTime + timeDiff));
try{
Thread.sleep(sleepTime);
} catch (InterruptedException ex) { ex.printStackTrace(); }
}
}
private TextField FPSTextField;
public void init(){
FPSTextField = new TextField("Currnet FPS: " + currentFPS, 25, 25);
}
public void updateEngine(){
FPSTextField.setText("Currnet FPS: " + currentFPS);
}
public void renderEngine(){
if (dbImage == null){
dbImage = createImage((int)getWidth(), (int)getHeight());
if (dbImage == null){
System.out.println("Graphical Context Error : DBImage is Null");
return;
} else {
dbg = dbImage.getGraphics();
}
}
Graphics2D g2d = (Graphics2D) dbg;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
FPSTextField.render(g2d, Color.MAGENTA);
}
}
I'm not quite sure why this keeps happening and I have searched for help but can not find the answer. Thanks in advance for all who help :)
EDIT: Added code for the TextField object:
package io.shparki.PhysicsEngine;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
public class TextField{
private Point location;
public Point getLocation(){ return location; }
public double getX() { return location.getX(); }
public double getY() { return location.getY(); }
private String text;
public void setText(String text) { this.text = text; }
public String getText() { return this.text; }
public TextField(String text, int x, int y){
this.location = new Point(x, y);
this.text = text;
}
public TextField(String text, Point location){
this.location = location;
this.text = text;
}
public void render(Graphics g){
g.drawString(text, (int)location.getX(), (int)location.getY());
}
public void render(Graphics2D g2d){
g2d.drawString(text, (int)location.getX(), (int)location.getY());
}
public void render(Graphics g, Color color){
g.setColor(color);
g.drawString(text, (int)location.getX(), (int)location.getY());
}
public void render(Graphics2D g2d, Color color){
g2d.setColor(color);
g2d.drawString(text, (int)location.getX(), (int)location.getY());
}
public void render(Graphics g, Color color, Font font){
g.setColor(color);
g.setFont(font);
g.drawString(text, (int)location.getX(), (int)location.getY());
}
public void render(Graphics2D g2d, Color color, Font font){
g2d.setColor(color);
g2d.setFont(font);
g2d.drawString(text, (int)location.getX(), (int)location.getY());
}
}
The preferred size of the JPanel EnginePanel restricts the panel from being resized the JFrame is rendered non-resizable. Invoke JFrame#pack after calling setResizable(false). Also move setLocationRelativeTo after pack so that the frame appears centered.
pack();
setLocationRelativeTo(null);
setVisible(true);
If you use a GridBagLayout in the JFrame, then the JPanel will be centered if it is the only thing you add to the JFrame. It will even stay in the center if you resize the window.
Also, the reason the coordinates seem off to you is that you are viewing it as an (x, y) coordinate while it is actually a (row, col) coordinate like in a 2D Array.
Related
I'm trying to draw rectangles on fairly large images in order to get the pixel coordinates of objects within the image. I am able to display the image and make it scrollable, or display the image and be able to draw rectangles on top of it....but not both.
It's obvious that I'm drawing the image on top of the canvas that I'm trying to draw the rectangles on, but I can't for the life of me figure out how to make it all coexist.
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DrawRect extends JPanel {
int x, y, x2, y2;
private static final long serialVersionUID = 1L;
private BufferedImage image;
private JPanel canvas;
public static void main(String[] args) {
JPanel p = new DrawRect();
JFrame f = new JFrame();
f.setContentPane(p);
f.setSize(400, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
DrawRect() {
x = y = x2 = y2 = 0; //
MyMouseListener listener = new MyMouseListener();
addMouseListener(listener);
addMouseMotionListener(listener);
try {
this.image = ImageIO.read(new URL("https://previews.123rf.com/images/victoroancea/victoroancea1201/victoroancea120100059/12055848-tv-color-test-pattern-test-card-for-pal-and-ntsc.jpg"));
}catch(IOException ex) {
Logger.getLogger(DrawRect.class.getName()).log(Level.SEVERE, null, ex);
}
this.canvas = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
canvas.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
JScrollPane sp = new JScrollPane(canvas);
setLayout(new BorderLayout());
add(sp, BorderLayout.CENTER);
}
public void setStartPoint(int x, int y) {
this.x = x;
this.y = y;
}
public void setEndPoint(int x, int y) {
x2 = (x);
y2 = (y);
}
public void drawRect(Graphics g, int x, int y, int x2, int y2) {
int px = Math.min(x,x2);
int py = Math.min(y,y2);
int pw=Math.abs(x-x2);
int ph=Math.abs(y-y2);
g.drawRect(px, py, pw, ph);
}
class MyMouseListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
setStartPoint(e.getX(), e.getY());
}
public void mouseDragged(MouseEvent e) {
setEndPoint(e.getX(), e.getY());
repaint();
}
public void mouseReleased(MouseEvent e) {
setEndPoint(e.getX(), e.getY());
repaint();
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
drawRect(g, x, y, x2, y2);
}
}
Your image-drawing JPanel must be the same JPanel that draws the rectangle and that has the MouseAdapter added to it. For instance:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class DrawRect2 extends JPanel {
public static final String IMG_PATH = "https://previews.123rf.com/images/victoroancea"
+ "/victoroancea1201/victoroancea120100059"
+ "/12055848-tv-color-test-pattern-test-card-for-pal-and-ntsc.jpg";
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private DrawingPanel drawingPanel;
public DrawRect2(Image img) {
drawingPanel = new DrawingPanel(img);
JScrollPane scrollPane = new JScrollPane(drawingPanel);
MyMouse myMouse = new MyMouse();
drawingPanel.addMouseListener(myMouse);
drawingPanel.addMouseMotionListener(myMouse);
setLayout(new BorderLayout());
add(scrollPane);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
Image img = null;
try {
URL url = new URL(IMG_PATH);
img = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
DrawRect2 mainPanel = new DrawRect2(img);
JFrame frame = new JFrame("DrawRect2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
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 javax.swing.JPanel;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private Image img;
private Rectangle rectangle;
public DrawingPanel(Image img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
if (rectangle != null) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setXORMode(Color.WHITE);
g2.draw(rectangle);
g2.dispose(); // since we created this object
}
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
if (img == null) {
return super.getPreferredSize();
} else {
int w = img.getWidth(this);
int h = img.getHeight(this);
return new Dimension(w, h);
}
}
public void setRectangle(Rectangle rectangle) {
this.rectangle = rectangle;
}
}
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MyMouse extends MouseAdapter {
private Point p1;
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (p1 != null) {
createRect(e);
}
}
private void createRect(MouseEvent e) {
Point p2 = e.getPoint();
int x = Math.min(p1.x, p2.x);
int y = Math.min(p1.y, p2.y);
int width = Math.abs(p1.x - p2.x);
int height = Math.abs(p1.y - p2.y);
Rectangle r = new Rectangle(x, y, width, height);
((DrawingPanel) e.getSource()).setRectangle(r);
((DrawingPanel) e.getSource()).repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (p1 != null) {
createRect(e);
}
p1 = null;
}
}
So in this paintComponent method, I draw both the image and the Rectangle, using Graphics2D XOR mode to help show the lines regardless of the background color:
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private Image img;
private Rectangle rectangle;
public DrawingPanel(Image img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
if (rectangle != null) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setXORMode(Color.WHITE);
g2.draw(rectangle);
g2.dispose(); // since we created this object
}
}
I also have this method:
public void setRectangle(Rectangle rectangle) {
this.rectangle = rectangle;
}
To allow the MouseListener/Adapter to pass in the Rectangle into this JPanel.
Rectangle r = new Rectangle(x, y, width, height);
((DrawingPanel) e.getSource()).setRectangle(r);
((DrawingPanel) e.getSource()).repaint();
I want to know How could I draw a String or Rectangle (the JFrame is in a full screen completely)
Heres what in my Main.java class:
public static int WIDTH, HEIGHT;
private Window window;
...
public Main() {
window = new Window("2D Shooter", this);
...
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
handler.render(g);//this calls the render method to render objects
g.dispose();
bs.show();
}
Later in a different class I have:
public void render(Graphics g){
...
g.setColor(Color.WHITE);
g.drawString("2D Shooter", ((Main.WIDTH)/2), (Main.HEIGHT/5));
...
}
This Code Works and runs BUT the text is not completely centered I want it to be Centered on top not in the middle. Thank You!
Use the graphics context's FontMetrics.
String s = "2D Shooter";
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getHeight();
g.drawString(s, getWidth() / 2 - w2, h2);
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see http://stackoverflow.com/a/37150783/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(getFont().deriveFont(Font.BOLD, 24f));
String s = "2D Shooter";
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getHeight();
g.drawString(s, getWidth() / 2 - w2, h2);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
});
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Test()::display);
}
}
I need to draw a ring, with given thickness, that looks something like this:
The center must be transparent, so that it doesn't cover previously drawn shapes. (or other rings) I've tried something like this:
//g is a Graphics2D object
g.setColor(Color.RED);
g.drawOval(x,y,width,height);
g.setColor(Color.WHITE);
g.drawOval(x+thickness,y+thickness,width-2*thickness,height-2*thickness);
which draws a satisfactory ring, but it covers other shapes; the interior is white, not transparent. How can I modify/rewrite my code so that it doesn't do that?
You can create an Area from an Ellipse2D that describes the outer circle, and subtract the ellipse that describes the inner circle. This way, you will obtain an actual Shape that can either be drawn or filled (and this will only refer to the area that is actually covered by the ring!).
The advantage is that you really have the geometry of the ring available. This allows you, for example, to check whether the ring shape contains a certain point, or to fill it with a Paint that is more than a single color:
Here is an example, the relevant part is the createRingShape method:
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RingPaintTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
RingPaintTestPanel p = new RingPaintTestPanel();
f.getContentPane().add(p);
f.setSize(800,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class RingPaintTestPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.RED);
g.drawString("Text", 100, 100);
g.drawString("Text", 300, 100);
Shape ring = createRingShape(100, 100, 80, 20);
g.setColor(Color.CYAN);
g.fill(ring);
g.setColor(Color.BLACK);
g.draw(ring);
Shape otherRing = createRingShape(300, 100, 80, 20);
g.setPaint(new GradientPaint(
new Point(250, 40), Color.RED,
new Point(350, 200), Color.GREEN));
g.fill(otherRing);
g.setColor(Color.BLACK);
g.draw(otherRing);
}
private static Shape createRingShape(
double centerX, double centerY, double outerRadius, double thickness)
{
Ellipse2D outer = new Ellipse2D.Double(
centerX - outerRadius,
centerY - outerRadius,
outerRadius + outerRadius,
outerRadius + outerRadius);
Ellipse2D inner = new Ellipse2D.Double(
centerX - outerRadius + thickness,
centerY - outerRadius + thickness,
outerRadius + outerRadius - thickness - thickness,
outerRadius + outerRadius - thickness - thickness);
Area area = new Area(outer);
area.subtract(new Area(inner));
return area;
}
}
You can use the Shape and Area classes to create interesting effects:
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
import java.net.*;
public class Subtract extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int size = 100;
int thickness = 10;
int innerSize = size - (2 * thickness);
Shape outer = new Ellipse2D.Double(0, 0, size, size);
Shape inner = new Ellipse2D.Double(thickness, thickness, innerSize, innerSize);
Area circle = new Area( outer );
circle.subtract( new Area(inner) );
int x = (getSize().width - size) / 2;
int y = (getSize().height - size) / 2;
g2d.translate(x, y);
g2d.setColor(Color.CYAN);
g2d.fill(circle);
g2d.setColor(Color.BLACK);
g2d.draw(circle);
g2d.dispose();
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Subtract");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Subtract());
frame.setLocationByPlatform( true );
frame.setSize(200, 200);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Using an Area you can also add multiple Shapes together or get the intersection of multiple Shapes.
You could use graphics.setStroke(...) for this. This way the center will be fully transparent and therefore won't cover previously drawn shapes. In my example I had to do some additional calculations because of this method though, to make sure the displayed x and y coordinates are actually the same as the ones of the Ring instance:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example {
public Example() {
ArrayList<Ring> rings = new ArrayList<Ring>();
rings.add(new Ring(10, 10, 100, 20, Color.CYAN));
JPanel panel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g.create();
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ring ring : rings) {
// Previously drawn
gg.setColor(Color.BLACK);
String str = "Hello!";
gg.drawString(str, ring.getX() + (ring.getWidth() - gg.getFontMetrics().stringWidth(str)) / 2,
ring.getY() + ring.getHeight() / 2 + gg.getFontMetrics().getAscent());
// The actual ring
ring.draw(gg);
}
gg.dispose();
}
};
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Example();
}
});
}
}
class Ring {
private int x, y, width, height, thickness;
private Color color;
public Ring(int x, int y, int width, int height, int thickness, Color color) {
setX(x);
setY(y);
setWidth(width);
setHeight(height);
setThickness(thickness);
setColor(color);
}
public Ring(int x, int y, int radius, int thickness, Color color) {
this(x, y, radius * 2, radius * 2, thickness, color);
}
public void draw(Graphics2D gg) {
Stroke oldStroke = gg.getStroke();
Color oldColor = gg.getColor();
gg.setColor(Color.BLACK);
gg.setStroke(new BasicStroke(getThickness()));
gg.drawOval(getX() + getThickness() / 2, getY() + getThickness() / 2, getWidth() - getThickness(),
getHeight() - getThickness());
gg.setColor(getColor());
gg.setStroke(new BasicStroke(getThickness() - 2));
gg.drawOval(getX() + getThickness() / 2, getY() + getThickness() / 2, getWidth() - getThickness(),
getHeight() - getThickness());
gg.setStroke(oldStroke);
gg.setColor(oldColor);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getThickness() {
return thickness;
}
public void setThickness(int thickness) {
this.thickness = thickness;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
}
Does anyone know how to draw solid lines when the mouse is rapidly moved?
When I move the mouse slowly the line is drawn solid, but when the mouse is moved quickly the line is drawn like a dotted line, as shown here.
The code to draw the lines is currently this:
private final class MouseL extends MouseAdapter implements MouseMotionListener
{
#Override
public void mouseClicked(MouseEvent e)
{
Point p = e.getPoint();
int half = brushDiameter / 1200;
Graphics2D g = getImage().createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setPaint(getColor());
g.fillOval(p.x - half, p.y - half, brushDiameter, brushDiameter);
g.dispose();
repaint(p.x - half, p.y - half, brushDiameter, brushDiameter);
}
#Override
public void mouseDragged(MouseEvent e)
{
mouseClicked(e);
}
But i would like to change it so that the line appears solid. Any help to achieve this is greatly appreciated thanks.
Simple: If you want to draw curves, don't draw individual points or ovals. Instead connect adjacent points with a line using g.drawLine(...). If you want a thicker curve, change the Graphics2D's Stroke width.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawCurve extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color CURRENT_PTS_COLOR = Color.BLUE.brighter().brighter();
public static final Color IMG_COLOR = Color.RED;
public static final Stroke IMG_STROKE = new BasicStroke(4f);
private static final Color FILL_COLOR = Color.WHITE;
private BufferedImage img = null;
private List<Point> currentPts = new ArrayList<>();
public DrawCurve() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setBackground(FILL_COLOR);
g2.clearRect(0, 0, PREF_W, PREF_H);
g2.dispose();
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
Graphics2D g2 = (Graphics2D) g;
g2.setColor(CURRENT_PTS_COLOR);
for (int i = 1; i < currentPts.size(); i++) {
int x1 = currentPts.get(i - 1).x;
int y1 = currentPts.get(i - 1).y;
int x2 = currentPts.get(i).x;
int y2 = currentPts.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
currentPts = new ArrayList<>();
currentPts.add(e.getPoint());
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
currentPts.add(e.getPoint());
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
currentPts.add(e.getPoint());
Graphics2D g2 = img.createGraphics();
g2.setColor(IMG_COLOR);
g2.setStroke(IMG_STROKE);
for (int i = 1; i < currentPts.size(); i++) {
int x1 = currentPts.get(i - 1).x;
int y1 = currentPts.get(i - 1).y;
int x2 = currentPts.get(i).x;
int y2 = currentPts.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.dispose();
currentPts.clear();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawCurve");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawCurve());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This is my code:
package com.Bench3.simplyMining;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Keying extends JPanel {
private static final long serialVersionUID = 1L;
public Rectangle miningRock;
public Rectangle Rocks;
public int mRockW = 150;
public int mRockH = 100;
public long rocks = 0;
public String rockAmount = rocks + " Rocks";
public Keying(Display f, Images i){
miningRock = new Rectangle(0, 658, mRockW, mRockH);
Rocks = new Rectangle(1024, 0, 0, 0);
f.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e){
}
public void keyReleased(KeyEvent e){
/* if(e.getKeyCode() == KeyEvent.VK_A){
left = false;
} -- Used for setting key combinations and stuff */
}
});
}
public void paintComponent(Graphics g){
if(Main.f.i.imagesLoaded){
super.paintComponent(g);
g.drawImage(Main.f.i.miningRock, miningRock.x, miningRock.y, miningRock.width, miningRock.height, null);
g.drawString(rockAmount, Rocks.x, Rocks.y);
this.setBackground(Color.WHITE); // Sets background color
// g.setColor(Color.WHITE); -- Used for setting colors
repaint();
}
}
}
I've been trying to insert the string "rockAmount" into the rectangle "Rocks" using g.drawString(), but when I try it doesn't output the string inside of the rectangle. What am I doing wrong?
EDIT: Solved, thanks to Paul for providing the answer.
The y position of text represents the base line minus the font ascent. This means that it appears that text is rendered above the y position.
When rendering text, you need to take into consideration the FontMetrics of the text and font.
For example, the following renders the text at the rectangles x/y position on the left and then calculates the x/y position so it can centre the text within the give rectangle.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestTextRect {
public static void main(String[] args) {
new TestTextRect();
}
public TestTextRect() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int mRockW = 150;
int mRockH = 100;
int x = ((getWidth() / 2) - mRockW) / 2;
int y = (getHeight() - mRockH) / 2;
badRect(g2d, x, y, mRockW, mRockH);
x = (getWidth() / 2) + (((getWidth() / 2) - mRockW) / 2);
goodRect(g2d, x, y, mRockW, mRockH);
g2d.dispose();
}
protected void badRect(Graphics2D g2d, int x, int y, int mRockW, int mRockH) {
g2d.drawRect(x, y, mRockW, mRockH);
String text = "rockAmount";
g2d.drawString(text, x, y);
}
protected void goodRect(Graphics2D g2d, int x, int y, int mRockW, int mRockH) {
g2d.drawRect(x, y, mRockW, mRockH);
FontMetrics fm = g2d.getFontMetrics();
String text = "rockAmount";
x = x + ((mRockW - fm.stringWidth(text)) / 2);
y = (y + ((mRockH - fm.getHeight()) / 2)) + fm.getAscent();
g2d.drawString(text, x, y);
}
}
}
Take a look at Working with text APIs for more details
You should avoid calling repaint or any method that might call repaint from within your paintXxx methods. Because of the way painting is scheduled in Swing, this will set up an infinite loop that will eventually consume you CPU cycles and make your application unresponsive...
You should, also, always call super.paintComponent, regardless of what ever else you want to do...
I would also recommend Key bindings over KeyListener as it provides better control over the focus level
You need to add to x and y position 1/2 of the width and height of the rectangle.
g.drawString(rockAmount,Rocks.x+rectangleWidth/2,Rocks.y+rectangleHeight/2);