Image is out of place after rotation (graphics)

I am trying to display two spinning wheels with a diameter of 512 at different speeds, but I am unable to remove the previous image graphic and set the rotated graphic in the correct position. At the moment I am doing a rotation with an arbitrary angle. I tried affineTransform and got rotations, but it was weird like all pixels. I'm using a while loop with thread.sleep (). Below is the code: // 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;
    }        
}

      

+3


source to share


1 answer


First Rule of Swing. Do not block the Event Manager. This will cause the application to appear as if it is frozen and prevent the EDT from handling any redraw requests.

This means you need to schedule updates somehow that do not block EDT

The second rule is Swing. Do not create or modify any UI component from any thread other than EDT.

As a general rule, you should avoid overriding the method paint

for top-level containers such as JFrame

, among other things, they are not duplicated by the buffer, which means your picture will flicker as it refreshes. Instead, you should use one of the containers Swing

, for exampleJPanel

There are many different ways to achieve this. Basically, I used three lists here, but if I was serious I would create an object that could support all the information needed (image, angle delta)



To achieve the actual animation I used javax.swing.Timer

. This will fire an event at least every n periods, but more importantly, it does so in the context of the Dispatching Event thread. This will ensure that all changes made to the corners are done in such a way as to prevent any drawing possibility when we update the values ​​...

This example rotates three images at different (random) speeds ...

enter image description here

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;

    }

}

      

+2


source







All Articles