Rotation transform not working properly in repaint when running thread

孤街浪徒 提交于 2019-12-23 15:39:12

问题


I have been working on trying to make this program work, even though I can't seem to find whatever is the problem. This program is made of the following 2 classes, no more, no less. It is basically supposed to draw a point on each click in the drawing zone, and on the 3rd click all points are connected. I still have to work on making it prettier and more accurate, but this part works. What doesn't work is what should follow : on the fourth click a thread should start (and it starts), and the triangle itself should rotate given an arbitrary refresh rate, for exactly 80 repaints. The next click should not work until the animating is finished and only then if there is a click after the animation stops(the thread dies), a new point is displayed and it starts all over again.

Is it possible that all paint calls are stacked until the end of my thread? I know it can happen that all events are stacked in the event queue and treated as one. Using a repaint with a time parameter doesn't help. I have added comments to help clarify, because all variables are french words (they're explained). I have a hard time trying to figure out if it's in my code to find the problem, if it's thread-related or even type-related. I'm just going nowhere in debug mode (using Eclipse). Have I completely ignored something obvious?

While my methods might not be the most efficient way to do this, the rotation is my primary problem. I can handle writing the unwritten remaining part of the code. Thanks for any help! Here are the two classes :

import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;


public class Application extends JFrame {

private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JButton btnTerminer;
private Triangle triangle;
private int totalClics = 0;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                Application frame = new Application();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the frame.
 */
public Application() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 453, 692);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    contentPane.setLayout(null);

    btnTerminer = new JButton("Terminer");
    btnTerminer.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            System.exit(0);
        }
    });
    btnTerminer.setBounds(138, 622, 132, 23);
    contentPane.add(btnTerminer);

    triangle = new Triangle();
    //Adds points for every mouse click
    triangle.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            if(totalClics < 3){
                triangle.ajouterPoint(e.getX(), e.getY());
                totalClics++;
            } else {
                triangle.getAnim().start();
                totalClics = 0;
            }
        }
    });
    triangle.setBounds(10, 11, 400, 600);
    contentPane.add(triangle);
}
}

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;


import javax.swing.JPanel;


public class Triangle extends JPanel implements Runnable,Serializable{

private static final long serialVersionUID = 1L;
private ArrayList<Point> points = null;
//Animation thread
private Thread anim;
private Color couleurPrin;
private Color couleurBoite;
//A point's diameter
private int diametre = 8;
//The rectangle's width
private int largeur;
//The rectangle's height
private int hauteur;
//The rectangle's top-left corner
private int minX;
private int minY;
//Angle incrementation multiplier
private int nbAng = 0 ;
//Thread stopping variable
private boolean continuer = true;


public Triangle() {
    setPreferredSize(new Dimension(400, 600));
    setBackground(Color.BLACK);
    couleurPrin = Color.GREEN;
    couleurBoite = Color.RED;
    setAnim(new Thread(this));
    points = new ArrayList<Point>();


}
/**
 * Repaints this component
 */
@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    int i = 0;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    int[] coorX = new int[points.size()+1];
    int[] coorY = new int[points.size()+1];
    Iterator<Point> iter = points.iterator();
    while(iter.hasNext()){
        Point p = iter.next();
        coorX[i] = p.getX();
        coorY[i]= p.getY();
        i++;
    }
    coorX[points.size()] = coorX[0];
    coorY[points.size()] = coorY[0];
    if(points.size() != 0){
        g2d.setColor(Color.white);
        g2d.fillOval(minX+largeur/2, minY+hauteur/2, 6, 6);
        g2d.setColor(couleurPrin);
        for(i =0; i<points.size(); i++){
            g2d.drawLine(coorX[i], coorY[i], coorX[i+1], coorY[i+1]);
        }
        for(i = 0; i<points.size(); i++){
            g2d.fillOval(coorX[i]-diametre/2, coorY[i]-diametre/2, diametre, diametre);
        }
        g2d.setColor(couleurBoite);
        g2d.drawRect(minX, minY, largeur, hauteur);
        g2d.rotate(15.0*nbAng, (largeur+getWidth())/2, (hauteur+getWidth())/2); 


    }

}
/**
 * Adds a point. Stops at 3.
 * @param x
 * @param y
 * 
 * 
 */
public void ajouterPoint(int x, int y){
    Point p = new Point(x,y);
    System.out.println(p.toString());
    if(points.size()>=0 && points.size()<3){
        points.add(p);
        minX = p.getX()-3;
        minY = p.getY()-3;
    }
    if(points.size() == 3){
        rectanguler(points);
    }
    repaint();

}

public Color getCouleurPrin() {
    return couleurPrin;
}
public void setCouleurPrin(Color c) {
    this.couleurPrin = c;
    repaint();
}
public int getDiametre() {
    return diametre;
}
public void setDiametre(int d) {
    this.diametre = d;
    repaint();
}
/**
 * Sets rectangle's values to the largest bounds possible
 * @param points
 */
private void rectanguler(ArrayList<Point> points){
    Iterator<Point> iter = points.iterator();
    Point p1, p2, p3;
    p1 = iter.next();
    p2 = iter.next();
    p3 = iter.next();
    int dLarg;
    int dLong;
    if(p2 != null && p3 != null){
        minX = Math.min(p1.getX(), p2.getX());
        minY = Math.min(p1.getY(), p2.getY());
        largeur = Math.abs(p1.getX()-p2.getX());
        hauteur = Math.abs(p1.getY()-p2.getY());
        if(p3 != null){
            minX = Math.min(minX, p3.getX());
            minY = Math.min(minY, p3.getY());
            dLarg = Math.max(Math.abs(p3.getX()-p2.getX()), Math.abs(p3.getX()-p1.getX()));
            dLong = Math.max(Math.abs(p3.getY()-p2.getY()), Math.abs(p3.getY()-p1.getY()));
            largeur = Math.max(dLarg, largeur);
            hauteur = Math.max(dLong, hauteur);
        }       
    }
}
/**
 * Custom point class
 * Stores an x and y value
 *
 */
private class Point{
    @Override
    public String toString() {
        return "Point [x=" + x + ", y=" + y + "]";
    }
    private int x,y;
    public Point(){
        setX(0);
        setY(0);
    }
    public Point(int x,int y){
        setX(x);
        setY(y);
    }
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }

}
/**
 * Starts the rotation
 * 
 */
@Override
public void run() {
    int i =1;
    while(continuer){

        nbAng = i;
        repaint();
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            System.out.println("Erreur dans le thread");
            e.printStackTrace();
        }
        i++;
        if(i== 80){
            continuer = false;
        }
    }
    anim = new Thread(this);

}
public Thread getAnim() {
    return anim;
}
public void setAnim(Thread anim) {
    this.anim = anim;
    repaint();
}

}

回答1:


The first problem is that you never call start() on the animation thread.

The second problem is that you should never do gui stuff outside of the EDT. as mentioned by @AndrewThompson in his comment, you should be using a swing Timer instead of a Thread.




回答2:


A Thread is overkill for what you want to achieve. A javax.swing.Timer is simpler and easier to use, it is also re-usable, unlike a Thread.

Graphics2D#rotate applies a translation to all subsequent rendering, meaning you need to apply it BEFORE you paint anything.

Your mouse click logic is a little off as well, it doesn't check the state of the thread before allowing the clicks to continue.

I modified your code a little as an example (sorry, I moved the mouse click handling to the Triangle class, but you should be able to remove it ;))

public class TestRotation01 {

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

    public TestRotation01() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new Triangle());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Triangle extends JPanel {

        private static final long serialVersionUID = 1L;
        private ArrayList<Point> points = null;
//Animation thread
//        private Thread anim;
        private Color couleurPrin;
        private Color couleurBoite;
//A point's diameter
        private int diametre = 8;
//The rectangle's width
        private int largeur;
//The rectangle's height
        private int hauteur;
//The rectangle's top-left corner
        private int minX;
        private int minY;
//Angle incrementation multiplier
        private int nbAng = 0;
//Thread stopping variable
        private boolean continuer = true;
        private int totalClics = 0;
        private Timer timer;
        private int cycle;

        public Triangle() {
            setPreferredSize(new Dimension(400, 600));
            setBackground(Color.BLACK);
            couleurPrin = Color.GREEN;
            couleurBoite = Color.RED;
            points = new ArrayList<Point>();
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (!timer.isRunning()) {
                        if (totalClics < 3) {
                            nbAng = 0;
                            ajouterPoint(e.getX(), e.getY());
                            totalClics++;
                        } else {
                            cycle = 0;
                            totalClics = 0;
                            timer.restart();
                        }
                    }
                }
            });

            timer = new Timer(200, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    nbAng += 5;
                    repaint();
                    cycle++;
                    if (cycle == 80) {
                        timer.stop();
                    }
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
        }

        /**
         * Repaints this component
         */
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            if (timer.isRunning()) {

                g2d.rotate(15.0 * nbAng, (largeur + getWidth()) / 2, (hauteur + getWidth()) / 2);

            }

            int i = 0;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            int[] coorX = new int[points.size() + 1];
            int[] coorY = new int[points.size() + 1];
            Iterator<Point> iter = points.iterator();
            while (iter.hasNext()) {
                Point p = iter.next();
                coorX[i] = p.getX();
                coorY[i] = p.getY();
                i++;
            }
            coorX[points.size()] = coorX[0];
            coorY[points.size()] = coorY[0];
            if (points.size() != 0) {
                g2d.setColor(Color.white);
                g2d.fillOval(minX + largeur / 2, minY + hauteur / 2, 6, 6);
                g2d.setColor(couleurPrin);
                for (i = 0; i < points.size(); i++) {
                    g2d.drawLine(coorX[i], coorY[i], coorX[i + 1], coorY[i + 1]);
                }
                for (i = 0; i < points.size(); i++) {
                    g2d.fillOval(coorX[i] - diametre / 2, coorY[i] - diametre / 2, diametre, diametre);
                }
                g2d.setColor(couleurBoite);
                g2d.drawRect(minX, minY, largeur, hauteur);
            }

            g2d.dispose();

        }

        /**
         * Adds a point. Stops at 3.
         *
         * @param x
         * @param y
         *
         *
         */
        public void ajouterPoint(int x, int y) {
            Point p = new Point(x, y);
            System.out.println(p.toString());
            if (points.size() >= 0 && points.size() < 3) {
                points.add(p);
                minX = p.getX() - 3;
                minY = p.getY() - 3;
            }
            if (points.size() == 3) {
                rectanguler(points);
            }
            repaint();

        }

        public Color getCouleurPrin() {
            return couleurPrin;
        }

        public void setCouleurPrin(Color c) {
            this.couleurPrin = c;
            repaint();
        }

        public int getDiametre() {
            return diametre;
        }

        public void setDiametre(int d) {
            this.diametre = d;
            repaint();
        }

        /**
         * Sets rectangle's values to the largest bounds possible
         *
         * @param points
         */
        private void rectanguler(ArrayList<Point> points) {
            Iterator<Point> iter = points.iterator();
            Point p1, p2, p3;
            p1 = iter.next();
            p2 = iter.next();
            p3 = iter.next();
            int dLarg;
            int dLong;
            if (p2 != null && p3 != null) {
                minX = Math.min(p1.getX(), p2.getX());
                minY = Math.min(p1.getY(), p2.getY());
                largeur = Math.abs(p1.getX() - p2.getX());
                hauteur = Math.abs(p1.getY() - p2.getY());
                if (p3 != null) {
                    minX = Math.min(minX, p3.getX());
                    minY = Math.min(minY, p3.getY());
                    dLarg = Math.max(Math.abs(p3.getX() - p2.getX()), Math.abs(p3.getX() - p1.getX()));
                    dLong = Math.max(Math.abs(p3.getY() - p2.getY()), Math.abs(p3.getY() - p1.getY()));
                    largeur = Math.max(dLarg, largeur);
                    hauteur = Math.max(dLong, hauteur);
                }
            }
        }

        /**
         * Custom point class Stores an x and y value
         *
         */
        private class Point {

            @Override
            public String toString() {
                return "Point [x=" + x + ", y=" + y + "]";
            }
            private int x, y;

            public Point() {
                setX(0);
                setY(0);
            }

            public Point(int x, int y) {
                setX(x);
                setY(y);
            }

            public int getX() {
                return x;
            }

            public void setX(int x) {
                this.x = x;
            }

            public int getY() {
                return y;
            }

            public void setY(int y) {
                this.y = y;
            }
        }
    }
}


来源:https://stackoverflow.com/questions/13964419/rotation-transform-not-working-properly-in-repaint-when-running-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!