TL;DR: Drawing Canvas3D off-screen to an image, then drawing the image on the screen. View in example should continously increase its distance to the cube; instead does so only when window gets resized.
In the following sample code, the Canvas3D object does not update the scene when I change the camera transform.
It stays as it is until I resize the window, at which point it updates once and doesn't do so again until I resize again. So it only updates on a resize event.
Any help in resolving this problem would be greatly appreciated!
EDIT: Minimal Example:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Transform3D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.vecmath.Vector3d;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.SimpleUniverse;
public class eg extends JComponent implements ActionListener {
SimpleUniverse m_universe = new SimpleUniverse(new Canvas3D(SimpleUniverse.getPreferredConfiguration(), true));
JFrame frame = new JFrame();
#Override
public void actionPerformed(ActionEvent arg0) {
repaint();
}
eg() {
// setup Screen3D
m_universe.getCanvas().getScreen3D().setSize(600, 600);
m_universe.getCanvas().getScreen3D().setPhysicalScreenHeight(0.0254/90.0 * 600);
m_universe.getCanvas().getScreen3D().setPhysicalScreenWidth(0.0254/90.0 * 600);
// setup scene
BranchGroup group = new BranchGroup();
group.addChild(new ColorCube(0.5f));
m_universe.addBranchGraph(group);
m_universe.getViewingPlatform().setNominalViewingTransform();
// setup JFrame
frame.add(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.setVisible(true);
// repaint() every half second
Timer timer = new Timer(500, this);
timer.start();
}
BufferedImage render(int width, int height) {
BufferedImage returnValue = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGBA, returnValue);
buffer.setCapability(ImageComponent.ALLOW_IMAGE_WRITE);
m_universe.getCanvas().setOffScreenBuffer(buffer);
m_universe.getCanvas().renderOffScreenBuffer();
m_universe.getCanvas().waitForOffScreenRendering();
returnValue = m_universe.getCanvas().getOffScreenBuffer().getImage();
return returnValue;
}
private Transform3D m_cameraTransform = new Transform3D();
private Vector3d m_cameraPosition = new Vector3d();
#Override
public void paintComponent(Graphics target) {
// Move camera back along z.
m_universe.getViewingPlatform().getViewPlatformTransform().getTransform(m_cameraTransform);
m_cameraTransform.get(m_cameraPosition);
m_cameraPosition.z += 0.05;
m_cameraTransform.set(m_cameraPosition);
m_universe.getViewingPlatform().getViewPlatformTransform().setTransform(m_cameraTransform);
System.out.println(m_cameraPosition.z);
// Draw canvas.
Graphics2D buffer = (Graphics2D) target;
buffer.drawImage(render(getWidth(), getHeight()), null, 0, 0);
}
public static void main(String[] args) {
eg test = new eg();
}
}
Related
I've been trying to create two layered images within a JFrame using JLayeredPane however I'm not able to draw a shape using the paint() function within the Canvas class.
Here is what I wrote:
package com.baduk.main;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Game extends Canvas implements Runnable {
private ImageIcon icon;
private JLabel back;
private JLayeredPane layer;
/**
*
*/
private static final long serialVersionUID = -3441156857004256039L;
public Game() {
//Background image with depth 0
icon = new ImageIcon("C:/Users/scaraven/Downloads/baduk_board.png");
back = new JLabel(icon);
back.setBounds(0,0,icon.getIconWidth(),icon.getIconHeight());
//Create JFrame
JFrame frame = new JFrame("BADUK");
frame.setSize(new Dimension(icon.getIconWidth()+5,icon.getIconHeight()+25));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
//Create JLayeredPane
layer = new JLayeredPane();
layer.add(back,new Integer(1));
layer.add(this,new Integer(0));
frame.setContentPane(layer);
frame.setVisible(true);
}
public static void main(String[] args) {
new Game();
}
#Override
public void run() {
// TODO Auto-generated method stub
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillRect(10, 10, 100, 100);
}
}
According to what I wrote, A Jframe should be generated with an image as the background and a black rectangle on top, however I only get the image. Does anyone know what is wrong with the code I wrote and what I need to change?
It shows the line without jpanel on jframe, but it doesn't when I add it to jpanel. I've tried setting the layout manager of jpanel to null but no result. I want to use JComponents for drawing lines because I want them clickable.
Main.java file:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setBackground(Color.YELLOW);
panel.setLayout(null);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
panel.repaint();
frame.add(panel);
frame.setVisible(true);
}
}
class Line extends JComponent {
private final Point2D start, end;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(2.0F));
g2.draw(new Line2D.Double(start,end));
}
public Line( Point2D start, Point2D end){
this.start = start;
this.end = end;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked");
}
});
}
}
It shows the line without jpanel on jframe, but it doesn't when I add it to jpanel
Swing components are responsible for determining their own preferred size.
When you add a component to a panel, the layout manager will then set the size/location of the component based on the rules of the layout manager.
When you add a component to the frame you really add it to the content pane of the frame which is a Jpanel which uses a BorderLayout by default. So the component is sized to fill the space available in the frame.
panel.setLayout(null);
You then added the component to a panel with a null layout. Now you are responsible for setting the size/location of the component. If you don't the size is (0, 0) so there is nothing to paint.
You should override the getPreferredSize() method of your class to return the preferred size of the component. Then layout managers can do their job.
If you really need a null layout, then the size of the component should be set in the application code, not it the Line class itself.
But now my line has a big container that listens for any clicks,
If you want hit detection then you override the contains(...) method.
Here is a basic example implementing the above suggestions:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Line extends JComponent
{
private Line2D.Double line;
public Line( Point2D start, Point2D end)
{
line = new Line2D.Double(start, end);
addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
System.out.println("mouse clicked");
}
});
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor( Color.BLUE );
g2.setStroke( new BasicStroke(2.0F) );
g2.draw( line );
}
#Override
public Dimension getPreferredSize()
{
Rectangle bounds = line.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
return new Dimension(width, height);
}
#Override
public boolean contains(int x, int y)
{
double distance = line.ptSegDist( new Point2D.Double(x, y) );
return distance < 2;
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setBackground(Color.YELLOW);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
panel.repaint();
frame.add(panel);
frame.setVisible(true);
}
}
Add custom size in Line constructor.
public Line( Point2D start, Point2D end){ ...
this.setSize(200, 200); }
Updated to fit also with painted Graph
Advice to change from JComponent to JPanel in order to see background
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setSize(300,300);
frame.add(panel);
panel.setBackground(Color.YELLOW);
panel.setLayout(null);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
frame.setVisible(true);
}
}
class Line extends JPanel {
private final Point2D start, end;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.RED);
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(2.0F));
g2.draw(new Line2D.Double(start,end));
Rectangle r = g2.getClipBounds();
System.out.println(r.x+":"+r.y);
}
public Line( Point2D start, Point2D end){
this.start = start;
this.end = end;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked at "+e.getX()+":"+e.getY());
}
});
int max_x = (int) Math.max(start.getX(), end.getX());
int max_y = (int) Math.max(start.getY(), end.getY());
System.out.println("max x="+max_y+",y="+max_y);
setSize(max_x,max_y);
setVisible(true);
setBackground(Color.GREEN);
}
}
Note: Only inside_green clicks allowed !
I am developing a small Paint tool. And I am able to load and draw Lines or Circles and other shapes on an image. Also I have an eraser tool to erase the shapes that I have drawn.
This is code for that:
g.setColor(getColor().WHITE);
g.fillRect(getXAxis() - getThickness(), getYAxis() - getThickness(), getThickness() * 2, getThickness() * 2);
My problem is that, If I have loaded an image and drawn some shapes on it. Then when I tried to erase the shapes, the image is also gets replaced with white color.
Is there any way to set the image as the background while using fillRect() to erase the shape, so that my image will be untouched.
Here is the example. To test it you need to replace my image with your background image.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
/**
* <code>PaintTryout</code>.
*
* #author smedvynskyy
*/
public class PaintPanel extends JPanel {
private Image backgroundImage;
private BufferedImage paintImage;
public PaintPanel() {
try {
// replace this image with your image
backgroundImage = ImageIO.read(new File("E:\\icons\\blackboard.png"));
paintImage = new BufferedImage(backgroundImage.getWidth(this),
backgroundImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
} catch (final Exception e) {
e.printStackTrace();
}
}
public void fillRect() {
final Graphics g = paintImage.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, 50, 50);
g.dispose();
repaint();
}
public void clearRect() {
final Graphics2D g = paintImage.createGraphics();
g.setColor(new Color(0, 0, 0, 0));
g.setComposite(AlphaComposite.Clear); // overpaint
g.fillRect(0, 0, 50, 50);
g.dispose();
repaint();
}
/**
* {#inheritDoc}
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(backgroundImage, 0, 0, this);
g.drawImage(paintImage, 0, 0, this);
}
/**
* {#inheritDoc}
*/
#Override
public Dimension getPreferredSize() {
return new Dimension(backgroundImage.getWidth(this),
backgroundImage.getHeight(this));
}
public static void main(String[] args) {
final JFrame frm = new JFrame("Tesp paint");
final PaintPanel p = new PaintPanel();
frm.add(p);
final JPanel buttons = new JPanel();
final JButton fill = new JButton("Fill Rect");
fill.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.fillRect();
}
});
final JButton clear = new JButton("Clear Rect");
clear.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.clearRect();
}
});
buttons.add(fill);
buttons.add(clear);
frm.add(buttons, BorderLayout.SOUTH);
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
}
The easy way to do this is to draw the lines in XOR mode. Then, to erase them, you just draw them again.
Currently I am having an issue whereby the Robot.png is replacing the image of my gameboard.png . I want to make it so that the robot .png is ontop of the board and be able to move the robot around the board.
Board.Java
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Board extends JPanel {
private Image gameboard;
public Board(){
initBoard();
}
private void initBoard(){
loadImage();
int w = gameboard.getWidth(this);
int h = gameboard.getHeight(this);
setPreferredSize(new Dimension(w,h));
}
private void loadImage(){
ImageIcon i = new ImageIcon("res/gameboard.png");
gameboard = i.getImage();
}
#Override
public void paintComponent(Graphics g){
g.drawImage(gameboard,0,0,null);
}
}
Player.Java
import javax.swing.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
public class Player extends JPanel {
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
ImageIcon ic = new ImageIcon("res/Robot.png");
Image image1 = ic.getImage();
g2d.drawImage(image1, 100, 100, null);
}
}
GameGUI.Java
import javax.swing.JFrame;
public class GameGui extends JFrame {
public GameGui(){
initGui();
}
public void initGui(){
add(new Board());
add(new Player());
setTitle("11+ Game");
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setSize(1240,620);
}
}
You don't need to reload the robot image every time. You should use ImageIO to load it, and save it in a member variable.
Otherwise, the way to not have the image overwrite the whole background image is to use height and width parameters with your drawImage call
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
ImageIcon ic = new ImageIcon("res/Robot.png");
Image image1 = ic.getImage();
int width = ..., height = ...;
g2d.drawImage(image1, 100, 100, width, height, null);
}
I'm trying to make a simple game in java where you can move the player (Defender) in the 4 directions. I tried to make the key detecting with a key adapter, but it doesn't work. What could be the problem (I tried to do a System.out.println at the key press to make sure that the problem isn't at the Defender)?
Code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class DefenderComponent extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private static final int WIDTH = 160;
private static final int HEIGHT = 120;
private static final int SCALE = 4;
Defender player = new Defender();
public DefenderComponent() {
Dimension size = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
setMinimumSize(size);
setMaximumSize(size);
setPreferredSize(size);
addKeyListener(new TKeyListener());
Timer timer = new Timer(5, this);
timer.start();
}
public static void main(String[] args) {
JFrame frame = new JFrame("Test2");
frame.add(new DefenderComponent());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setFocusable(true);
new DefenderComponent();
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
Image i = player.getImage();
g2d.drawImage(i, player.getX(), player.getY(), i.getWidth(this) * SCALE, i.getHeight(this) * SCALE, this);
}
public void actionPerformed(ActionEvent e) {
player.move();
repaint();
}
}
KeyEvents are only generated for the component that has focus. A JPanel is not focusable by default.
Don't use a KeyListener. Instead you should be using Key Bindings which are more flexible.
See Motion Using the Keyboard for more information and examples.
frame.addActionListener(this);
is what you missed.
that line says. this class is an ActionListener. please call this class when you receive an action.
if you want to add the ActionListener to the JPanel
public DefenderComponent() {
addActionListener(this);
....
}