使用AffineTransform缩放图形

时间:2015-08-26 02:13:16

标签: java swing affinetransform

我正在使用Swing制作一个GUI,它使用AffineTransform缩放绘制在JInternalFrame上的Graphics2D对象。问题是它在当前状态下是错误的,我无法弄清楚原因。

为什么我的代码没有正确缩放?为什么图形"跳跃"在调整大小的面板顶部?

这是我自包含的例子:

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.util.*;

public class MainPanel extends JFrame implements ActionListener{

    private static final double version = 1.0;
    private JDesktopPane desktop;
    public static RFInternalFrame frame;

    private java.util.List<Point> POINT_LIST = Arrays.asList(
            //Top Row
            new Point(50, 30),
            new Point(70, 30),
            new Point(90, 30),
            new Point(110, 30),
            new Point(130, 30),
            new Point(150, 30),
            new Point(170, 30),
            new Point(190, 30),
            new Point(210, 30),
            new Point(230, 30),

            //Circle of Radios
            new Point(140, 60),
            new Point(120, 80),
            new Point(100, 100),
            new Point(100, 120),
            new Point(120, 140),
            new Point(140, 160),
            new Point(160, 140),
            new Point(180, 120),
            new Point(180, 100),
            new Point(160, 80));

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        JFrame frame = new MainPanel();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLocationByPlatform(false);
        frame.setVisible(true);
    }

    public MainPanel() {
        super("MainPanel " + version);

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                screenSize.width - inset * 7,
                screenSize.height - inset * 4);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        desktop.setBackground(Color.DARK_GRAY);

        createRFFrame(); //create first RFFrame
        createScenarioFrame(); //create ScenarioFrame

        setContentPane(desktop);
        setJMenuBar(createMenuBar());

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
    }

    protected JMenuBar createMenuBar() {
        JMenuBar menuBar = new JMenuBar();

        //Set up the lone menu.
        JMenu menu = new JMenu("File");
        menu.setMnemonic(KeyEvent.VK_D);
        menuBar.add(menu);

        //Set up the first menu item.
        JMenuItem menuItem = new JMenuItem("Add Panel");
        menuItem.setMnemonic(KeyEvent.VK_N);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_N, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("new");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        //Set up the second menu item.
        menuItem = new JMenuItem("Quit");
        menuItem.setMnemonic(KeyEvent.VK_Q);
        menuItem.setAccelerator(KeyStroke.getKeyStroke(
                KeyEvent.VK_Q, ActionEvent.ALT_MASK));
        menuItem.setActionCommand("quit");
        menuItem.addActionListener(this);
        menu.add(menuItem);

        return menuBar;
    }

    //React to menu selections.
    public void actionPerformed(ActionEvent e) {
        if ("new".equals(e.getActionCommand())) { //new
            createRFFrame();
        } else {
            //quit
            quit();
        }
    }

    /*
     * ActivateAllAction activates all radios on the panel, essentially changes the color
     * of each ellipse from INACTIVE to ACTIVE
     */
    private class ActivateAllAction extends AbstractAction {
        public ActivateAllAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(1);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        /*
         * This will find the selected tab and extract the DrawEllipses instance from it
         * Then for the actionPerformed it will call activateAll() from DrawEllipses
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            Component comp = desktop.getSelectedFrame();
            if (comp instanceof DrawEllipses){
                DrawEllipses desktop = (DrawEllipses) comp;
                desktop.activateAll();
            }
        }
    }

    /*
     * DeactivateAllAction deactivates all radios on the panel, essentially changes the color
     * of each ellipse from ACTIVE to INACTIVE
     */
    private class DeactivateAllAction extends AbstractAction {
        public DeactivateAllAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        /*
         * This will find the selected tab and extract the DrawPanel2 instance from it
         * Then for the actionPerformed it will call activateAll() from DrawEllipses
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            Component comp = desktop.getSelectedFrame();
            if (comp instanceof DrawEllipses){
                DrawEllipses desktop = (DrawEllipses) comp;
                desktop.deactivateAll();
            }
        }
    }

    /*
     * Define a JPanel that will hold the activate and deactivate all JButtons
     */
    protected JPanel btnPanel() {
        JPanel btnPanel = new JPanel();

        btnPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());

        //Set the layout of the frame to a grid bag layout
        btnPanel.setLayout(new GridBagLayout());

        //Creates constraints variable to hold values to be applied to each aspect of the layout
        GridBagConstraints c = new GridBagConstraints();

        //Column 1
        c.gridx = 0;
        btnPanel.add(new JButton(new ActivateAllAction("Activate All")));

        //Column 2
        c.gridx = 1;
        btnPanel.add(new JButton(new DeactivateAllAction("Deactivate All")));
        return btnPanel;
    }

    //not used currently
    protected JPanel drawPanel() {
        JPanel drawPanel = new JPanel();
        drawPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
        DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
        drawPanel.add(drawEllipses);

        return drawPanel;

    }

    //Create a new internal frame.
    protected void createRFFrame() {
        RFInternalFrame iframe = new RFInternalFrame();
        iframe.setLayout(new BorderLayout());

        DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
        iframe.add(drawEllipses);
        iframe.add(btnPanel(), BorderLayout.SOUTH);

        iframe.setVisible(true);
        desktop.add(iframe);

        try {
            iframe.setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
    }

    protected void createScenarioFrame() {
        ScenarioInternalFrame frame = new ScenarioInternalFrame();
        frame.setLayout(new BorderLayout());

        frame.setVisible(true);
        desktop.add(frame);

        try {
            frame.setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
    }

    //Quit the application.
    protected void quit() {
        System.exit(0);
    }

}

@SuppressWarnings("serial")
class DrawEllipses extends JPanel {
    private double translateX; //
    private double translateY; //
    protected static double scale; //
    private static final int OVAL_WIDTH = 15;
    private static final Color INACTIVE_COLOR = Color.RED;
    private static final Color ACTIVE_COLOR = Color.green;
    private java.util.List<Point> points; //
    private java.util.List<Ellipse2D> ellipses = new ArrayList<>();
    private Map<Ellipse2D, Color> ellipseColorMap = new HashMap<>();

    public DrawEllipses(java.util.List<Point> points) {
        this.points = points; //
        translateX = 0; //
        translateY = 0; //
        scale = 1; //
        setOpaque(true); //
        setDoubleBuffered(true); //


        for (Point p : points) {
            int x = p.x - OVAL_WIDTH / 2;
            int y = p.y - OVAL_WIDTH / 2;
            int w = OVAL_WIDTH;
            int h = OVAL_WIDTH;
            Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
            ellipses.add(ellipse);
            ellipseColorMap.put(ellipse, INACTIVE_COLOR);
        }

        MyMouseAdapter mListener = new MyMouseAdapter();
        addMouseListener(mListener);
        addMouseMotionListener(mListener);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        AffineTransform tx = new AffineTransform(); //
        tx.translate(translateX, translateY); //
        tx.scale(scale, scale); //

        Graphics2D g2 = (Graphics2D) g;
        g2.setTransform(tx);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        for (Ellipse2D ellipse : ellipses) {
            g2.setColor(ellipseColorMap.get(ellipse));
            g2.fill(ellipse);
        }
    }

    private class MyMouseAdapter extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            for (Ellipse2D ellipse : ellipses) {
                if (ellipse.contains(e.getPoint())) {
                    Color c = ellipseColorMap.get(ellipse);
                    c =  (c == INACTIVE_COLOR) ? ACTIVE_COLOR : INACTIVE_COLOR;
                    ellipseColorMap.put(ellipse, c);
                }
            }
            repaint();
        }
    }

    //Used for button click action to change all ellipses to ACTIVE_COLOR
    public void activateAll(){
        for (Ellipse2D ellipse : ellipses){
            ellipseColorMap.put(ellipse, ACTIVE_COLOR);
        }
        repaint();
    }

    //Used for button click action to change all ellipses to INACTIVE_COLOR
    public void deactivateAll(){
        for (Ellipse2D ellipse : ellipses){
            ellipseColorMap.put(ellipse, INACTIVE_COLOR);
        }
        repaint();
    }
}

class RFInternalFrame extends JInternalFrame implements ComponentListener {
    protected static double scale = 1; //
    static int openFrameCount = 0;
    static final int xOffset = 300, yOffset = 0;

    public RFInternalFrame() {
        super("RF Panel #" + (++openFrameCount),
                true, //resizable
                true, //closable
                true, //maximizable
                true);//iconifiable

        setSize(300, 300);
        setMinimumSize(new Dimension(300, 300));
        addComponentListener(this);

        if (openFrameCount == 1) {

            setLocation(0,0);
        }
        else if (openFrameCount <= 4) {

            //Set the window's location.
            setLocation(xOffset * (openFrameCount - 1), yOffset * (openFrameCount - 1));
        }
        else if (openFrameCount == 5) {

            setLocation(xOffset - 300, yOffset + 300);
        }
        else if (openFrameCount == 6) {

            setLocation(xOffset + 600, yOffset + 300);
        }
    }

    @Override
    public void componentResized(ComponentEvent e) {
        String str = "";
        if (getWidth() < 300) {
            str = "0." + getWidth();
        } else {
            str = "1." + (getWidth() - 300);
            System.out.println(getWidth() - 300);
        }
        double dou = Double.parseDouble(str);
        MainPanel.frame.scale = dou;
        repaint();
    }

    @Override
    public void componentMoved(ComponentEvent componentEvent) {

    }

    @Override
    public void componentShown(ComponentEvent componentEvent) {

    }

    @Override
    public void componentHidden(ComponentEvent componentEvent) {

    }
}

class ScenarioInternalFrame extends JInternalFrame {
    static int openFrameCount = 0;
    static final int xOffset = 300, yOffset = 300;

    public ScenarioInternalFrame() {
        super("Test Scenario" + (++openFrameCount),
                true, //resizable
                true, //closable
                true, //maximizable
                true);//iconifiable

        //...Create the GUI and put it in the window...

        //...Then set the window size or call pack...
        setSize(600, 300);

        //Set the window's location.
        setLocation(xOffset, yOffset);
    }
}

2 个答案:

答案 0 :(得分:4)

据我了解,Graphics对象已经包含一个转换,它执行转换以考虑内部框架标题栏的高度。当您替换转换时,您将失去此转换,因此您的代码将绘制在标题栏下方框架的顶部。

  1. 不要更改传递给paintComponent()方法的Graphics对象的属性。而是创建一个可以自定义的Graphics2D对象。
  2. 创建新变换时,需要先添加现有变换,然后再添加新变换。
  3. 基本结构如下:

    super.paintComponent(g);
    
    Graphics2D g2 = (Graphics2D)g.create();
    
    AffineTransform tx = new AffineTransform(); //
    tx.concatenate( g2.getTransform() );
    tx.scale(...); 
    g2.setTransform(tx);
    
    // do custom painting
    
    g2.dispose(); // release Graphics resources
    

    这只会对这幅画有所帮助。你还有几个问题(我无法解决):

    1. 您的比例值永远不会更新。您应该将ComponentListener添加到DrawEllipse面板。您可能希望在面板调整时调用面板中创建一个setScale()方法。

    2. 绘制圆形缩放后,MouseListener将无效。所有圆圈的位置都不同,因为它们已经缩放。您可以在遍历圆圈列表时缩放每个圆圈。

    3. 此外,如果您有问题,请发布适当的SSCCE来证明问题。您有一个关于在面板上使用变换的简单问题。因此,创建一个带有面板的框架,并在面板上绘制几个圆圈以测试该概念。

      所有其他代码与问题无关。菜单项无关紧要,第二个内部框架无关紧要。 MouseListener单击代码无关紧要。我们没有时间阅读100行代码来理解这个问题。

      编辑:

      我改变了代码的顺序。在将变换设置为Graphics对象之前,必须调用tx.scale(...)方法。

答案 1 :(得分:1)

我的经验,在Swing上绘画将使用双缓冲完成。表示您创建绘图缓冲区(即ImageBuffer)。将所有绘图逻辑应用于绘图缓冲区的图形,包括转换,最后,将缓冲区绘制到组件的图形中。

这就是我解决问题的方法......

CC=g++
SHELL:=/bin/bash

all:
        . /usr/share/Modules/init/bash; \
        module load gcc/4.8.1; \
        module load opencv; \
        module load python/2.7.5; \
        $(CC) -std=gnu++11 -lstdc++ -fPIC -shared -o .......

clean:
        rm ../../lib/linux/extract_features.so

尝试一下......希望它有效并有所帮助。