Java 用仿射变换缩放图形

Java 用仿射变换缩放图形,java,swing,affinetransform,Java,Swing,Affinetransform,我正在用Swing制作一个GUI,它使用仿射变换缩放绘制在JInternalFrame上的图形2D对象。问题是,在目前的状态下,它是有缺陷的,我不知道为什么 为什么我的代码不能正确伸缩?为什么图形会“跳转”到面板顶部 以下是我的独立示例: import javax.swing.*; import java.awt.event.*; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.Ellip

我正在用Swing制作一个GUI,它使用仿射变换缩放绘制在JInternalFrame上的图形2D对象。问题是,在目前的状态下,它是有缺陷的,我不知道为什么

为什么我的代码不能正确伸缩?为什么图形会“跳转”到面板顶部

以下是我的独立示例:

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);
    }
}
import javax.swing.*;
导入java.awt.event.*;
导入java.awt.*;
导入java.awt.geom.AffineTransform;
导入java.awt.geom.Ellipse2D;
导入java.util.*;
公共类主面板扩展JFrame实现ActionListener{
私有静态最终双版本=1.0;
私人JDesktopPane桌面;
公共静态框架;
private java.util.List POINT_List=Arrays.asList(
//第一排
新点(50,30),
新点(70,30),
新点(90,30),
新点(110,30),
新点(130,30),
新点(150,30),
新点(170,30),
新点(190,30),
新点(210,30),
新点(230,30),
//无线电圈
新点(140,60),
新点(120,80),
新点(100100),
新点(100120),
新点(120140),
新点(140160),
新点(160140),
新点(180、120),
新点(180100),
新点(160,80);;
公共静态void main(字符串[]args){
调用器(()->createAndShowGui());
}
私有静态void createAndShowGui(){
JFrame=新的主面板();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationByPlatform(假);
frame.setVisible(true);
}
公共主面板(){
超级(“主面板”+版本);
//使大窗口每边缩进50像素
//屏幕的另一端。
内插=50;
维度screenSize=Toolkit.getDefaultToolkit().getScreenSize();
立根(插图、插图、,
屏幕尺寸.宽度-插图*7,
屏幕尺寸.高度-插图*4);
//设置GUI。
desktop=new JDesktopPane();//专门的分层窗格
桌面.背景(颜色.深灰色);
createRFFrame();//创建第一个RFFrame
createScenarioFrame();//创建ScenarioFrame
setContentPane(桌面);
setJMenuBar(createMenuBar());
//拖得快一点,但可能更难看。
setDragMode(JDesktopPane.OUTLINE\u DRAG\u模式);
}
受保护的JMenuBar createMenuBar(){
JMenuBar menuBar=新的JMenuBar();
//设置单独菜单。
JMenu菜单=新JMenu(“文件”);
menu.set助记符(KeyEvent.VK_D);
菜单栏。添加(菜单);
//设置第一个菜单项。
JMenuItem菜单项=新的JMenuItem(“添加面板”);
menuItem.set助记符(KeyEvent.VK_N);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_N、ActionEvent.ALT_掩码);
menuItem.setActionCommand(“新”);
menuItem.addActionListener(此);
menu.add(menuItem);
//设置第二个菜单项。
menuItem=新项目(“退出”);
menuItem.set助记符(KeyEvent.VK_Q);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_Q,ActionEvent.ALT_MASK));
menuItem.setActionCommand(“退出”);
menuItem.addActionListener(此);
menu.add(menuItem);
返回菜单栏;
}
//对菜单选择做出反应。
已执行的公共无效操作(操作事件e){
如果(“new”.equals(e.getActionCommand()){//new
createRFFrame();
}否则{
//退出
退出();
}
}
/*
*ActivateLaction激活面板上的所有收音机,从本质上改变颜色
*从非活动到活动的每个椭圆的
*/
私有类ActivateLaction扩展了AbstractAction{
公共ActivateLaction(字符串名称){
超级(姓名);
int助记符=(int)name.charAt(1);
putValue(助记符键,助记符);
}
/*
*这将找到所选选项卡并从中提取抽屉实例
*然后,对于执行的操作,它将从抽屉调用activateAll()
*/
@凌驾
已执行的公共无效操作(操作事件e){
Component comp=desktop.getSelectedFrame();
if(抽屉的组件实例){
抽屉桌面=(抽屉)组件;
desktop.activateAll();
}
}
}
/*
*停用Lactivation停用面板上的所有收音机,本质上改变颜色
*从活动到非活动的每个椭圆的
*/
私有类去激活扩展了AbstractAction{
公共停用(字符串名称){
超级(姓名);
int助记符=(int)name.charAt(0);
putValue(助记符键,助记符);
}
/*
*这将找到所选选项卡并从中提取DrawPanel2实例
*然后,对于执行的操作,它将从抽屉调用activateAll()
*/
@凌驾
已执行的公共无效操作(操作事件e){
Component comp=desktop.getSelectedFrame();
if(抽屉的组件实例){
抽屉桌面=(抽屉)组件;
desktop.deactivateAll();
}
}
}
/*
*定义一个JPanel,它将保存激活和停用所有JButton
*/
受保护的JPanel btnPanel(){
JPanel btnPanel=新的JPanel();
btnPanel.setBorder(BorderFactory.createLoweredSoftBevelOrder());
//将框架布局设置为网格袋布局
btnPanel.setLayout(新GridBagL
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
class DrawEllipses extends JComponent { // I change from JPanel to JComponent, this might not be necessary though...
...
...
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    // create the drawing buffer.
    BufferedImage bi = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics big = bi.getGraphics();

    // prepare transform
    AffineTransform tx = new AffineTransform(); //
    tx.translate(translateX, translateY); //
    tx.scale(scale, scale); //

    // get the buffer graphics and paint the background white.
    Graphics2D g2 = (Graphics2D) big;
    g2.setColor(Color.WHITE);
    g2.fillRect(0, 0, this.getWidth(), this.getHeight());

    // apply drawing logic to the Graphics of the buffer
    g2.setTransform(tx);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    for (Ellipse2D ellipse : ellipses) {
        g2.setColor(ellipseColorMap.get(ellipse));
        g2.fill(ellipse);
    }

    // finally, draw the buffer to the component graphics.
    g.drawImage(bi, 0, 0, null);
}