简体   繁体   中英

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. Basically my goal is the make a JInternal Frame that scales the Graphics2D objects when it is minimized. 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? 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:

  1. Set a JDesktopPane as the JFrame 's contentpane.
  2. Create a JInternalFrame and add it do the 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.

Here is this implemented in your code (without any AffineTransform yet, just be basic concept):

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 . 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 ). Use the code above, just change the MyInternalFrame class to this:

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:

I'll give you an idea of how you could make it resize (change the componentResized method to this):

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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