I'm working on a uni project which is to create a dice face using 2d graphics shapes. I have got that all done but I have a problem: I want my shape to change size when I adjust the window size, instead of just staying still also so that it stays in the middle.
I thought I could set the position so that is was center tried this but didn't work. I'm not sure but would I have to write co-ordinates so that its changes size with the window? Any help with both problems would be great.
GUI setup code
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class DiceSimulator {
public static void main(String[] args) {
JFrame frame = new JFrame("DiceSimulator");
frame.setVisible(true);
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
draw object = new draw();
frame.add(object);
frame.setLocationRelativeTo(null);
object.drawing();
}
}
Painting code
import javax.swing.*;
import java.awt.*;
//import java.util.Random;
public class draw extends JComponent {
public void drawing() {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Random1 random = new Random1();
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.BLACK);
Rectangle box = new Rectangle(115, 60, 150, 150);
g2.fill(box);
g.setColor(Color.WHITE);
g.fillOval(145, 75, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(205, 75, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(145, 115, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(205, 115, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(145, 155, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(205, 155, 30, 30);
}
}
In the paintComponent() method you need to use
int width = getSize().width;
int height = getSize().height;
to get the current size of the component as it changes size when the frame changes size. Then based on this current size you can draw your components. This means that you can't hardcode the values in your drawing methods.
If you want to shift all the drawing coordinates with one command then you can use:
g.translate(5, 5);
at the top of the method. Then all hardcoded (x, y) values will be adjusted by 5 pixels each. This will allow you to change the centering of the drawing.
Related
I'm having issues getting a rectangle drawn when the mouse is clicked on screen in my JFrame. I've tried a few different methods and the closest I've gotten is just getting the coordinates appearing. Any drawing seems to be ignored for some reason.
package pathfinder;
import java.awt.BorderLayout;
import java.awt.Canvas;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
public class forLoopDesign extends JPanel{
//offsets for hard-coded path
int hOffset = 40;
int vOffset = 40;
//check if adjacent block is wall
boolean wall = false;
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
//draw start and end points
g.setColor(Color.magenta);
g.fillRect(0, 0, 40, 40);
g.setColor(Color.white);
g.drawString("Start", 7, 24);
g.setColor(Color.red);
g.fillRect(720, 720, 40, 40);
g.setColor(Color.white);
g.drawString("Finish", 724, 744);
//draw grid
g.setColor(Color.black);
for(int i=0; i<760; i+=40){
for(int j=0; j<800; j+= 40){
g.drawRect(i, j, 40, 40);
}
}
//draw hard-coded path
g.setColor(Color.cyan);
for(int i=0; i< 17; i++){
g.fillRect(hOffset + 1, vOffset + 1, 39, 39);
hOffset += 40;
vOffset += 40;
}
//check for mouse click, print coordinates
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.println("(x, y) of mouse click= (" + x + ", " + y + ")");
g.setColor(Color.orange);
g.fillRect(200,200,40,40);
}
});
}
//draw wall at mouse click location
public void highlightSquare(int x, int y, Graphics g){
wall = true;
System.out.println("wall value=" + wall);
g.setColor(Color.black);
g.fillRect(x, y, 40, 40);
}
//initialize jframe properties
public static void main(String[] args) {
JFrame f = new JFrame();
f.getContentPane().add(new forLoopDesign(), BorderLayout.CENTER);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(767, 790);
f.setVisible(true);
f.setResizable(false);
f.setLocationRelativeTo(null);
}}
My end goal is to be able to draw rectangles on the screen to create a maze like screen and have the program navigate its way through it (cyan squares). This is an ambitious project and I realize I've probably made many mistakes so far so feel free to add any other criticisms you might have. Thanks in advance
All custom painting needs to be done in the paintComponent(..) method.
So in your mouseClick logic you need to save the point where you clicked in an ArrayList and invoke repaint(). Then the paintComponent(...) method needs to iterate through the ArrayList to paint the rectangle at the given point.
See the DrawOnComponent example from Custom Painting Approaches for a working example of this approach.
I need to be able to display filled rectangles for the program i am creating, however the following code produces the following GUI with only the black text 'test' after calling start then change, could anyone explayin why please?
package core;
import java.awt.Color;
import java.awt.Graphics2D;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class GUI extends JFrame{
private Graphics2D g;
private int[][][] clickable;
public void start(){
this.setSize(500, 500);
this.setTitle("Placeholder");
this.setVisible(true);
g = (Graphics2D) this.getGraphics();
}
public void change(String[] fields, int type[], boolean forwards){
g.setColor(new Color(28,35,57));
g.drawRect(0, 0, 100, 100);
g.drawRect(50, 50, 150, 150);
g.fillRect(0, 0, 100, 100);
g.drawString("test", 300, 300);
}
}
And here is what it looks like ..
Drawing on Swing components (like JFrame) works only in onPaint event.
The event can be fired using repaint() method.
This event fires automatically when frame needs to be painted.
To implement this event behavior override paint() method.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
public class GUI extends JFrame{
private Graphics2D g;
public void start(){
this.setSize(500, 500);
this.setTitle("Placeholder");
this.setVisible(true);
}
public void change(){
g.setColor(new Color(28,35,57));
g.drawRect(0, 0, 100, 100);
g.drawRect(50, 50, 150, 150);
g.fillRect(0, 0, 100, 100);
g.drawString("test", 300, 300);
}
public void paint(Graphics g2d){
g = (Graphics2D) g2d;
change();
}
public static void main(String[] args){
GUI frame = new GUI();
frame.start();
}
}
This example draws a simple PolyLine.
Is it possible to draw an outline around this PolyLine in red.
Not a single large red square but one that outlines the original PolyLine by 3-5 points away from all areas.
Some calculations were attempted and work for a fixed value, but when the PolyLine values are random, the algorithm doesn't always work as the next section of the line could turn right instead of left or up instead of down.
You almost have to look 2-3 points ahead to know if you are going to have add or subtract.
Is there an easier way to do it?
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PolyLine extends JPanel
{
public void paint(Graphics g) {
int[] xs = {25, 125, 85, 75, 25, 65, };
int[] ys = {50, 50, 100, 110, 150, 100};
BasicStroke traceStroke = new BasicStroke (1);
Graphics2D gc = (Graphics2D) g.create();
gc.setStroke(traceStroke);
gc.setColor(Color.BLUE);
gc.drawPolyline(xs, ys, 6);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new PolyLine());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(20,20, 1500,1500);
frame.setVisible(true);
}
}
First, a remark: It's usually preferable to have the geometric primitives as a Shape. The drawPolyline function (which uses these odd integer array coordinates) is somewhat out-dated. Creating the polyline as a Path2D is far more flexible.
For the task that you described, it will also be necessary to convert the polyline coordinates into a Path2D (if you switched to Path2D anyhow, you could omit this conversion step).
When you have the polyline as a Shape, the task is rather simple: You can create a stroked version of this shape, using a BasicStroke with the desired thickness and cap/join characteristics, by calling BasicStroke#createStrokedShape. This shape will basically be the shape of a "thick line". In order to avoid artifacts at the joins, you can create an Area from this Shape, and then draw this area.
So in the end, painting the actual outline is done with 2 lines of code, and the result is as follows:
But the MCVE here, for completeness:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeOutlineTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new ShapeOutlineTestPanel());
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ShapeOutlineTestPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
int[] xs = { 25, 125, 85, 75, 25, 65, };
int[] ys = { 50, 50, 100, 110, 150, 100 };
BasicStroke traceStroke = new BasicStroke(1);
g.setStroke(traceStroke);
g.setColor(Color.BLUE);
g.drawPolyline(xs, ys, 6);
Path2D path = new Path2D.Double();
for (int i = 0; i < xs.length; i++)
{
if (i == 0)
{
path.moveTo(xs[i], ys[i]);
}
else
{
path.lineTo(xs[i], ys[i]);
}
}
g.setColor(Color.RED);
BasicStroke stroke = new BasicStroke(
10.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g.draw(new Area(stroke.createStrokedShape(path)));
}
}
So I am new to gui stuff and I want to make a simple program to print a circle to represent the sun, and then near it I want to print another circle to represent a planet. My issue is when I added the method paintPlanet, all that is returned in the gui window is blank screen now. Even when I commented paintPlanet out, the circle for the sun will not print and I am left with a blank window. Can someone help me figure out where I went wrong how to fix it so both circles will print? I am new to GUI things so be easy on me :)
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class PlanetsLogic extends JPanel
{
private static final long serialVersionUID = 1L;
public void paintSun(Graphics g)
{
super.paintComponent(g);
//create circle and fill it as yellow to represent the sun
g.setColor(Color.YELLOW);
g.drawOval(100, 75, 75, 75);
g.fillOval(100, 75, 75, 75);
} //end paintSun
public void paintPlanet(Graphics g)
{
super.paintComponent(g);
//create circle and fill it as yellow to represent the orbiting planet
g.setColor(Color.BLUE);
g.drawOval(75, 75, 75, 75);
g.fillOval(75, 75, 75, 75);
}//end paintPlanet
}//end class PlanetsLogic
MAIN:
import javax.swing.JFrame;
public class OrbitingPlants_main
{
public static void main(String[] args)
{
PlanetsLogic planet = new PlanetsLogic();
JFrame frame = new JFrame();
frame.setTitle("Orbiting Planets");
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(planet); //add panel onto frame
frame.setVisible(true);
}
}
Your paintSun and paintPlanet methods will never be magically called. Instead your JPanel needs to override the paintComponent method as all drawing is done there. You can even call your paintSun and paintPlanet methods from within paintComponent, but I would recommend calling the super.paintComponent(g) only once and only from within your paintComponent method override itself.
e.g.,
// use #Override to ask the compiler to check if this method is a true override
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // HERE!
paintSun(g);
paintPlanet(g);
}
public void paintSun(Graphics g) {
// super.paintComponent(g); // nope, not here!
//create circle and fill it as yellow to represent the sun
g.setColor(Color.YELLOW);
g.drawOval(100, 75, 75, 75);
g.fillOval(100, 75, 75, 75);
} //end paintSun
public void paintPlanet(Graphics g) {
// super.paintComponent(g); // NO don't call this here
//create circle and fill it as yellow to represent the orbiting planet
g.setColor(Color.BLUE);
g.drawOval(75, 75, 75, 75);
g.fillOval(75, 75, 75, 75);
}//end paintPlanet
I have been trying to draw lines in swing and get some laser-like effects using gradients. I want to apply the gradient on the line's width (ex: a red line core fading to orange on the edges). The problem is when I draw at an angle, I want to somehow apply the same angle to the gradient.
public void paintComponent(Graphics g) {
Graphics2D en = (Graphics2D) g;
GradientPaint gp = new GradientPaint(25, 25, Color.red, 15, 25,
Color.orange, true);
en.setPaint(gp);
en.setStroke(new BasicStroke(4.0F));
en.drawLine(10, 10, 800, 600);
}
One easy trick to get away with this, is to call setTransform() on the Graphics2D. This will do for you the rotation of both the line and the gradient at once. If you don't want to do this, then you will have to rotate the gradient itself (so basically pt1 and pt2 of the Gradient manually, ie, you need to compute them according to the rotation you have applied to your line).
Here is a small example illustrating my first idea. Just slide the ticker at the bottom to watch the line (and the gradient) rotate around the center of the panel:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestPanel extends JPanel {
private double angle = 0;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D en = (Graphics2D) g;
AffineTransform tf = AffineTransform.getTranslateInstance(-getWidth() / 2, -getHeight() / 2);
tf.preConcatenate(AffineTransform.getRotateInstance(Math.toRadians(angle)));
tf.preConcatenate(AffineTransform.getTranslateInstance(getWidth() / 2, getHeight() / 2));
en.setTransform(tf);
GradientPaint gp = new GradientPaint(25, 25, Color.red, 15, 25, Color.orange, true);
en.setPaint(gp);
en.setStroke(new BasicStroke(4.0F));
en.drawLine(400, 400, 600, 600);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
public void setAngle(double angle) {
this.angle = angle;
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
final TestPanel panel = new TestPanel();
final JSlider slider = new JSlider(0, 360);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
panel.setAngle(slider.getValue());
}
});
slider.setValue(0);
frame.add(panel);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}