// Charles A. Loomis, Jr., and University of California, Santa Cruz, // Copyright (c) 2000 package org.freehep.swing.graphics; import java.awt.Container; import java.awt.Insets; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.RectangularShape; /** * This class is a collection of static methods which are useful for * implementations of the PanelArtist interface. Most methods return * an AffineTransform which will perform some common operation on a * window. * * @author Charles Loomis * @version $Id: PanelArtistUtilities.java 8584 2006-08-10 23:06:37Z duns $ */ public class PanelArtistUtilities { /** * This returns an affine transform which is appropriate for * modifying an existing one for a change in the window size. * (NOTE: that this transform should be pre-concatenated with the * existing one!) The returned transform will satisfy the * following constraints: the centerpoint of the old window will * map to the center point of the new window, a uniform scaling * will be used in both the x and y-directions, the entire visible * region of in the old window will be visible in the new * window. */ public static AffineTransform getResizeTransform(int oldWidth, int oldHeight, int newWidth, int newHeight) { // First calculate the scaling which is necessary. double scale = Math.min((double) newWidth/(double) oldWidth, (double) newHeight/(double) oldHeight); // Now calculate the translation which is necessary. double tx = 0.5*(newWidth-scale*oldWidth); double ty = 0.5*(newHeight-scale*oldHeight); // Now create the transform and return it. return new AffineTransform(scale,0.,0.,scale,tx,ty); } /** * This returns an affine transform which is appropriate for * modifying an existing one for a change in the window size. * (NOTE: that this transform should be pre-concatenated with the * existing one!) The final transform will map the four corners * of the old window to the four corners of the new window. In * general, the scaling in the x and y direction will be * different. */ public static AffineTransform getStretchTransform(int oldWidth, int oldHeight, int newWidth, int newHeight) { // First calculate the scaling which is necessary. double scaleX = (double) newWidth/(double) oldWidth; double scaleY = (double) newHeight/(double) oldHeight; // Now create the transform and return it. return new AffineTransform(scaleX,0.,0.,scaleY,0.,0.); } /** * This returns an affine transform which will keep the center point in * the center and scale the x- and y-directions uniformly by the factor * given. Note that a value of 1 corresponds to the identify transform, * values less than 1 will "zoom out," and values greater than 1 will * "zoom in." (NOTE: that this transform should be pre-concatenated with * the existing one!) */ public static AffineTransform getZoomTransform(double zoomFactor, int width, int height) { double tx = width/2.*(1.-zoomFactor); double ty = height/2.*(1-zoomFactor); return new AffineTransform(zoomFactor,0.,0.,zoomFactor,tx,ty); } /** * This returns an affine transform which will flip the horizontal * axis around. (NOTE: that this transform should be * pre-concatenated with the existing one!) The returned * transform will maintain the centerpoint of the window and flip * the direction of the x-axis. */ public static AffineTransform getXFlipTransform(int width) { return new AffineTransform(-1.,0.,0.,1.,(double) width,0.); } /** * This returns an affine transform which will flip the vertical * axis around. (NOTE: that this transform should be * pre-concatenated with the existing one!) The returned * transform will maintain the centerpoint of the window and flip * the direction of the y-axis. */ public static AffineTransform getYFlipTransform(int height) { return new AffineTransform(1.,0.,0.,-1.,0.,(double) height); } /** * This returns an affine transform which will center the given * point in the window. (NOTE: that this transform should be * pre-concatenated with the existing one!) The returned * transform will move the given point to the center and maintain * the x and y scales. */ public static AffineTransform getCenteringTransform(int newX, int newY, int width, int height) { return new AffineTransform(1.,0.,0.,1., width/2.-newX, height/2.-newY); } /** * This returns an affine transform which will rotate the contents * of the window by 90 degrees. (NOTE: that this transform should * be pre-concatenated with the existing one!) The returned * transform will rotate the contents of the window by 90 degrees * while keeping the centerpoint the same. The x and y-scaling * will be adjusted to keep the same area visible. */ public static AffineTransform getCCWRotateTransform(int width, int height) { return new AffineTransform(0., -((double) height)/width, ((double) width)/height, 0., 0., (double) height); } /** * This returns an affine transform which will rotate the contents * of the window by -90 degrees. (NOTE: that this transform should * be pre-concatenated with the existing one!) The returned * transform will rotate the contents of the window by -90 degrees * while keeping the centerpoint the same. The x and y-scaling * will be adjusted to keep the same area visible. */ public static AffineTransform getCWRotateTransform(int width, int height) { return new AffineTransform(0., ((double) height)/width, -((double) width)/height, 0., (double) width, 0.); } /** * This modifies the supplied affine transform so that the * rectangle given by realBounds will fit inside of the rectangle * given by windowBounds. The center of the realBounds rectangle * will coincide with that of the windowBounds rectangle. * * NOTE: THIS ONLY CORRECTLY HANDLES THE CASE WHEN THE USER SPACE * RECTANGLE IS CENTERED ON THE ORIGIN. * * @param transform the transform which will be modified * @param realBounds the user space rectangle * @param windowBounds the window to map the user rectangle to */ public static void getFittingTransform(AffineTransform transform, RectangularShape realBounds, RectangularShape windowBounds) { if (realBounds==null || windowBounds==null) { transform.setToIdentity(); } else { // Get the dimensions of the windows. double realWidth = realBounds.getWidth(); double realHeight = realBounds.getHeight(); double windowWidth = windowBounds.getWidth(); double windowHeight = windowBounds.getHeight(); if (realWidth>0 && realHeight>0) { // Get the necessary scaling factor. double scaleWidth = windowWidth/realWidth; double scaleHeight = windowHeight/realHeight; double scale = Math.min(scaleWidth,scaleHeight); transform.setTransform(scale,0.,0.,-scale, windowWidth/2.,windowHeight/2.); } else { transform.setToIdentity(); } } } /** * This modifies the supplied affine transform so that the * rectangle given by realBounds will fit exactly inside the * rectangle given by windowBounds. The origins of the realBounds * and the windowBounds coincide; the opposite corner corresponds * to (x0+dx,y0-dy) for the real coordinates. * * @param transform the transform which will be modified * @param realBounds the user space rectangle * @param windowBounds the window to map the user rectangle to */ public static void getFillingTransform(AffineTransform transform, RectangularShape realBounds, RectangularShape windowBounds) { if (realBounds==null || windowBounds==null) { transform.setToIdentity(); } else { // Get the dimensions of the windows. double realWidth = realBounds.getWidth(); double realHeight = realBounds.getHeight(); double windowWidth = windowBounds.getWidth(); double windowHeight = windowBounds.getHeight(); if (realWidth>0 && realHeight>0) { // Get the necessary scaling factor. double scaleWidth = windowWidth/realWidth; double scaleHeight = windowHeight/realHeight; transform.setTransform(scaleWidth,0.,0.,-scaleHeight, -scaleWidth*realBounds.getX(), scaleHeight*realBounds.getY()); } else { transform.setToIdentity(); } } } /** * This returns the "local" bounds of a component. This does the * same calculation as the method of the same name in * SwingUtilities, but this doesn't create a new Rectangle, but * instead overwrites the one passed in. * * @param bounds rectangle to modify with the given component's * bounds (will create new Rectangle if this is null) * @param c component to get the bounds from * * @return convenience reference to the rectangle passed in (or * the created rectangle) */ public static Rectangle getLocalBounds(Rectangle bounds, Container c) { // Create a new Rectangle only if necessary. if (bounds==null) bounds = new Rectangle(); // Get the insets of the components. Insets insets = c.getInsets(); // Set the origin to (0,0) and the width and height to those // of the given component. bounds.setBounds(0,0, c.getWidth()-(insets.left+insets.right), c.getHeight()-(insets.top+insets.bottom)); // Return the given rectangle (or the created one if this was // necessary). return bounds; } }