I have been trying to develop a program that has human stick figure with an arrow. So the issue here is, the drawing under paintComponent doesn't get displayed when ImageIcon as background is added. How do I get to display the painting on top of the background image. My coding is as follows.
public class Drawing
{
public Drawing()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//f.getContentPane().setBackground(new Color(204,229,255));
f.getContentPane().add(new ArrowPanel());
f.setSize(1000,600);
//f.setLocation(200,200);
f.setLayout(new BorderLayout());
f.setContentPane(new JLabel(new ImageIcon("/Users/marian/NetBeansProjects/Drawing/src/drawing/wall.jpg")));
f.setLayout(new FlowLayout());
f.setVisible(true);
}
public static void main(String[] args)
{
new Drawing();
}
}
class ArrowPanel extends JPanel
{
double phi;
int barb;
public ArrowPanel()
{
phi = Math.toRadians(40);
barb = 30;
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth();
int h = getHeight();
Point sw = new Point(w/8, h*7/8);
Point ne = new Point(w*7/8, h/8);
g2.draw(new Line2D.Double(sw, ne));
//drawArrowHead(g2, sw, ne, Color.red);
drawArrowHead(g2, ne, sw, Color.blue);
Ellipse2D.Double head = new Ellipse2D.Double(90,60,20,20);
g2.draw(head);
Line2D.Double body=new Line2D.Double(100,80,100,120);
g2.draw(body);
Line2D.Double arm1=new Line2D.Double(100,100,80,100);
g2.draw(arm1);
Line2D.Double arm2=new Line2D.Double(100,100,120,75);
g2.draw(arm2);
Line2D.Double leg1=new Line2D.Double(100,120,85,135);
g2.draw(leg1);
Line2D.Double leg2=new Line2D.Double(100,120,115,135);
g2.draw(leg2);
}
private void drawArrowHead(Graphics2D g2, Point tip, Point tail, Color color)
{
g2.setPaint(color);
double dy = tip.y - tail.y;
double dx = tip.x - tail.x;
double theta = Math.atan2(dy, dx);
//System.out.println("theta = " + Math.toDegrees(theta));
double x, y, rho = theta + phi;
for(int j = 0; j < 2; j++)
{
x = tip.x - barb * Math.cos(rho);
y = tip.y - barb * Math.sin(rho);
g2.draw(new Line2D.Double(tip.x, tip.y, x, y));
rho = theta - phi;
}
}
}
Im still learning Java, could someone help to solve this problem. Thank you.
If you want to use different layers in your Swing Application, you should use a LayeredPane instead of a JPanel. And you should avoid setting different LayoutManagers. This code sets the last LayoutManager, so the first line is useless:
f.setLayout(new BorderLayout());
f.setContentPane(new JLabel(new ImageIcon("/Users/marian/NetBeansProjects/Drawing/src/drawing/wall.jpg")));
f.setLayout(new FlowLayout());
Here is an article on how to use LayeredPanes Tutorial
You replace your ArrowPanel with the call of setContentPane():
f.getContentPane().add(new ArrowPanel());
...
f.setContentPane(new JLabel(new ImageIcon("/Users/marian/NetBeansProjects/Drawing/src/drawing/wall.jpg")));
Try changing the order of these two statements, EG put the getContentPane() call after the setContentPane().
Related
Hello I would like to prevent graphics drawing and drawing again but I don't know how to do, I just want my panel delete all painted graphics and restart with same code. I tried some methods posted here but no one does the job.
public class Main extends JPanel implements ActionListener {
Timer timer;
private double angle = 444;
private double scale = 1;
private double delta = 0.0001;
RoundRectangle2D.Float r = new RoundRectangle2D.Float();
int counter = 0;
public Main() {
timer = new Timer(55, this);
timer.start();
}
public void paint(Graphics g) {
counter++;
int h = getHeight();
int w = getWidth();
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(randomNumber(0, 155), randomNumber(0, 255),randomNumber(0, 155), randomNumber(0, 255)));
drawCircles(g2d, getWidth()/2, getHeight()/2, 250);
if(counter > 200){
g2d.clearRect (0, 0, getWidth(), getHeight());
super.paintComponent(g2d);
counter = 0;
}
}
public int randomNumber(int min, int max) {
int c = new Random().nextInt((max - min) + 1);
return c;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setUndecorated(true);
Dimension dim = new Dimension(Toolkit.getDefaultToolkit()
.getScreenSize().width, Toolkit.getDefaultToolkit()
.getScreenSize().height);
frame.setSize(dim);
frame.setLocation(0, 0);
frame.setBackground(new Color(0, 0, 0, 255));
frame.add(new Main());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void drawCircles(Graphics graphics, int xMid, int yMid, int radius) {
// end recursion
if(radius < 5)
return;
// Draw circle
// start recursion
//left
drawCircles(graphics, xMid-radius, yMid, radius / 2);
((Graphics2D) graphics).rotate(angle);
graphics.drawOval(xMid - radius, yMid - radius, radius * 2, radius * 2);
//right
drawCircles(graphics, xMid+radius, yMid, radius / 2);
graphics.drawOval(xMid - radius, yMid - radius, radius * 2, radius * 2);
((Graphics2D) graphics).rotate(angle);
((Graphics2D) graphics).rotate(angle);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) graphics).setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
}
public void actionPerformed(ActionEvent e) {
if (scale < 0.01)
delta = -delta;
else if (scale > 0.99)
delta = -delta;
scale += delta;
angle += 0.001;
repaint();
}
}
I am not sure I understand you fully, but you can use a JToggleButton (for example) where is the toggle button is down it prevents drawing. I can see something like this inside your drawCircles() method:
void drawCircles(Graphics graphics, int xMid, int yMid, int radius)
{
if(!toggleBtn.isSelected() // the toggle button is pressed
{
// draw something
}
}
In your example, you are drawing two circles and two ovals. If I understood you correctly, you want to be able to pause in the middle of the method, for example, and only draw the first circle. Then, at some point, you want to continue drawing the two ovals and the remaining circle. Unfortunately, you cannot do that. You cannot stop (or pause) a method in the middle of it.
Methods have to execute to completion (whether to the end, or an exception is thrown). However, you can create some kind of task to draw ONE shape (for example, a circle). If you create multiple tasks, you can draw many circles. To accomplish this, you will need to learn about Concurrency and probably about Java Tasks. You can have these tasks execute in some kind of order and because of concurrency, you could pause and resume these drawing tasks the way I think you would want.
After I compiled the previous code it worked but now I cant get the circumference of the circles to change to a different color of whats inside the circle. Any suggestions.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Dimension d = getSize();
for(int i = 0; i < 100; ++i)
{
Color color = new Color(generator.nextInt(255), generator.nextInt(255), generator.nextInt(255));
g.setColor(color);
int circleSize = generator.nextInt(d.width / 4);
int x = generator.nextInt(d.width - circleSize);
int y = generator.nextInt(d.height - circleSize);
g.fillOval(x, y, circleSize, circleSize);
color = new Color(generator.nextInt(255), generator.nextInt(255), generator.nextInt(255));
g.setColor(color);
g.drawArc(x, y, circleSize, circleSize, 0, 360);
}
}
}
Simply add frame.setPreferredSize(new Dimension (700,500)); right before packing the frame. Those two methods are meant to be used in conjunction. In addition move the adding of the JPanel to after the size is set, this improves code readability and makes more sense if you think about the order of the statements.
public static void main (String[] args)
{
JFrame frame = new JFrame ("CircleFrame");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
CircleFramePanel panel = new CircleFramePanel();
frame.setPreferredSize(new Dimension (700,500)); // add it here
frame.pack();
frame.getContentPane().add(panel);
frame.setVisible(true);
}
I am trying to display two rotating wheels with diameter 512untis at different rates but i am not able to remove the previous drawn image graphics and set the rotated graphics at the correct position.
For now i am doing a rotation with arbitrary angle.
I tried affineTransform and got the rotations but it was weird like all pixels spread away.
Im using a while loop with thread.sleep(). The following is the code :
//The drawSmallCircle and drawBigCircle return two images.
class MyFramePart2 extends JFrame
{
String name;
JPanel big_obj_panel,small_obj_panel;
JLabel bigLabel,smallLabel;BufferedImage imgRet,imgRetSmall;
static double radians,angle,rev,fps,smallAngle,smallRadians;
int numLines,i=0;
MyFramePart2(String frameName,int numStrokes,double revolutions,double frameps)
{
numLines=numStrokes;
smallAngle=smallRadians=angle=radians=Math.toRadians(360/numLines);
rev=revolutions;
fps=frameps;
setSize(1240,720);
setLocation(0,0);
setLayout(null);
getContentPane().setBackground(Color.WHITE);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
big_obj_panel=new JPanel();
big_obj_panel.setLayout(null);
big_obj_panel.setSize(512,512);
big_obj_panel.setLocation(100,100);
big_obj_panel.setBackground(Color.white);
add(big_obj_panel);
imgRet=drawBigCircle();
bigLabel = new JLabel(new ImageIcon(imgRet));
bigLabel.setLayout(null);
bigLabel.setLocation(0,0);
bigLabel.setSize(512,512);
bigLabel.setOpaque(true);
bigLabel.setBackground(Color.white);
big_obj_panel.add(bigLabel);
small_obj_panel=new JPanel();
small_obj_panel.setLayout(null);
small_obj_panel.setSize(512,512);
small_obj_panel.setLocation(700,100);
small_obj_panel.setBackground(Color.white);
add(small_obj_panel);
imgRetSmall=drawSmallCircle();
smallLabel = new JLabel(new ImageIcon(imgRetSmall));
smallLabel.setLayout(null);
smallLabel.setLocation(0,0);
smallLabel.setSize(512,512);
smallLabel.setOpaque(true);
smallLabel.setBackground(Color.white);
small_obj_panel.add(smallLabel);
setVisible(true);
while(i!=5) // suppose to be while true, just checking
{
setVisible(true);
bigLabel.setIcon(new ImageIcon(imgRet));
smallLabel.setIcon(new ImageIcon(imgRetSmall));
try{
Thread.sleep(10);
}
catch(Exception e)
{}
i++;
}
}
#Override
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d=(Graphics2D)g;
g2d.translate(256,256);
g2d.rotate(Math.toRadians(20));
g2d.translate(-256,-256);
g2d.drawImage(imgRet,0,0,null);
g2d.dispose();
super.paint(g);
g2d=(Graphics2D)g;
g2d.translate(256,256);
g2d.rotate(Math.toRadians(30));
g2d.translate(-256,-256);
g2d.drawImage(imgRetSmall,0,0,null);
g2d.dispose();
}
public static BufferedImage drawBigCircle()
{
BufferedImage img=new BufferedImage(512,512,BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d=img.createGraphics();
g2d.setColor(Color.black);
g2d.drawOval(0,0,511,511);
Line2D l2d;
while(angle <= 2*Math.PI)
{
l2d=new Line2D.Double(256,256,256+256*Math.cos(angle),256+256*Math.sin(angle));
g2d.draw(l2d);
angle=angle+radians;
}
return img;
}
}
First rule of Swing. Don't block the Event Dispatching Thread. Doing so will make you application look like it's hung and prevent the EDT from processing any repaint requests.
This means, you need some way to schedule updates that doesn't block the EDT
Second rule of Swing. Don't create or modify any UI component from any thread other then the EDT.
Generally, you should avoid overriding the paint method of top level containers like JFrame, apart from everything else, they're not double buffered, meaning your painting will flicker as it's updated. Instead, you should use one of the Swing containers, like JPanel
There are lots of different ways to achieve this. Basically, here I've used three lists, but if I was serious, I would create an object that could maintain all the required information (image, angle an delta)
In order to achieve the actual animation, I've used a javax.swing.Timer. This will trigger an event at least every n periods, but more importantly, it does it within the context of the Event Dispatching Thread. This ensures that all the changes made to the angles are done in way that will prevent any possibility of painting occurring while we're updating the values...
This example rotates the three images at different (random) speeds...
public class TestRotation {
public static void main(String[] args) {
new TestRotation();
}
public TestRotation() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class AnimationPane extends JPanel {
private List<BufferedImage> images;
private List<Double> angles;
private List<Double> speed;
public AnimationPane() {
images = new ArrayList<>(5);
images.add(createWheel(50, 4));
images.add(createWheel(50, 3));
images.add(createWheel(50, 6));
angles = new ArrayList<>();
speed = new ArrayList<>();
for (int index = 0; index < images.size(); index++) {
angles.add(0d);
speed.add(Math.random() * 5d);
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int index = 0; index < angles.size(); index++) {
double angle = angles.get(index);
double delta = speed.get(index);
angle += delta;
angles.set(index, angle);
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = 0;
int y = 0;
for (int index = 0; index < images.size(); index++) {
BufferedImage image = images.get(index);
double angle = angles.get(index);
// This is important. Basically we going to grab a isolated snap shot
// of the current graphics context. This means any changes we make
// will not affect the original graphics context (other then painting)
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
g2d.setTransform(at);
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
x += image.getWidth();
}
}
}
protected Point2D calculateOutterPoint(int radius, double angel) {
int x = Math.round(radius / 2);
int y = Math.round(radius / 2);
double rads = Math.toRadians((angel + 90));
// This determins the length of tick as calculate from the center of
// the circle. The original code from which this derived allowed
// for a varible length line from the center of the cirlce, we
// actually want the opposite, so we calculate the outter limit first
double fullLength = (radius / 2d);
// Calculate the outter point of the line
double xPosy = (x + Math.cos(rads) * fullLength);
double yPosy = (y - Math.sin(rads) * fullLength);
return new Point2D.Double(xPosy, yPosy);
}
public BufferedImage createWheel(int radius, int spokes) {
BufferedImage img = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
g2d.drawOval(0, 0, radius - 1, radius - 1);
Point2D center = new Point2D.Double(radius / 2d, radius / 2d);
double angle = 360d / spokes;
for (int index = 0; index < spokes; index++) {
Point2D p = calculateOutterPoint(radius, index * angle);
g2d.draw(new Line2D.Double(center, p));
}
g2d.dispose();
return img;
}
}
im trying to make a swing app that draws a function's graph(simple for now ex. x+2)
but i'm having problems to make mathematical coordinates of my points depending on screen coordinates .
I want it to simply draw a line that goes from P1(0,1) to P2(1,2) inside my graph.
here is my code :
import java.awt.*;
import javax.swing.*;
public class Graph extends JPanel {
protected void paintComponent(Graphics g) {
int YP1,YP2;
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
int h = getHeight();
int w = getWidth();
// Draw axeX.
g2.draw(new Line2D.Double(0, h/2, w, h/2)); //to make axisX in the middle
// Draw axeY.
g2.draw(new Line2D.Double(w/2,h,w/2,0));//to make axisY in the middle of the panel
//line between P1(0,1) and P2(1,2) to draw function x+1
Point2D P1 = new Point2D.Double(w/2,(h/2)+1);
Point2D P2 = new Point2D.Double((w/2)+1,(h/2)+2);
g2.draw(new Line2D.Double(P1,P2));
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Graphe());
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
}
}
thanks.
import java.awt.*;
import javax.swing.*;
public class Graph extends JPanel {
private static final int UNIT = 20;
protected void paintComponent(Graphics g) {
int YP1,YP2;
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
int h = getHeight();
int w = getWidth();
// Draw axeX.
g2.draw(new Line2D.Double(0, h/2, w, h/2)); //to make axisX in the middle
// Draw axeY.
g2.draw(new Line2D.Double(w/2,h,w/2,0));//to make axisY in the middle of the panel
//line between P1(0,1) and P2(1,2) to draw function x+1
Point2D P1 = new Point2D.Double(w/2,(h/2)+ UNIT);
Point2D P2 = new Point2D.Double((w/2)+ UNIT,(h/2)+ 2*UNIT); //considering 20 = 1 unit in your syste,
g2.draw(new Line2D.Double(P1,P2));
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Graphe());
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
}
}
Tryout this code read comments to get the solution
The center of your coordinate system (0,0) is painted at (w/2, h/2). The missing part is the scale, in other words: how many pixels make one unit on the x axis and y axis.
So usually you multiply your unit value by the scaling factor (like 10, if you want 10 pixel per unit) and add the offset of the axis from the left or lower boundary. The annoying part is the (0,0) on screen coordinates is the upper left corner and height counts from top to bottom (reversed y axis). That makes it a bit more complicated:
xOnScreenPos = (xUnit * xScale) + xScaleOffset;
yOnScreenPos = -(yUnit * yScale) + yScaleOffset;
I've got a JPanel I'm drawing onto and I'm trying to get it so that you can use the mouse wheel to "zoom" in and out by modifying the variable scale. Right now what happens is you can zoom in or out but all of the drawing shifts to the right and downwards when you zoom in and then back up and left when zooming out.
I want to make it so that it adjusts as if you were zooming in on the point at the center of the JPanel but I can't figure out how to calculate the right offset to translate by…
Anybody got an idea of how to do this, or even better a cleaner way of achieving this whole pan and zoom ability?
I am basically plotting a bunch of coordinates that came from a system where 0,0 is in the lower left corner and fall between the bounds:
xMin = 661208
xMax = 662618
yMin = 4291657
yMax = 4293285
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
double scale = details.getScale();
double xOffset = details.getxOffset();
double yOffset = details.getyOffset();
g2.scale(scale, -scale);
// Dividing offset by scale makes panning 1:1 with the cursor still. yMax to account for the
// fact we needed to flip around the y axis to make it right-side up.
g2.translate((-xMin + xOffset / scale), (-yMax + yOffset / scale));
// Code to draw stuff goes here. It uses coordinates between xMin-xMax and yMin-yMax to draw.
.
.
.
}
Here is an example with the wheel:
public class GraphicsOnly extends JPanel implements MouseWheelListener {
Shape[] shapes;
Dimension size;
double scale = 1.0;
private static int source = 100;
public GraphicsOnly() {
addMouseWheelListener(this);
size = new Dimension(10,10);
setBackground(new Color(240,200,200));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if(shapes == null) initShapes();
// Keep shapes centered on panel.
double x = (getWidth() - scale*size.width)/2;
double y = (getHeight() - scale*size.height)/2;
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.scale(scale, scale);
g2.setPaint(Color.blue);
g2.draw(at.createTransformedShape(shapes[0]));
g2.setPaint(Color.green.darker());
g2.draw(at.createTransformedShape(shapes[1]));
g2.setPaint(new Color(240,240,200));
g2.fill(at.createTransformedShape(shapes[2]));
g2.setPaint(Color.red);
g2.draw(at.createTransformedShape(shapes[2]));
}
public Dimension getPreferredSize() {
int w = (int)(scale*size.width);
int h = (int)(scale*size.height);
return new Dimension(w, h);
}
private void initShapes() {
shapes = new Shape[3];
int w = getWidth();
int h = getHeight();
shapes[0] = new Rectangle2D.Double(w/16, h/16, w*7/8, h*7/8);
shapes[1] = new Line2D.Double(w/16, h*15/16, w*15/16, h/16);
shapes[2] = new Ellipse2D.Double(w/4, h/4, w/2, h/2);
size.width = w;
size.height = h;
}
public static void main(String[] args) {
GraphicsOnly app = new GraphicsOnly();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(app));
f.setSize(400, 400);
f.setLocation(200,200);
f.setVisible(true);
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
// TODO Auto-generated method stub
if(e.getWheelRotation() >0){
source = source-e.getScrollAmount();
}else{
source = source+e.getScrollAmount();
}
scale = source/100.0;
System.out.println(scale);
repaint();
revalidate();
}
}