Reputation: 943
I am creating a program that draws a polygon and fills it. It then displays buttons so the user can move the polygon up, down, left, or right. Right now I am having an issue when I call the repaint() method.
In the paint() method I create a new instance of the class that contains the methods to draw and fill the polygon. Therein lies the reason for my problem. A new instance of the class is created every time that paint() is called so the arrays that I have that contain the coordinates of the points of the polygon are reset to their initial values.
My question is what is the best way to attack this problem? How do I access these methods from the paint() method without creating a new instance? I know everyone is going to say I can use something like "NameOfClass.neededMethod();" which requires me to make many things static and it does not work out as hoped.
I appreciate any pointers.
Here is the code for my class that has the methods that I need access to:
public class FillPolygon
{
int left_most_edge, right_most_edge, scan = 0;
double[] xcoord;
double[][] table = new double[4][200]; //2d array containing:
//[0][-] -> ymax, [1][-] -> ymin, [2][-] -> dx, [3][-] -> x
double[] px = {100, 150, 250, 300, 250, 150, 100}; //contains all x coord.
double[] py = {125, 100, 200, 150, 100, 200, 200}; //contains all y coord.
public void initializeTable()
{
int i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 200; j++)
{
table[i][j] = 0;
}//end for
}//end for
}//end initializeTable
public void upPressed()
{
for (int i = 0; i < py.length; i++)
{
py[i] -= 5;
}//end for
repaint();
}//end upPressed
public void downPressed()
{
for (int i = 0; i < py.length; i++)
{
py[i] += 5;
}//end for
repaint();
}//end upPressed
public void leftPressed()
{
for (int i = 0; i < px.length; i++)
{
px[i] -= 5;
}//end for
repaint();
}//end upPressed
public void rightPressed()
{
for (int i = 0; i < px.length; i++)
{
px[i] += 5;
}//end for
repaint();
}//end upPressed
public double max (double x, double y)
{ //determines the greater of two values
double max;
if (x > y)
max = x;
else
max = y;
return max;
}//end max
public void edgeInsert(double xStart, double yStart, double xEnd, double yEnd, int number_entered_edges)
{ //inserting edges into the edge table
int j = number_entered_edges; //removing the - 1 removes line on left side
double x;
if (yStart > yEnd)
{
table[0][j] = yStart;
table[1][j] = yEnd;
}//end if
else
{
table[0][j] = yEnd;
table[1][j] = yStart;
}//end else
if (table[1][j] == xStart)
x = xStart;
else
x = xEnd;
if (table[0][j] == yStart)
table[2][j] = -(-(xEnd - xStart) / (yEnd - yStart));
else
table[2][j] = -(xEnd - xStart) / (yEnd - yStart);
table[3][j] = x + table[2][j] / 2;
help(j);
}//end edgeInsert
public void loadTable(int number_vertices, int number_entered_edges,
double[] px, double[] py)
{ //take the x and y coordinates and build an edge table based off of them
int k;
double xStart, yStart, xEnd, yEnd;
xStart = px[number_vertices - 1];
yStart = trunc(py[number_vertices - 1]) + 0.5;
//start off with no edges in edge table
number_entered_edges = 0;
for (k = 0; k < number_vertices; k++)
{
xEnd = px[k];
yEnd = trunc(py[k]) + 0.5;
if (yStart == yEnd)
{
xStart = xEnd;
}//end if
else
{
//add edge to edge table
number_entered_edges++;
edgeInsert(xStart, yStart, xEnd, yEnd, number_entered_edges);
yStart = yEnd;
xStart = xEnd;
}//end else
}//end for
scan = (int)trunc(table[1][0]); //start at the top of the polygon
}//end loadTable
public void include(int number_entered_edges)
{ //pushing the right most edge
while ((right_most_edge + 1 < number_entered_edges) && (table[1][right_most_edge + 1] < scan))
{
right_most_edge++;
}//end while
}//end include
public void exclude()
{ //excluding edges that we no longer care about
for (int i = left_most_edge; i <= right_most_edge; i++)
{
if (table[0][i] < scan)
{
left_most_edge++;
for (int j = i; j >= left_most_edge; j--)
{
table[0][j] = table[0][j - 1];
table[2][j] = table[2][j - 1];
table[3][j] = table[3][j - 1];
}//end for
}//end if
}//end for
}//end exclude
public void help(int i)
{
double helpX, helpDX, helpYMax, helpYMin;
for (int j = i - 1; j >= 0; j--)
{
if ((table[1][j] == table[1][j + 1] && table[3][j] > table[3][j + 1]) || table[1][j] > table[1][j + 1])
{
helpYMax = table[0][j];
table[0][j] = table[0][j + 1];
table[0][j + 1] = helpYMax;
helpYMin = table[1][j];
table[1][j] = table[1][j + 1];
table[1][j + 1] = helpYMin;
helpDX = table[2][j];
table[2][j] = table[2][j + 1];
table[2][j + 1] = helpDX;
helpX = table[3][j];
table[3][j] = table[3][j + 1];
table[3][j + 1] = helpX;
}//end if
}//end for
}//end help
public void updateX()
{ //increment x based on dx
for (int i = left_most_edge; i <= right_most_edge; i++)
{
table[3][i] += table[2][i];
}//end for
}//end updateX
public void sortOnX()
{ //sorting x values from least to greatest in edge table
int l = 0;
double t;
xcoord = new double[right_most_edge - left_most_edge + 1];
for (int i = left_most_edge; i <= right_most_edge; i++)
{
xcoord[l] = table[3][i];
for(int j = l - 1; j >= 0; j--)
{
if (xcoord[j] > xcoord[j + 1])
{
t = xcoord[j];
xcoord[j] = xcoord[j + 1];
xcoord[j + 1] = t;
}//end if
}//end for
l++;
}//end for
}//end sortOnX
public void fillScan(Graphics g)
{ //determines the line to be drawn for filling
for (int i = 0; i < xcoord.length; i += 2)
{
drawMyHorizontalLine(g, (int)Math.round(xcoord[i]), scan, (int)Math.round(xcoord[i + 1]));
}//end for
}//end fillScan
public double trunc(double num)
{ //trucates the number passed in to remove any decimal
double rem;
if ((num % 2) == 0)
return num;
else
{
rem = num % 2;
return num - rem;
}//end else
}//end trunc
public void drawMyPolygon(Graphics g)
{ //draws the polygon
g.setColor(Color.RED);
g.drawLine((int)px[0], (int)py[0], (int)px[1], (int)py[1]);
g.drawLine((int)px[1], (int)py[1], (int)px[2], (int)py[2]);
g.drawLine((int)px[2], (int)py[2], (int)px[3], (int)py[3]);
g.drawLine((int)px[3], (int)py[3], (int)px[4], (int)py[4]);
g.drawLine((int)px[4], (int)py[4], (int)px[5], (int)py[5]);
g.drawLine((int)px[5], (int)py[5], (int)px[6], (int)py[6]);
g.drawLine((int)px[6], (int)py[6], (int)px[0], (int)py[0]);
}//end drawMyPolygon
public void drawMyHorizontalLine(Graphics g, int x1, int y, int x2)
{ //draws the line for filling
g.setColor(Color.GREEN);
g.drawLine(x1, y, x2, y);
}//end drawMyHorizontalLine
public void fillMyPolygon(Graphics g, int number_vertices, int number_entered_edges)
{ //calls methods to deal with edge table and fill the polygon
if (number_entered_edges < 3 || number_entered_edges > 200)
{
System.out.println("Polygon size error");
}//end if
else
{
loadTable(number_vertices, number_entered_edges, px, py);
while (left_most_edge < number_entered_edges)
{
scan++; //move down the screen
exclude();
updateX();
include(number_entered_edges);
sortOnX();
fillScan(g);
}//end while
}//end else
}//end fillMyPolygon
}//end FillPolygon
Here is my paint() method that needs access to the methods in the FillPolygon class in order to actually draw to the JFrame:
@Override
public void paint(Graphics g)
{
FillPolygon f = new FillPolygon();
jButton1.setVisible(true);
jButton2.setVisible(true);
jButton3.setVisible(true);
jButton4.setVisible(true);
jButton5.setVisible(true);
jButton6.setVisible(true);
//initialize the edge table to all zeroes
f.initializeTable();
//begin filling the polygon
f.fillMyPolygon(g, 7, 7);
//draw polygon with red outline
f.drawMyPolygon(g);
}//end paint
Upvotes: 2
Views: 84
Reputation: 285403
In the paint() method I create a new instance of the class that contains the methods to draw and fill the polygon. Therein lies the reason for my problem. A new instance of the class is created every time that paint() is called so the arrays that I have that contain the coordinates of the points of the polygon are reset to their initial values.
Don't do this. The paint method (or better the paintComponent
method override of a JPanel) should be for painting and painting only. You shouldn't be creating such instances there, nor should you be changing class state there. Just paint and that's it. Note that 1) painting may be initiated by the system and is not under your completely under your control, and 2) painting is not guaranteed to occur each time you call repaint()
, and so if your program logic depends on a painting method, it risks failure.
I know everyone is going to say I can use something like "NameOfClass.neededMethod();" which requires me to make many things static and it does not work out as hoped.
No one who understands your problem fully (and that's not yet me I'm afraid) and who understands OOPs is going to suggest this, trust me.
For more specific help, please show more code, and explain more details of your problem.
Edit
You should not be changing the visibility of buttons in paint. You should not be creating FillPolygon in paint, but rather should do this once in perhaps the class constructor. Also you should always call the super painting method, and should avoid overriding paint, preferring paintComponent. For example:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
@SuppressWarnings("serial")
public class DrawMovePolygonMain extends JPanel {
private DrawPolygonPanel drawPolygonPanel = new DrawPolygonPanel();
private MyMouseListener myMouseListener = new MyMouseListener();
public DrawMovePolygonMain() {
drawPolygonPanel.addMouseListener(myMouseListener);
JPanel buttonPanel = new JPanel();
buttonPanel.add(createDrawToggleButton());
for (PolyDirection dir : PolyDirection.values()) {
buttonPanel.add(new JButton(new DirectionAction(dir)));
}
setLayout(new BorderLayout());
add(drawPolygonPanel, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
}
private JComponent createDrawToggleButton() {
JToggleButton toggleButton = new JToggleButton("Draw Poly Points");
toggleButton.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
drawPolygonPanel.clearPoly();
drawPolygonPanel.resetPoints();
myMouseListener.setEnabled(true);
} else {
myMouseListener.setEnabled(false);
Path2D poly = new Path2D.Double();
List<Point> points = drawPolygonPanel.getPoints();
if (points == null || points.size() == 0) {
return;
}
poly.moveTo(points.get(0).getX(), points.get(0).getY());
for (Point point : points) {
poly.lineTo(point.getX(), point.getY());
}
poly.closePath();
drawPolygonPanel.clearPoints();
drawPolygonPanel.setPoly(poly);
}
}
});
return toggleButton;
}
private class DirectionAction extends AbstractAction {
private PolyDirection dir;
public DirectionAction(PolyDirection dir) {
super(dir.name());
this.dir = dir;
}
@Override
public void actionPerformed(ActionEvent e) {
drawPolygonPanel.move(dir);
}
}
private class MyMouseListener extends MouseAdapter {
private boolean enabled;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public void mousePressed(MouseEvent e) {
if (enabled) {
drawPolygonPanel.addPoint(e.getPoint());
}
}
}
private static void createAndShowGui() {
DrawMovePolygonMain mainPanel = new DrawMovePolygonMain();
JFrame frame = new JFrame("DrawMovePolygonMain");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
@SuppressWarnings("serial")
class DrawPolygonPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color POLY_COLOR = Color.red;
private static final Color POLY_EDGE_COLOR = Color.blue;
private static final Stroke EDGE_STROKE = new BasicStroke(3f,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
private static final double SCALE = 10.0;
private static final int PT_RADIUS = 4;
private Path2D poly;
private List<Point> points = new ArrayList<>();
public void move(PolyDirection direction) {
double tx = direction.getTx() * SCALE;
double ty = direction.getTy() * SCALE;
AffineTransform transform = AffineTransform.getTranslateInstance(tx, ty);
poly.transform(transform);
repaint();
}
public void resetPoints() {
points = new ArrayList<>();
}
public void setPoly(Path2D poly) {
this.poly = poly;
repaint();
}
public void clearPoly() {
poly = null;
repaint();
}
public void addPoint(Point p) {
if (points != null) {
points.add(p);
}
repaint();
}
public List<Point> getPoints() {
return points;
}
public void clearPoints() {
points = null;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (poly != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(POLY_COLOR);
if (poly != null) {
g2.fill(poly);
}
g2.setColor(POLY_EDGE_COLOR);
Stroke oldStroke = g2.getStroke();
g2.setStroke(EDGE_STROKE);
g2.draw(poly);
g2.setStroke(oldStroke);
}
if (points != null && points.size() > 0) {
g.setColor(Color.black);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Point pt : points) {
int x = pt.x - PT_RADIUS;
int y = pt.y - PT_RADIUS;
int width = 2 * PT_RADIUS;
int height = width;
g.fillOval(x, y, width, height);
}
}
}
@Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
enum PolyDirection {
UP(0.0, -1.0), DOWN(0.0, 1.0), LEFT(-1.0, 0.0), RIGHT(1.0, 0.0);
private double tx;
private double ty;
private PolyDirection(double tx, double ty) {
this.tx = tx;
this.ty = ty;
}
public double getTx() {
return tx;
}
public double getTy() {
return ty;
}
}
Note that my example code above the drawing component extends JPanel, the drawing method is a paintComponent method, its first method call inside of it is to the super paintComponent method. And inside it, all it does is draw the polygon or the points used to create a polygon and nothing else.
Upvotes: 6
Reputation: 16234
All code in paint()
should be moved to the component's constructor.
Only drawings should be done in paint()
, or even better - in paintComponent()
that is designed for override (while paint()
has some internal functions, which BTW you interfered by not calling super.paint()
).
Upvotes: 0