Source code for TransformManager.java

What follows is the highlighted source code with comments. You can also download this file directly: TransformManager.java.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package coordinatechanges;

import java.awt.Component;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
 *
 * @author pat
 */
public class TransformManager {

    private class Listener implements ComponentListener {

        public Listener() {
        }

        @Override
        public void componentHidden(ComponentEvent ce) {
        }

        @Override
        public void componentMoved(ComponentEvent ce) {
        }

        @Override
        public void componentResized(ComponentEvent ce) {
            updateTransform();
        }

        @Override
        public void componentShown(ComponentEvent ce) {
        }
    }
    //// FUNCTIONS RELATED TO THE BOUNDING BOX AND COORDINATE CHANGES
    // We guarantee that this box will be displayed.
    private Rectangle2D display_box;
    // This affine transformation converts from math coordinates to screen coordinates.
    private AffineTransform current_transform = new AffineTransform();
    private Component c;
    private TransformChangedListener listener = null;

    public TransformManager(Component c, Rectangle2D initial_display_box) {
        this.c = c;
        display_box = initial_display_box;
        c.addComponentListener(new Listener());
        updateTransform();
    }

    public void setListener(TransformChangedListener tcl) {
        listener = tcl;
    }

    public Rectangle2D getDisplayBox() {
        return display_box;
    }

    public void setDisplayBox(Rectangle2D r) {
        display_box = r;
        updateTransform();
    }

    private void updateTransform() {
        /*
         * The component has pixels whose x-coordinates are numbered 0 to
         * getWidth()-1 as we move rightward. The y-coordinates of these pixels
         * increases from 0 to getHeight()-1 as we move downward.
         *
         * We will return the transformation which takes the bounding box for
         * our curve into the rectangle representing coordinates for our
         * component on the screen. To move this box into the screen we use the
         * following steps: 1. We translate our box, moving the center of our
         * box to the origin. 2. We scale the box by a constant so that the
         * image of our box has width and height less than or equal to the width
         * and height of this component. 3. We negate the y-coordinate, because
         * in mathematics the y-coordinate increases as we move upward. 4. We
         * translate the origin so that it is moved to the center of the
         * component.
         *
         * These steps are carried out below.
         */

        // Construct a transformation which translates the plane moving the center of 
        // our box to the origin.
        AffineTransform transform =
                AffineTransform.getTranslateInstance(-display_box.getCenterX(),
                -display_box.getCenterY());

        // The number scale is the minimal ratio of screen dimensions to bounding box dimensions.
        double scale;
        if ((display_box.getWidth() != 0) && (c.getWidth() != 0)) {
            if ((display_box.getHeight() != 0) && (c.getHeight() != 0)) {
                scale = Math.min(c.getWidth() / display_box.getWidth(),
                        c.getHeight() / display_box.getHeight());
            } else {
                scale = c.getWidth() / display_box.getWidth();
            }
        } else {
            if ((display_box.getHeight() != 0) && (c.getHeight() != 0)) {
                scale = c.getHeight() / display_box.getHeight();
            } else {
                scale = 1;
            }
        }

        // The following line post-composes by scaling the plane by the number "scale".
        // Because we use the same constant in each coordinate, we preserve the aspect ratio.
        transform.preConcatenate(AffineTransform.getScaleInstance(scale, scale));

        // This has the effect of negating the y-coordinate:
        transform.preConcatenate(AffineTransform.getScaleInstance(1, -1));

        // Now translate the origin until it is centered in the component.
        transform.preConcatenate(
                AffineTransform.getTranslateInstance(c.getWidth() / 2, c.getHeight() / 2));

        current_transform = transform;

        if (listener != null) {
            listener.transformChanged();
        }
    }

    /**
     * Convert the point from math coordinates into screen coordinates.
     */
    public Point2D toScreenCoordinates(Point2D p) {
        // Convert z to a Point2D, apply the affine transform and return the result.
        return current_transform.transform(p, null);
    }

    /**
     * Convert the point from screen coordinates into math coordinates.
     */
    public Point2D toMathCoordinates(Point2D p) {
        try {
            return current_transform.inverseTransform(p, null);
        } catch (NoninvertibleTransformException e) {
            // We would enter here if the current_transform represents a matrix
            // which is not invertible. This should never happen, because
            // we constructed our transformation as a composition of
            // invertible transformations.
            System.err.println("TransformManager failed to invert the "
                    + "current_transform in the toMathCoordinates() method.");
            return new Point2D.Double(0, 0);
        }
    }

    /**
     * Return the current affine transformation which converts from math
     * coordinates to screen coordinates.
     */
    public AffineTransform getTransform() {
        return current_transform;
    }

    /**
     * Rescale the box we are guaranteed to display by a multiplicative constant
     * via a dilation fixing the provided complex number.
     *
     * All calculations are done in math coordinates.
     *
     * @param scaling_constant A positive constant.
     * @param fixed_point Point fixed by the dilation.
     */
    public void scale(double scaling_constant, Point2D fixed_point) {
        if (scaling_constant > 0) {
            setDisplayBox(new Rectangle2D.Double(
                    scaling_constant * (display_box.getMinX() - fixed_point.getX()) + fixed_point.getX(),
                    scaling_constant * (display_box.getMinY() - fixed_point.getY()) + fixed_point.getY(),
                    scaling_constant * display_box.getWidth(),
                    scaling_constant * display_box.getHeight()));
        }
    }

    /**
     * Rescale the box we are guaranteed to display by a multiplicative constant
     * via a dilation fixing the provided complex number. This dilation will fix
     * the center of the display box.
     *
     * @param scaling_constant A positive constant.
     */
    public void scale(double scaling_constant) {
        Point2D fixed_point = new Point2D.Double(display_box.getCenterX(), display_box.getCenterY());
        scale(scaling_constant, fixed_point);
    }
}
HOOPER >>>>> JAVA TUTORIAL
Last modified on August 25, 2021.
[check html] [check css]