Jump to content

Help? AffineTransform scale around a point


Recommended Posts

Posted

I want to be able to zoom in using scaling but right now it scales relative to the top left corner and I want to be able to scale based on my mouse position. Does any one have any ideas on how I can make this happen?

 

51b8971846720785fc05d60a6ff7209d.gif

 

This is how it looks as of now, scaling into the top left corner.

 

Code:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class PictureFrame {
	
	private ImageCanvas canvas;
	
	public PictureFrame(String filePath) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createFrame(filePath);
			}
		});
	}
	
	public PictureFrame(Image image) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createFrame(image);
			}
		});
	}

	private void createFrame(String filePath) {
		JFrame frame = new JFrame("Picture Frame Test");
		frame.setSize(800, 600);
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		canvas = new ImageCanvas(filePath, frame.getWidth(), frame.getHeight());

		frame.getContentPane().add(canvas);
		frame.setVisible(true);
	}
	
	private void createFrame(Image image) {
		JFrame frame = new JFrame("Picture Frame Test");
		frame.setSize(800, 600);
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		canvas = new ImageCanvas(image, frame.getWidth(), frame.getHeight());

		frame.getContentPane().add(canvas);
		frame.setVisible(true);
	}

	private class ImageCanvas extends JComponent {

		private Image image;
		private AffineTransform transformer = new AffineTransform();

		ImageCanvas(String filePath, int width, int height) {
			try {
				image = ImageIO.read(new File(filePath));
				setSize(width, height);
				EventListener eventListener = new EventListener();
				addMouseListener(eventListener);
				addMouseWheelListener(eventListener);
				addMouseMotionListener(eventListener);
			} catch (IOException e) {
				System.out.println("Unable to read image file!");
			}

		}
		
		ImageCanvas(Image image, int width, int height) {
				this.image = image;
				setSize(width, height);
				EventListener eventListener = new EventListener();
				addMouseListener(eventListener);
				addMouseWheelListener(eventListener);
				addMouseMotionListener(eventListener);

		}
		
		public double getMinScale(){
			
			double limitX = (double) canvas.getWidth() / (double) canvas.getImage().getWidth(null);
			double limitY = (double) canvas.getHeight() / (double) canvas.getImage().getHeight(null);
			
			return (limitX > limitY) ? limitX : limitY;
		}
		
		public Image getImage(){
			return image;
		}
		
		public AffineTransform getTransformer(){
			return transformer;
		}

		@[member=Override]
		protected void paintComponent(Graphics g) {
			try {
				Graphics2D graphics = (Graphics2D) g;
				graphics.setTransform(transformer);
				Point mouse = MouseInfo.getPointerInfo().getLocation();
				SwingUtilities.convertPointFromScreen(mouse, this);
				graphics.drawImage(image, 0, 0, image.getWidth(null), image.getHeight(null), null);
				graphics.setColor(Color.BLACK);
				graphics.fillRect(0, 0, 90, 15);
				graphics.setColor(Color.WHITE);
				graphics.drawString(String.format("X: %.0f Y: %.0f", mouse.getX(), mouse.getY()), 10, 10);
				Thread.sleep(15);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	private class EventListener implements MouseListener, MouseWheelListener, MouseMotionListener {
		
		private Point startPoint, endPoint;
		private double deltaX, deltaY;
		
		
		@[member=Override]
		public void mouseWheelMoved(MouseWheelEvent e) {
			AffineTransform transformer = canvas.getTransformer();
			
			if(e.getWheelRotation() < 0)
				transformer.setToScale(transformer.getScaleX() + .05, transformer.getScaleY() + .05);
			else
				transformer.setToScale(Math.max(canvas.getMinScale(), transformer.getScaleX() - .05), Math.max(canvas.getMinScale(), transformer.getScaleY() - .05));
			
			
			 transformer.translate(deltaX, deltaY);
			 canvas.repaint();
		}
		
		@[member=Override]
		public void mouseClicked(MouseEvent e) {
			
		}

		@[member=Override]
		public void mousePressed(MouseEvent e) {
			startPoint = e.getPoint();
		}

		@[member=Override]
		public void mouseReleased(MouseEvent e) {

		}

		@[member=Override]
		public void mouseEntered(MouseEvent e) {

		}

		@[member=Override]
		public void mouseExited(MouseEvent e) {

		}

		@[member=Override]
		public void mouseDragged(MouseEvent e) {
			AffineTransform transformer = canvas.getTransformer();
			 try {
		            endPoint = e.getPoint();
		            Point dragStart = transformPoint(startPoint);
		            Point dragEnd = transformPoint(endPoint);
		            /*
		            double dx = dragEnd.getX() - dragStart.getX();
		            double dy = dragEnd.getY() - dragStart.getY();
		            deltaX += dx;
		            deltaY += dy;
		            */
		             double deltaW = (double) canvas.getWidth() - (double) canvas.getImage().getWidth(null) * transformer.getScaleX();
					 double deltaH = (double) canvas.getHeight() - (double) canvas.getImage().getHeight(null) * transformer.getScaleY();
					 double px = Math.max(deltaW, Math.min(0, deltaX + (dragEnd.getX() - dragStart.getX())));
					 double dx = px - deltaX;
					 double py = Math.max(deltaH, Math.min(0, deltaY + (dragEnd.getY() - dragStart.getY())));
					 double dy = py - deltaY;
					 deltaX = px;
					 deltaY = py;
		            
		            transformer.translate(dx, dy);
		            startPoint = endPoint;
		            endPoint = null;
		        } catch (NoninvertibleTransformException ex) {
		            ex.printStackTrace();
		        }
			canvas.repaint();
		}

		@[member=Override]
		public void mouseMoved(MouseEvent e) {
			canvas.repaint();
		}
		
		private Point transformPoint(Point p1) throws NoninvertibleTransformException{
			AffineTransform transformer = canvas.getTransformer();
			AffineTransform inverse = transformer.createInverse();
	        Point p2 = new Point();
	        inverse.transform(p1, p2);
	        return p2;
		}

	}

}

Posted (edited)

I reckon you should translate the transformed image according to your mouse.

				Graphics2D graphics = (Graphics2D) g;
				graphics.setTransform(transformer);
				Point mouse = MouseInfo.getPointerInfo().getLocation();
				SwingUtilities.convertPointFromScreen(mouse, this);
                                > graphics.translate(-mouse.getX() * graphics.getTransform().getScaleX(), -mouse.getY() * graphics.getTransform().getScaleY());
				graphics.drawImage(image, 0, 0, image.getWidth(null), image.getHeight(null), null);
                                > graphics.translate(mouse.getX() * graphics.getTransform().getScaleX(), mouse.getY() * graphics.getTransform().getScaleY());
				graphics.setColor(Color.BLACK);
				graphics.fillRect(0, 0, 90, 15);
				graphics.setColor(Color.WHITE);
				graphics.drawString(String.format("X: %.0f Y: %.0f", mouse.getX(), mouse.getY()), 10, 10);

Just a guess tho, haven't tried compiling your code

EDIT: remember to translate back :boge:

Edited by FrostBug
Posted

try something like this

            AffineTransform transformer = canvas.getTransformer();
            Point p = e.getPoint();
            int mouseX = p.x;
            int mouseY = p.y;
            double zoom = e.getUnitsToScroll() < 0 ? 1.05 : 1 / 1.05;
            deltaX = (int) ((transformer.getTranslateX() * zoom) - ((mouseX * zoom) - mouseX));
            deltaY = (int) ((transformer.getTranslateY() * zoom) - ((mouseY * zoom) - mouseY));
            transformer.setToScale(transformer.getScaleX() * zoom, transformer.getScaleY() * zoom);
            transformer.translate(deltaX, deltaY);
            canvas.repaint();

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...