1

I am trying to create a program that, using parametric equation, can draw lines between lines and create an ellipse. Here's my code:

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;

import javax.swing.JFrame;


public class test extends JFrame{

    ArrayList<Line2D> lines = new ArrayList<>();
    public test(){
        this.setTitle("test");
        this.setSize(800, 600);
        this.setLocationRelativeTo(null);
        this.setLayout(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        drawCircle(3,3.0,3.0);

        this.setVisible(true);


        System.out.println(lines.size());
    }

    public void drawCircle(int radio, double xCenter, double yCenter) {
        double t = 0;
        double xPoint;
        double yPoint;
        double xActual = xCenter;
        double yActual = yCenter + radio*Math.sin(0);
        t += 0.01;

        while(t < 360) {
            xPoint = xCenter + radio*Math.cos(t);
            yPoint = yCenter + radio*Math.sin(t);

           lines.add(new Line2D.Double(xActual, yActual, xPoint, yPoint));
           this.repaint();

            t += 0.01;
            xActual = xPoint;
            yActual = yPoint;
        }
    }

     public void paint(Graphics g) {
         super.paint(g);

         for(Line2D s : lines){
             ((Graphics2D) g).draw(s);
         }
     }
}

I am not getting any errors, but the lines don't seem to appear in the frame. What am I doing wrong?

rasperryPi
  • 377
  • 2
  • 4
  • 12

3 Answers3

2

Drawing in swing should under all circumstances be done in paintComponent(Graphics), not paint as described in this article. I'd recommend you use a JPanel or any some other nontoplevel component to paint on - which is common habit. And

...
lines.add(new Line2D.Double(xActual, yActual, xPoint, yPoint));
this.repaint();
...

This repaint call will cause some really ugly behaviour and is quite a waste of resources (36000 useless repaints per added shape).

  • What's the problem with the repaint()-method being called? – rasperryPi Aug 25 '15 at 23:56
  • @rasperryPi In your case, nothing, as the window hasn't been displayed. – MadProgrammer Aug 25 '15 at 23:59
  • *"First of all you don't set any color before painting"* - Not true. The `Graphics` context normally set to have the foreground color of the component set before the paint methods are called. This, however, might be changed by subsequent calls to the other paint methods in the chain, but in my simple test, it came out black. *"Drawing in swing should under all circumstances be done in paintComponent(Graphics)"* - While this advisable and highly recommended, there are cases where this is not true, besides, `JFrame` does not have a `paintComponent` method. – MadProgrammer Aug 26 '15 at 00:02
  • @rasperryPi in this precise example, there's actually no problem, as MadProgrammer already commented. But if you reuse this code to display more shapes, you'll have 36000 repaints, if you simply add a single shape, which is useless and quite a useless waste of resources –  Aug 26 '15 at 00:02
  • I would also encourage your to try adding your suggestions to the OP's code and see if they work, because at this point in time, this doesn't answer the OP's question ;) – MadProgrammer Aug 26 '15 at 00:03
  • @MadProgrammer damn yeah,i'm just used to a bit higher standard of code and didn't even read the code entirely. guess i'll have to edit –  Aug 26 '15 at 00:04
  • @Paul "But if you reuse this code to display more shapes, you'll have 36000 repaints, if you simply add a single shape, which is useless and quite a useless waste of resources" I didn't get that. – rasperryPi Aug 26 '15 at 00:06
  • @rasperryPi `repaint` generally is an inexpensive call, but you are calling it 36, 000 times, which adds up. You don't actually want to `repaint` (I'm assuming) until you've actually build the shape at which time you should call it just once. But in your case, since the frame isn't actually visible, it's just a waste of time – MadProgrammer Aug 26 '15 at 00:10
  • @rasperryPi i'm talking about your `drawCircle` method. The while-loop will run 36000 times (360 / 0.01). Inside the while-loop you have a call to `this.repaint()`, which causes swing to repaint the window. Which is a quite useless waste of resources. Why not simply add all lines to the list and call `repaint` afterwards? one call instead of 36000 with the same result (or even better, since there won't be no flickering or other ugly misbehaviour). –  Aug 26 '15 at 00:11
  • Ah, I see the problem now. I am still quite new when it comes to paint etc., so where in my do you suggest I could put the repaint-method that only results in calling it once? – rasperryPi Aug 26 '15 at 00:16
  • After the whileloop. Simple as that –  Aug 26 '15 at 00:20
2

Welcome to the wonderful world of why you shouldn't extend from JFrame and why you shouldn't override paint of top level containers like JFrame

The main problem is, the frame has borders. If you override paint of a JFrame and paint at 0x0, you will actually be painting UNDER the frame. Your circle (at a ratio of 3) is small enough to be painted entirely under the window's title bar.

See How to get the EXACT middle of a screen, even when re-sized, How can I set in the midst? and Java AWT drawString() does not display on window for examples of this problem and why you shouldn't override paint of top level containers

Instead, use a component like JPanel and override it's paintComponent and place your custom painting there. Then place this within an instance of a JFrame, for example...

Panel in frame

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test extends JFrame {

    public static void main(String[] args) {
        new Test();
    }

    public Test() throws HeadlessException {
        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("Test");
                frame.add(new TestPane());
                frame.pack();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        ArrayList<Line2D> lines = new ArrayList<>();

        public TestPane() {
            drawCircle(3, 3.0, 3.0);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            g2d.setColor(Color.RED);
            System.out.println(lines.size());
            for (Line2D s : lines) {
                g2d.draw(s);
            }
            g2d.dispose();
        }

        public void drawCircle(int radio, double xCenter, double yCenter) {
            double t = 0;
            double xPoint;
            double yPoint;
            double xActual = xCenter;
            double yActual = yCenter + radio * Math.sin(0);
            t += 0.01;

            while (t < 360) {
                xPoint = xCenter + radio * Math.cos(t);
                yPoint = yCenter + radio * Math.sin(t);

//              System.out.println(xActual + "x" + yActual + "x" + xPoint + "x" + yPoint);
                lines.add(new Line2D.Double(xActual, yActual, xPoint, yPoint));

                t += 0.01;
                xActual = xPoint;
                yActual = yPoint;
            }
        }
    }

}

Your next problem will be, 0x0 (or close enough to it) is the center point of the circle. You need to be able to calculate a x/y offset when you generate the shape so that the top/left corner of the circle is at 0x0 instead

For example, in testing your code, I increased the radio to 100 (to see if it made any difference, which is how I discovered that you were painting under the frame) and it generated something like...

Offset

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • That worked perfectly. Thank you! I will read the stuff you suggested, because I didn't quite get why it didn't work using JFrame to paint, when I've done it earlier. – rasperryPi Aug 26 '15 at 00:13
  • Have a look at [this](http://stackoverflow.com/questions/13457237/how-to-get-the-exact-middle-of-a-screen-even-when-re-sized/13460914#13460914) and [this](http://stackoverflow.com/questions/13734069/how-can-i-set-in-the-midst/13734319#13734319) examples for more details about why overriding `paint` of top level container is generally a bad idea – MadProgrammer Aug 26 '15 at 00:16
0

Create a Graphics2D object. It helps. For example:

Graphics2D g2 = (Graphics2D)g;

Once you have that, you need to set the color. Which you didn't do. Example:

g2.setColor(Color.BLACK);

Also just a tip, override paintComponent(Graphics g) and do NOT override paint().

Wyatt Lowery
  • 513
  • 1
  • 7
  • 21