简体   繁体   English

在Graphics2D对象上使用仿射变换

[英]Using Affine Transform on Graphics2D Objects

I am new to AffineTransform but I spent a couple of hours and kind of figured out how to get it to do what I want. 我是AffineTransform的新手,但是我花了几个小时,并弄清楚了如何使它完成我想做的事情。 Basically my goal is the make a JInternal Frame that scales the Graphics2D objects when it is minimized. 基本上,我的目标是制作一个JInternal Frame,以在最小化Graphics2D对象时缩放该对象。 For proof of concept I have created some code that scales with the mouse wheel and is translated on a mouse drag. 为了概念验证,我创建了一些代码,该代码可随鼠标滚轮缩放并在鼠标拖动时进行翻译。

My question is, how can I package this into a JInternalFrame that scales when the JInternalFrame is minimized? 我的问题是,如何将其打包到最小化JInternalFrame时可缩放的JInternalFrame中? Also, is there a better way to do what I want to do? 另外,是否有更好的方法来做我想做的事?

Self contained example: 自包含的示例:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;

public class AffineTransformTest {

    private static TransformingCanvas canvas;

    public static void main(String[] args) {
        TransformingCanvas.initializePointList();
        JFrame frame = new JFrame();
        canvas = new TransformingCanvas(TransformingCanvas.POINT_LIST);
        TranslateHandler translator = new TranslateHandler();
        canvas.addMouseListener(translator);
        canvas.addMouseMotionListener(translator);
        canvas.addMouseWheelListener(new ScaleHandler());
        frame.setLayout(new BorderLayout());
        frame.getContentPane().add(canvas, BorderLayout.CENTER);
        frame.setSize(750, 750);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setVisible(true);
    }

    private static class TransformingCanvas extends JComponent {
        private double translateX;
        private double translateY;
        private double scale;
        private final int PREF_W = 800; //Window width
        private final int PREF_H = 800; //Window height
        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 static java.util.List<Point> POINT_LIST = new ArrayList<>();

        /*
        * This loop will initialize POINT_LIST with the set of points for drawing the ellipses.
        * The for each loop initializes points for the top row and the second for loop draws the
        * right triangle.
        */
        protected static void initializePointList() {

            int ellipsePointsYCoordinate[] = {140, 200, 260, 320, 380, 440, 500, 560, 620};
            int ellipsePointsXCoordinate[] = {140, 200, 260, 320, 380, 440, 500, 560, 620, 680};
            int xx = 80;

            for (int aXt : ellipsePointsXCoordinate) {
                POINT_LIST.add(new Point(aXt, xx));
            }

            for (int i = 0; i < ellipsePointsYCoordinate.length; i++) {
                for (int j = i; j < ellipsePointsYCoordinate.length; j++) {
                    POINT_LIST.add(new Point(ellipsePointsXCoordinate[i], ellipsePointsYCoordinate[j]));
                }
            }
        }

            TransformingCanvas() {
            translateX = 0;
            translateY = 0;
            scale = 1;
            setOpaque(true);
            setDoubleBuffered(true);
        }

        public TransformingCanvas(java.util.List<Point> points) {
            this.points = points;
            int OVAL_WIDTH = 30;

            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);
            }
        }

        @Override public void paint(Graphics g) {

            AffineTransform tx = new AffineTransform();
            tx.translate(translateX, translateY);
            tx.scale(scale, scale);
            Graphics2D g2 = (Graphics2D) g;
            g2.fillRect(0,0, getWidth(), getHeight());
            g2.setTransform(tx);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            for (Ellipse2D ellipse : ellipses) {
                g2.setColor(ellipseColorMap.get(ellipse));
                g2.fill(ellipse);
                g2.setColor(Color.BLACK);
                g2.setStroke(new BasicStroke(2));
                g2.draw(ellipse);
            }

            /*
            * Set the font characteristics, color, and draw the row labels.
            */
            g.setFont(new Font("TimesRoman", Font.BOLD, 18));
            g.setColor(Color.BLACK);

            //NOTE to self: add label DrawStrings back

            //Draws a 3DRect around the top row of ellipse2D objects
            g2.setColor(Color.lightGray);
            g2.draw3DRect(120, 60, 580, 40, true);
            g2.draw3DRect(121, 61, 578, 38, true);
            g2.draw3DRect(122, 62, 576, 36, true);
            //super.paint(g);
        }
    }

    private static class TranslateHandler implements MouseListener,
            MouseMotionListener {
        private int lastOffsetX;
        private int lastOffsetY;

        public void mousePressed(MouseEvent e) {
            // capture starting point
            lastOffsetX = e.getX();
            lastOffsetY = e.getY();
        }

        public void mouseDragged(MouseEvent e) {

            // new x and y are defined by current mouse location subtracted
            // by previously processed mouse location
            int newX = e.getX() - lastOffsetX;
            int newY = e.getY() - lastOffsetY;

            // increment last offset to last processed by drag event.
            lastOffsetX += newX;
            lastOffsetY += newY;

            // update the canvas locations
            canvas.translateX += newX;
            canvas.translateY += newY;

            // schedule a repaint.
            canvas.repaint();
        }

        public void mouseClicked(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
        public void mouseMoved(MouseEvent e) {}
        public void mouseReleased(MouseEvent e) {}
    }

    private static class ScaleHandler implements MouseWheelListener {
        public void mouseWheelMoved(MouseWheelEvent e) {
            if(e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {

                // make it a reasonable amount of zoom
                // .1 gives a nice slow transition
                canvas.scale += (.1 * e.getWheelRotation());
                // don't cross negative threshold.
                // also, setting scale to 0 has bad effects
                canvas.scale = Math.max(0.00001, canvas.scale);
                canvas.repaint();
            }
        }
    }
}  

I don't fully get why you would want to scale the Graphics when they're not visible, but here is a rough instruction how to do it: 我不完全理解为什么您希望在不可见的Graphics时缩放它们,但是这里有一个粗略的说明:

  1. Set a JDesktopPane as the JFrame 's contentpane. JDesktopPane设置为JFrame的contentpane。
  2. Create a JInternalFrame and add it do the JDesktopPane 创建一个JInternalFrame并将其添加到JDesktopPane
  3. Add a InternalFrameListener to the internal frame and then you can put inside the public void internalFrameIconified(InternalFrameEvent arg0) method everything you want to do if the internal frame was minimized. 将内部框架InternalFrameListener添加到内部框架,然后可以将public void internalFrameIconified(InternalFrameEvent arg0)方法放入内部框架最小化时要执行的所有操作。

Here is this implemented in your code (without any AffineTransform yet, just be basic concept): 这是在您的代码中实现的(尚无任何AffineTransform ,仅是基本概念):

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;

public class AffineTransformTest {

    private static TransformingCanvas canvas;

    public static void main(String[] args) {
        TransformingCanvas.initializePointList();
        JFrame frame = new JFrame();
        canvas = new TransformingCanvas(TransformingCanvas.POINT_LIST);
        JDesktopPane desktop = new JDesktopPane();
        TranslateHandler translator = new TranslateHandler();
        canvas.addMouseListener(translator);
        canvas.addMouseMotionListener(translator);
        canvas.addMouseWheelListener(new ScaleHandler());
        MyInternalFrame iFrame = new MyInternalFrame();
        iFrame.setLayout(new BorderLayout());
        iFrame.add(canvas, BorderLayout.CENTER);
        iFrame.setVisible(true);
        desktop.add(iFrame);
        frame.setContentPane(desktop);
        frame.setSize(750, 750);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setVisible(true);
    }

    private static class TransformingCanvas extends JComponent {
        private double translateX;
        private double translateY;
        private double scale;
        private final int PREF_W = 800; // Window width
        private final int PREF_H = 800; // Window height
        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 static java.util.List<Point> POINT_LIST = new ArrayList<>();

        /*
         * This loop will initialize POINT_LIST with the set of points for
         * drawing the ellipses. The for each loop initializes points for the
         * top row and the second for loop draws the right triangle.
         */
        protected static void initializePointList() {

            int ellipsePointsYCoordinate[] = { 140, 200, 260, 320, 380, 440,
                    500, 560, 620 };
            int ellipsePointsXCoordinate[] = { 140, 200, 260, 320, 380, 440,
                    500, 560, 620, 680 };
            int xx = 80;

            for (int aXt : ellipsePointsXCoordinate) {
                POINT_LIST.add(new Point(aXt, xx));
            }

            for (int i = 0; i < ellipsePointsYCoordinate.length; i++) {
                for (int j = i; j < ellipsePointsYCoordinate.length; j++) {
                    POINT_LIST.add(new Point(ellipsePointsXCoordinate[i],
                            ellipsePointsYCoordinate[j]));
                }
            }
        }

        TransformingCanvas() {
            translateX = 0;
            translateY = 0;
            scale = 1;
            setOpaque(true);
            setDoubleBuffered(true);
        }

        public TransformingCanvas(java.util.List<Point> points) {
            this.points = points;
            int OVAL_WIDTH = 30;

            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);
            }
        }

        @Override
        public void paint(Graphics g) {

            AffineTransform tx = new AffineTransform();
            tx.translate(translateX, translateY);
            tx.scale(scale, scale);
            Graphics2D g2 = (Graphics2D) g;
            g2.fillRect(0, 0, getWidth(), getHeight());
            g2.setTransform(tx);
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            for (Ellipse2D ellipse : ellipses) {
                g2.setColor(ellipseColorMap.get(ellipse));
                g2.fill(ellipse);
                g2.setColor(Color.BLACK);
                g2.setStroke(new BasicStroke(2));
                g2.draw(ellipse);
            }

            /*
             * Set the font characteristics, color, and draw the row labels.
             */
            g.setFont(new Font("TimesRoman", Font.BOLD, 18));
            g.setColor(Color.BLACK);

            // NOTE to self: add label DrawStrings back

            // Draws a 3DRect around the top row of ellipse2D objects
            g2.setColor(Color.lightGray);
            g2.draw3DRect(120, 60, 580, 40, true);
            g2.draw3DRect(121, 61, 578, 38, true);
            g2.draw3DRect(122, 62, 576, 36, true);
            // super.paint(g);
        }
    }

    private static class TranslateHandler implements MouseListener,
            MouseMotionListener {
        private int lastOffsetX;
        private int lastOffsetY;

        public void mousePressed(MouseEvent e) {
            // capture starting point
            lastOffsetX = e.getX();
            lastOffsetY = e.getY();
        }

        public void mouseDragged(MouseEvent e) {

            // new x and y are defined by current mouse location subtracted
            // by previously processed mouse location
            int newX = e.getX() - lastOffsetX;
            int newY = e.getY() - lastOffsetY;

            // increment last offset to last processed by drag event.
            lastOffsetX += newX;
            lastOffsetY += newY;

            // update the canvas locations
            canvas.translateX += newX;
            canvas.translateY += newY;

            // schedule a repaint.
            canvas.repaint();
        }

        public void mouseClicked(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseMoved(MouseEvent e) {
        }

        public void mouseReleased(MouseEvent e) {
        }
    }

    private static class ScaleHandler implements MouseWheelListener {
        public void mouseWheelMoved(MouseWheelEvent e) {

        }
    }

    static class MyInternalFrame extends JInternalFrame implements
            InternalFrameListener {

        public MyInternalFrame() {
            super("iFrame", true, // resizable
                    true, // closable
                    true, // maximizable
                    true);// iconifiable
            setSize(300, 300);
            addInternalFrameListener(this);

        }

        @Override
        public void internalFrameActivated(InternalFrameEvent arg0) {

        }

        @Override
        public void internalFrameClosed(InternalFrameEvent arg0) {

        }

        @Override
        public void internalFrameClosing(InternalFrameEvent arg0) {

        }

        @Override
        public void internalFrameDeactivated(InternalFrameEvent arg0) {

        }

        @Override
        public void internalFrameDeiconified(InternalFrameEvent arg0) {

        }

        @Override
        public void internalFrameIconified(InternalFrameEvent arg0) {
            System.out.println("Minimized");


            //What you want to  do
        }

        @Override
        public void internalFrameOpened(InternalFrameEvent arg0) {

        }
    }
}

If now you still can't achieve what you want, please explain further, as I don't really see the point of scaling Graphics that are not visible. 如果现在您仍然无法实现所需的功能,请进一步解释,因为我看不到扩展看不见的图形的要点。

Edit: 编辑:

Now that I understand what the real question was: You can add a ComponentListener to your JInternalFrame . 现在,我了解了真正的问题是:您可以将ComponentListener添加到JInternalFrame Then put inside the public void componentResized(ComponentEvent e) { method everything that you want to change (probably the variables of the width and height of the Graphics ). 然后,将要更改的所有内容(可能是Graphics的width和height的变量public void componentResized(ComponentEvent e) {放入public void componentResized(ComponentEvent e) {方法中。 Use the code above, just change the MyInternalFrame class to this: 使用上面的代码,只需将MyInternalFrame类更改为此:

static class MyInternalFrame extends JInternalFrame implements
        ComponentListener {

    public MyInternalFrame() {
        super("iFrame", true, // resizable
                true, // closable
                true, // maximizable
                true);// iconifiable
        setSize(300, 300);
        addComponentListener(this);

    }

    @Override
    public void componentHidden(ComponentEvent e) {

    }

    @Override
    public void componentMoved(ComponentEvent e) {

    }

    @Override
    public void componentResized(ComponentEvent e) {
        System.out.println("Resized");
        System.out.println("Width: " + getWidth());
        System.out.println("Height: " + getHeight());
        System.out.println();

    }

    @Override
    public void componentShown(ComponentEvent e) {

    }


}

Edit 2: 编辑2:

I'll give you an idea of how you could make it resize (change the componentResized method to this): 我将为您提供一个如何调整其大小的想法(将componentResized方法更改为此):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM