Home > Software design >  Drawing lines the full height of JPanel
Drawing lines the full height of JPanel

Time:03-16

I've gotten my code to work for the most part except for one last thing. When the number of lines drawn > 20, the lines aren't being drawn to take up the full amount of panel

** Look at the bottom right side of my screenshot. The lines go part way down the full height of the panel. I need it to go the full length of the height down. **

enter image description here

Here's my code for DrawPanelTest


// Creating JPanel to display DrawPanel
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class DrawPanelTest
{
   public static void main(String[] args)
   {
      int nValue = 0; // declare variable to store user input. Default value 0.
      Boolean flag = true; // initalize flag to true for argument value of while-loop

      // while-loop to prompt user input
      while (flag) {
         // prompt user for the value of N
         nValue = Integer.parseInt(JOptionPane.showInputDialog("Enter number of lines between 2 and 100."));

        // user input validation. Valid input is nValue [2, 100]
        if (nValue < 2 || nValue > 100) {
           nValue = Integer.parseInt(JOptionPane.showInputDialog("Enter number of lines between 2 and 100."));
        } else {
           flag = false; // if user input is correct, while-loop will end
        } // end if-else
      } // end while-loop
     
      // displays the value of N to make sure it really is correct; This works
      // String message = String.format("The value of n is: %d ", nValue);
      // JOptionPane.showMessageDialog(null, message);

      // create a panel that contains our drawing
      DrawPanel drawPanel = new DrawPanel(nValue);
      // create a new frame to hold the panel
      JFrame application = new JFrame();

      // set the frame to exit when closed
      application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      application.add(drawPanel); // add panel to the frame
      application.setSize(600, 600); // set the size of the frame
      application.setVisible(true); // make the frame visible
   }
 } // end class DrawPanelTest

Here's my code DrawPanel

 // Using drawLine() to connect the corners of a panel
import java.awt.Graphics;
import javax.swing.JPanel;


public class DrawPanel extends JPanel 
{
  int numLines;

  // constructor initializes DrawPanel and initializes
  // numLines with the argument value of n
  public DrawPanel(int n)
  {
    numLines = n;
  }

  // draws a X from the corners of the panel
  public void paintComponent( Graphics g)
  {
    // call paintComponent to ensure the panel displays correctly
    super.paintComponent(g);

    int width = getWidth(); // total width
    int height = getHeight(); // total height
    int x1 = 0; // starting x-coordinate
    int y1 = height / 2; // starting y-coordinate
    int x2 = width; // initial value for end x-coordinate
    int spaceValue = 0; // represents the space between the lines
    int stepValue = height/numLines; // represents the increment value starting from top right corner

    for (int i = 0; i <= numLines; i  ) {
      if (numLines == 2) {
        g.drawLine(x1, y1, x2, 0);
        g.drawLine(x1, y1, x2, height);
        break;
      } // end numLines == 2

      else if (numLines >= 3) {
        g.drawLine(x1, y1, x2, spaceValue);
        spaceValue  = stepValue;
      } // end else if
    } // end for-loop
  } // end paintComponent
} // end class DrawPanel

I think my problem lies in DrawPanel line 41. I don't think I'm calculating the spaces between the lines correctly so that they end up taking the entire height of the panel.

Thanks in advance for your help.

CodePudding user response:

I think (one of) the issue you're having is a "integer division" issue.

int stepValue = height / numLines;

is truncating the result. For example, if the height is 400 and the number of lines is 6 the stepValue will be 66 instead of 67 (which would allow 66.6666 to be rounded up)

Now, you could "round" the value up yourself, but I'd prefer to make use of the available APIs to do these things for me.

Line2D.Double supports double precision parameters, which makes it perfect for this job

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    if (lineCount > 1) {
        double gap = getHeight() / (double)(lineCount - 1);
        for (int i = 0; i < lineCount; i  ) {
            Line2D line = new Line2D.Double(0, getHeight() / 2, getWidth(), i * gap);
            g2d.draw(line);
        }
    } else {
        g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
    }
    g2d.dispose();
}

Runnable example...

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                JPanel contentPane = new JPanel(new BorderLayout());
                contentPane.setBorder(new EmptyBorder(32, 32, 32, 32));
                frame.setContentPane(contentPane);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private int lineCount = 1;

        public TestPane() {
            setBorder(new LineBorder(Color.RED));
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    lineCount  ;
                    repaint();
                }
            });
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            FontMetrics fm = g2d.getFontMetrics();
            g2d.drawString(Integer.toString(lineCount), 10, fm.getAscent());
            if (lineCount > 1) {
                double gap = getHeight() / (double) (lineCount - 1);
                for (int i = 0; i < lineCount; i  ) {
                    Line2D line = new Line2D.Double(0, getHeight() / 2, getWidth(), i * gap);
                    g2d.draw(line);
                }
            } else {
                g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
            }
            g2d.dispose();
        }

    }
}

CodePudding user response:

Your calculation of the "stepValue" has two problems:

  1. the stepValue is to small, so the last line will never appear at the bottom
  2. the stepValue is truncated because of integer math so each addition of the truncated value to the "spaceValue" will yield a slightlight less accurate value.

Lets say you have a panel with a height of 600 and the number of lines is 3.

Your calculation is:

int stepValue = 600 / 3 = 200

which I would suggest is wrong as you would draw 3 lines with a "spaceValue" of 0, 200, 400, so the last line would never be drawn.

In reality I think the calculation should be:

double stepValue = (double)height / (numLine - 1);

which gives:

double stepValue = 600 / (3 - 1) = 300.

which will give lines with a "spaceValue" of 0, 300, 600 which would be a line at the top, middle and bottom.

So your painting loop simply becomes:

for (int i = 0; i < numLines; i  ) 
{
    g.drawLine(x1, y1, x2, spaceValue);
    spaceValue  = stepValue;
}

CodePudding user response:

In the for loop, change

for (int i = 0; i < numLines; i  ) {

to

for (int i = 0; i <= numLines; i  ) {

to iterate the full height of the component. The i must equal numLines to reach unity.

  • Related