This is a simplified version of a larger project. I have three JPanels contained in a larger JSplitPane called wholePanel . The top panel and left panel hold labels and buttons, which I have not included here. The lower right panel, called canvasPanel , is where I draw shapes. I'm trying to tilt an oval at an angle and I am successful at doing so. However, a problem occurs when I resize canvasPanel . The oval will jump if you drag the Pane dividers to resize. I believe that what is occurring is that the oval is realigning itself sometimes with respect to the upper left corner of wholePanel and sometimes with respect to the upper left of canvasPanel. So it has an annoying jump. I want it to always align itself with respect to canvasPanel , not wholePanel . This seems to happen because of the AffineTransform, but I don't know why or how to fix it.

My second problem is that sometimes canvasPanel does not paint until I resize it, leaving a dull gray panel with no background color and no shape. I want it to paint without the user having to do anything. Thank you. Code is below.

package ColorForms;

import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import java.lang.*;



public class ColorForms extends JFrame
{
    LeftPanel leftPanel;
    TopPanel topPanel;
    CanvasPanel canvasPanel;
    JSplitPane wholePanel, bottomPanel;
    
    public static void main (String args[])
    {
        ColorForms cf = new ColorForms ();
    }
    
    
    public ColorForms ()
    {
        this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        this.setSize (400, 400);
        this.setVisible (true);
        
        leftPanel = new LeftPanel ();
        topPanel = new TopPanel ();
        canvasPanel = new CanvasPanel ();
        
        bottomPanel = new JSplitPane (JSplitPane.HORIZONTAL_SPLIT, leftPanel, canvasPanel);
        bottomPanel.setDividerLocation (50);
        wholePanel = new JSplitPane (JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel);
        wholePanel.setDividerLocation (70);

        this.getContentPane ().add (wholePanel);
    }
}


class LeftPanel extends JPanel
{
    public LeftPanel ()
    {        
    }
}


class TopPanel extends JPanel
{
    public TopPanel ()
    {
        
    }
}


class CanvasPanel extends JPanel
{
    public CanvasPanel ()
    {        
    }


    public void paintComponent (Graphics g)
    {
        System.out.println ("I am in CanvasPanel repaint.");
        
        int ovalCenterX = 100;
        int ovalCenterY = 100;
        int ovalWidth = 200;
        int ovalHeight = 100;
        int ovalUpperLeftX = ovalCenterX - ovalWidth / 2;
        int ovalUpperLeftY = ovalCenterY - ovalHeight / 2;
        double angle = 0.5;
        
        super.paintComponent (g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        setBackground(Color.GREEN);
        
        AffineTransform orig = g2.getTransform();
        AffineTransform af = new AffineTransform ();
        af.rotate(angle, ovalCenterX, ovalCenterY);
        g2.setTransform(af);
        g2.setColor (Color.BLACK);
        g2.fillOval (ovalUpperLeftX, ovalUpperLeftY, ovalWidth, ovalHeight);
        g2.setTransform(orig);
    }
}

Recommended Answers

All 6 Replies

After running some tests I realized that somehow your transform is jumping between 1-22 on the (result?) matrice--

import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import java.lang.*;



public class ColorForms extends JFrame
{
    LeftPanel leftPanel;
    TopPanel topPanel;
    CanvasPanel canvasPanel;
    JSplitPane wholePanel, bottomPanel;

    public static void main (String args[])
    {
        ColorForms cf = new ColorForms ();
    }


    public ColorForms ()
    {
        this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        this.setSize (400, 400);
        this.setVisible (true);

        leftPanel = new LeftPanel ();
        topPanel = new TopPanel ();
        canvasPanel = new CanvasPanel ();

        bottomPanel = new JSplitPane (JSplitPane.HORIZONTAL_SPLIT, leftPanel, canvasPanel);
        bottomPanel.setDividerLocation (50);
        wholePanel = new JSplitPane (JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel);
        wholePanel.setDividerLocation (70);

        this.getContentPane ().add (wholePanel);
    }
}


class LeftPanel extends JPanel
{
    public LeftPanel ()
    {
    }
}


class TopPanel extends JPanel
{
    public TopPanel ()
    {

    }
}


class CanvasPanel extends JPanel
{
    public CanvasPanel ()
    {
    }


    public void paintComponent (Graphics g)
    {
        System.out.println (((Graphics2D)g).getTransform());

        int ovalCenterX = 100;
        int ovalCenterY = 100;
        int ovalWidth = 200;
        int ovalHeight = 100;
        int ovalUpperLeftX = ovalCenterX - ovalWidth / 2;
        int ovalUpperLeftY = ovalCenterY - ovalHeight / 2;
        double angle = 0.5;

        super.paintComponent (g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        setBackground(Color.GREEN);

        AffineTransform orig = g2.getTransform();
        AffineTransform af = new AffineTransform ();
        af.rotate(angle, ovalCenterX, ovalCenterY);
        g2.setTransform(af);
        g2.setColor (Color.BLACK);
        g2.fillOval (ovalUpperLeftX, ovalUpperLeftY, ovalWidth, ovalHeight);
        g2.setTransform(orig);
    //    g2.setTransform(af);
    }
}

-but I can't say much else with my lack of knowledge in transformations =/

Alex, thanks for looking at it. I'm a little confused about what you meant with this line:

After running some tests I realized that somehow your transform is jumping between 1-22 on the (result?) matrice--

I put in some debugging output and the matrix values are staying the same for me. Maybe we aren't talking about the same thing.

I added a button which causes a repaint to see what that did and the results are interesting. Click the button and the oval appears to draw where I want it to. Drag the PANE resize bars and the oval jumps. It really looks like it is drawing at the proper (x,y) coordinates, but that it's baseline (0, 0) coordinate is changing from the upper left of canvasPanel to the upper left of another panel/pane, but again I don't know why or what to do about it. Here's the updated code with the button.

package ColorForms;

import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import java.lang.*;


public class ColorForms extends JFrame
{
    LeftPanel leftPanel;
    TopPanel topPanel;
    CanvasPanel canvasPanel;
    JSplitPane wholePanel, bottomPanel;
    
    public static void main (String args[])
    {
        ColorForms cf = new ColorForms ();
    }
    
    
    public ColorForms ()
    {
        this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        this.setSize (400, 400);
        this.setVisible (true);
        
        leftPanel = new LeftPanel ();
        topPanel = new TopPanel (this);
        canvasPanel = new CanvasPanel ();
        
        bottomPanel = new JSplitPane (JSplitPane.HORIZONTAL_SPLIT, leftPanel, canvasPanel);
        bottomPanel.setDividerLocation (50);
        wholePanel = new JSplitPane (JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel);
        wholePanel.setDividerLocation (70);

        this.getContentPane ().add (wholePanel);
    }
}


class LeftPanel extends JPanel
{
    public LeftPanel ()
    {        
    }
}


class TopPanel extends JPanel implements ActionListener
{
    JButton repaintButton;
    ColorForms cf;
    
    public TopPanel (ColorForms cForm)
    {
        cf = cForm;
        repaintButton = new JButton ("Repaint");
        repaintButton.addActionListener(this);
        add (repaintButton);
    }

    public void actionPerformed(ActionEvent e) 
    {
        cf.canvasPanel.repaint ();
    }
}


class CanvasPanel extends JPanel
{
    int timesPainted = 0;
    
    public CanvasPanel ()
    {        
    }


    public void paintComponent (Graphics g)
    {
        timesPainted++;
        System.out.print ("I am in CanvasPanel repaint.  # of times painted = ");
        System.out.println (timesPainted);
        
        int ovalCenterX = 100;
        int ovalCenterY = 100;
        int ovalWidth = 200;
        int ovalHeight = 100;
        int ovalUpperLeftX = ovalCenterX - ovalWidth / 2;
        int ovalUpperLeftY = ovalCenterY - ovalHeight / 2;
        double angle = 0.5;
        
        super.paintComponent (g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        setBackground(Color.GREEN);
        
        AffineTransform orig = g2.getTransform();
        AffineTransform af = new AffineTransform ();
        af.rotate(angle, ovalCenterX, ovalCenterY);
        g2.setTransform(af);
        
        
        // display AffineTransform attributes
        System.out.println (af.getScaleX());
        System.out.println (af.getScaleY());
        System.out.println (af.getShearX());
        System.out.println (af.getShearY());
        System.out.println (af.getTranslateX());
        System.out.println (af.getTranslateY());
        
        g2.setColor (Color.BLACK);
        g2.fillOval (ovalUpperLeftX, ovalUpperLeftY, ovalWidth, ovalHeight);
        g2.setTransform(orig);
    }
}

I've been playing around with this. I added MouseListeners to the top panel and the canvas panel. Like the button, if you click on either, it will redraw in the correct place. I also draw a circle on the canvas that doesn't use a transform and it doesn't do that jump. Finally, when I add a ComponentListener (presently commented out) to the canvas panel and have it repaint when the component is resized, I don't need to click a panel or a button, but it jumps back and forth if you drag by the lower right corner. I'd still like to understand what's going on though. Thanks.

package ColorForms;

import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.io.*;
import javax.swing.*;
import java.util.*;
import java.lang.*;



public class ColorForms extends JFrame // implements ComponentListener
{
    LeftPanel leftPanel;
    TopPanel topPanel;
    CanvasPanel canvasPanel;
    JSplitPane wholePanel, bottomPanel;
    
    public static void main (String args[])
    {
        ColorForms cf = new ColorForms ();
    }
    
    
    public ColorForms ()
    {
        this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        this.setSize (400, 400);
        this.setVisible (true);
        
        leftPanel = new LeftPanel ();
        topPanel = new TopPanel (this);
        canvasPanel = new CanvasPanel ();
        
        bottomPanel = new JSplitPane (JSplitPane.HORIZONTAL_SPLIT, leftPanel, canvasPanel);
        bottomPanel.setDividerLocation (50);
        wholePanel = new JSplitPane (JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel);
        wholePanel.setDividerLocation (70);

//        canvasPanel.addComponentListener(this);
        
        this.getContentPane ().add (wholePanel);
    }
/*
    public void componentResized(ComponentEvent e) 
    {
        System.out.println ("Hi there");
        canvasPanel.repaint();
    }

    public void componentMoved(ComponentEvent e) 
    {
    }

    public void componentShown(ComponentEvent e) 
    {
    }

    public void componentHidden(ComponentEvent e) 
    {
    }*/
}


class LeftPanel extends JPanel
{
    public LeftPanel ()
    {        
    }
}


class TopPanel extends JPanel implements ActionListener, MouseListener
{
    JButton repaintButton;
    ColorForms cf;
    
    public TopPanel (ColorForms cForm)
    {
        cf = cForm;
        repaintButton = new JButton ("Repaint");
        repaintButton.addActionListener(this);
        add (repaintButton);
        this.addMouseListener(this);
    }

    public void actionPerformed(ActionEvent e) 
    {
        cf.canvasPanel.repaint ();
    }

    public void mouseClicked(MouseEvent e) 
    {
        cf.canvasPanel.repaint();
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }
}


class CanvasPanel extends JPanel implements MouseListener
{
    int timesPainted = 0;
    
    public CanvasPanel ()
    {
        this.addMouseListener(this);
    }


    public void paintComponent (Graphics g)
    {
        timesPainted++;
        System.out.print ("I am in CanvasPanel repaint.  # of times painted = ");
        System.out.println (timesPainted);
        
        int ovalCenterX = 100;
        int ovalCenterY = 100;
        int ovalWidth = 200;
        int ovalHeight = 100;
        int ovalUpperLeftX = ovalCenterX - ovalWidth / 2;
        int ovalUpperLeftY = ovalCenterY - ovalHeight / 2;
        double angle = 0.5;
        
        super.paintComponent (g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        setBackground(Color.GREEN);
        
        AffineTransform orig = g2.getTransform();
        AffineTransform af = new AffineTransform ();
        af.rotate(angle, ovalCenterX, ovalCenterY);
        g2.setTransform(af);
        
        g2.setColor (Color.BLACK);
        g2.fillOval (ovalUpperLeftX, ovalUpperLeftY, ovalWidth, ovalHeight);
        g2.setTransform(orig);
        g2.setColor (Color.MAGENTA);
        g2.fillOval (250, 100, 50, 50);
    }

    public void mouseClicked(MouseEvent e) 
    {
        repaint ();
    }

    public void mousePressed(MouseEvent e) 
    {
    }

    public void mouseReleased(MouseEvent e) 
    {
    }

    public void mouseEntered(MouseEvent e) 
    {
    }

    public void mouseExited(MouseEvent e) 
    {
    }
}

The problem is originating with your creation of the affine transform that you are rotating:

AffineTransform af = new AffineTransform ();

If you change that to pass the original transform to the constructor, it works just fine:

AffineTransform af = new AffineTransform (orig);

Without backtracing the stack on every repaint() call, I can't tell you for certain, but I would guess the new transform is getting generated against the frame context when you resize and using the CanvasPanel on the other events. The fact that it's an inner class of another visual component further clouds the issue.

Honestly, you don't need to create a new transform from scratch anyway. Just call rotate directly on the graphics context

g2.rotate(angle, ovalCenterX, ovalCenterY);

and it behaves just fine.

Also, if you move the setVisible() call to the end of the ColorForms constructor, it will paint the components you added properly on construction.

commented: Very helpful. +7

Actually, disregard the comment on the inner class. I just noticed that it is not actually an inner class of ColorForms.

Since the "jumping" occurs on a top-level repaint of the containment heirarchy and CanvasPanel is a component of "bottomPanel", the new transform seems to be created relative to the bottomPanel graphics context when the paintComponent() call comes down from that container. Your other events are calling repaint directly on the CanvasPanel and the transform is fine on those.

All right! Now we're getting somewhere! Moving the setVisible line solved that half of the problem and using the original transform in the constructor solved the other part, at least for the way I have it now. I'm still playing around with it trying to expand on it to make it fit into my larger program, but I think I'm going to flag this one as solved. Thank you very much Ezzaral!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.