Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Graphics2D wrapper for 2d game engine

I'm trying to write a 2d game engine and I'm trying to implement a viewport system so that when I'm drawing in a certain viewport, the game coordinates will be transformed to the screen coordinates without having to manually do the transformation.

What I want to do is create a Graphics2D wrapper that adds a setViewport method.

The way I see it there are 2 options:

  1. Create a class that has an instance of a Graphics2D and has all the same methods as Graphics2D plus setViewport and just calls the respective method on the Graphics2D instance.

  2. Subclass Graphics2D and just add a setViewport method and then just cast from Graphics2D to this new class

I tried #2 because #1 seemed very impractical but ran into a ClassCastException. I'm unable to cast Graphics or Graphics2D to this new class. When I print the graphics object before the cast (either Graphics or Graphics2D), both come out as sun.java2d.SunGraphics2D.

Am I doing something fundamentally wrong trying to subclass and cast? If not, how can I fix this problem?

like image 468
elaid Avatar asked Aug 17 '16 20:08

elaid


3 Answers

One OO design priciple is "favor composition over inheritance", this is acheived using the wrapper class or Decorator design pattern (which is in my opinion what is meant by wrapper in the course slide). So what you have done is in fact the elegant solution for a number of reasons:

  • It protects against future implementation changes in Graphics2D, if you use inheritance and by misfortune a new method is added to Graphics2D with the same signature as your new method and a different return type, your class will no longer compile. If you use the same signature and return type you would override the new method in Graphics2D which could (and has) result in several days of frustrating debugging.

  • Inheritance in this way violates encapsulation making software fragile and error-prone in the long run.

  • Using composition you are protecting your class from future changes in the class your composing with, your class would forward all method calls to it's private Graphics2D instance and handle transforming coordinates separately.

  • It also allows for easy future extension, using inheritance will tie you to the current implementation of Graphics2D potentially limiting the performance of your class.

An example of this exists in the Java API: The Properties class extends HashTable, this illustrates improper use of inheritance because a Properties is not a HashTable, it's not meant to be used in the same way. In this case a call to Properties p.getProperty(key) might give different results than p.get(key), because the latter-case does not take defaults into account.

The Decorator design pattern:

public class Wrapper {
    private WrappedClass w;

    public Wrapper(WrappedClass w) {
       this.w = w;  
    }

    // Forward calls to WrappedClass methods to the private instance.
    public ReturnType example(Argument a) { return w.example(a); }

    // Add your methods here:
}

Although this might seem like a tedious approach it is worth it in the long run for the reasons above. Also the existance of interfaces in the Java API such as the Set interface for the HashSet above makes writing such classes easier although I don't know if such an interface exists for Graphics2D.

sources: Effective Java 2nd Edition - Joshua Bloch

like image 117
Adel Khial Avatar answered Sep 25 '22 21:09

Adel Khial


I created a coordinate system for a game that I wrote. Feel free to use these classes as examples.

Here's the CoordinateSystem class.

package com.ggl.game.utilities;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.geom.Point2D;

/**
 * <p>
 * This class creates a Cartesian and Polar coordinate system to overlay a Swing
 * drawing area (usually a <code>JPanel</code>). The user of the class sets a
 * limit as to how far the X axis or the Y axis extends in the negative and
 * positive direction from (0, 0). Point (0, 0) will be placed in the center of
 * the drawing area.
 * </p>
 * <p>
 * Since the drawing area is usually not square, the limit applies to the
 * shorter dimension. The actual limits can be retrieved from this class, and
 * will change if the user of the application changes the drawing area size by
 * maximizing or normalizing the application <code>JFrame</code> window.
 * </p>
 * <p>
 * Using a Cartesian or Polar coordinate system frees the user of this class
 * from having to worry about Swing pixel coordinates.
 * </p>
 * 
 * @author Gilbert G. Le Blanc
 * @version 1.0 - 23 February 2015
 * 
 * @see com.ggl.game.utilities.Polar2D
 * @see java.awt.geom.Point2D
 * @see javax.swing.JFrame
 * @see javax.swing.JPanel
 */
public class CoordinateSystem {

    private double conversionFactor;
    private double xLimit;
    private double yLimit;

    private int height;
    private int width;

    /**
     * <p>
     * This creates a Cartesian and Polar coordinate system over a Swing drawing
     * area (usually a <code>JPanel</code>). If the drawing area is square, the
     * X axis extends from -limit to limit and the Y axis extends from -limit to
     * limit.
     * </p>
     * <p>
     * If the drawing area is not square, then the smaller dimension, either X
     * or Y, extends from -limit to limit. The larger dimension extends beyond
     * the limit in both directions.
     * </p>
     * <p>
     * Since most displays are not square, the X and Y axis will usually have
     * different limits.
     * </p>
     * 
     * @param limit
     *            - The limit of the X and Y axis in a Cartesian coordinate
     *            system.
     * @param width
     *            - The width of the drawing area in pixels.
     * @param height
     *            - The height of the drawing area in pixels.
     */
    public CoordinateSystem(double limit, int width, int height) {
        this.width = width;
        this.height = height;

        if (width > height) {
            this.xLimit = limit * width / height;
            this.yLimit = limit;
            this.conversionFactor = (limit + limit) / (double) height;
        } else if (width < height) {
            this.xLimit = limit;
            this.yLimit = limit * height / width;
            this.conversionFactor = (limit + limit) / (double) width;
        } else {
            this.xLimit = limit;
            this.yLimit = limit;
            this.conversionFactor = (limit + limit) / (double) width;
        }
    }

    /**
     * This method changes the drawing area dimension, along with the X and Y
     * axis limits.
     * 
     * @param dimension
     *            - A <code>Dimension</code> with the new drawing area
     *            dimension.
     */
    public void setDrawingSize(Dimension dimension) {
        setDrawingSize(dimension.width, dimension.height);
    }

    /**
     * This method changes the drawing area width and height, along with the X
     * and Y axis limits.
     * 
     * @param width
     *            - The width of the drawing area in pixels.
     * @param height
     *            - The height of the drawing area in pixels.
     */
    public void setDrawingSize(int width, int height) {
        xLimit = xLimit / this.width * width;
        yLimit = yLimit / this.height * height;

        this.width = width;
        this.height = height;
    }

    /**
     * This method returns the Cartesian coordinate limit for the X axis.
     * 
     * @return The Cartesian coordinate limit for the X axis.
     */
    public double getxLimit() {
        return xLimit;
    }

    /**
     * This method returns the Cartesian coordinate limit for the Y axis.
     * 
     * @return The Cartesian coordinate limit for the Y axis.
     */
    public double getyLimit() {
        return yLimit;
    }

    /**
     * This method converts a Polar coordinate distance and theta angle in
     * radians to a pixel location on a drawing area.
     * 
     * @param distance
     *            - A Polar coordinate distance
     * @param theta
     *            - A Polar coordinate theta angle in radians
     * @return A pixel location on a drawing area.
     */
    public Point convertPolarToPixels(double distance, double theta) {
        return convertToPixels(new Polar2D.Double(distance, theta));
    }

    /**
     * This method converts a Cartesian coordinate x and y to a pixel location
     * on a drawing area.
     * 
     * @param x
     *            - A Cartesian coordinate x.
     * @param y
     *            - A Cartesian coordinate y.
     * @return A pixel location on a drawing area.
     */
    public Point convertPointToPixels(double x, double y) {
        return convertToPixels(new Point2D.Double(x, y));
    }

    /**
     * This method converts a Polar coordinate to a pixel location on a drawing
     * area.
     * 
     * @param polar
     *            - A Polar coordinate.
     * @return A pixel location on a drawing area.
     */
    public Point convertToPixels(Polar2D polar) {
        double x = polar.getDistance() * Math.cos(polar.getTheta());
        double y = polar.getDistance() * Math.sin(polar.getTheta());
        return convertToPixels(new Point2D.Double(x, y));
    }

    /**
     * This method converts a Cartesian coordinate to a pixel location on a
     * drawing area.
     * 
     * @param cartesian
     *            - A Cartesian coordinate.
     * @return A pixel location on a drawing area.
     */
    public Point convertToPixels(Point2D cartesian) {
        int x = (int) Math
                .round((cartesian.getX() + xLimit) / conversionFactor);
        int y = (int) Math.round((-cartesian.getY() + yLimit)
                / conversionFactor);
        return new Point(x, y);
    }

    /**
     * This method converts a pixel location on a drawing area to a Cartesian
     * coordinate.
     * 
     * @param x
     *            - The x pixel location.
     * @param y
     *            - The y pixel location.
     * @return A Cartesian coordinate.
     */
    public Point2D convertToCartesian(int x, int y) {
        return convertToCartesian(new Point(x, y));
    }

    /**
     * This method converts a pixel location on a drawing area to a Cartesian
     * coordinate.
     * 
     * @param point
     *            - The pixel location.
     * @return A Cartesian coordinate.
     */
    public Point2D convertToCartesian(Point point) {
        double x = (double) point.x * conversionFactor - xLimit;
        double y = (double) -point.y * conversionFactor + yLimit;
        return new Point2D.Double(x, y);
    }

    /**
     * This method converts a pixel location on a drawing area to a Polar
     * coordinate.
     * 
     * @param x
     *            - The x pixel location.
     * @param y
     *            - The y pixel location.
     * @return A Polar coordinate.
     */
    public Polar2D convertToPolar(int x, int y) {
        return convertToPolar(new Point(x, y));
    }

    /**
     * This method converts a pixel location on a drawing area to a Polar
     * coordinate.
     * 
     * @param point
     *            - The pixel location.
     * @return A Polar coordinate.
     */
    public Polar2D convertToPolar(Point point) {
        double x = (double) point.x * conversionFactor - xLimit;
        double y = (double) -point.y * conversionFactor + yLimit;
        double distance = Math.sqrt(x * x + y * y);
        double theta = Math.atan2(y, x);
        return new Polar2D.Double(distance, theta);
    }

}

And here's the Polar2D class. It's a clone of the Point2D class.

package com.ggl.game.utilities;

/**
 * The <code>Polar2D</code> class defines a point representing a location in
 * distance, theta angle coordinate space.
 * <p>
 * This class is only the abstract superclass for all objects that store a 2D
 * coordinate. The actual storage representation of the coordinates is left to
 * the subclass.
 * 
 * @version 1.0 - 23 February 2015
 * @author Jim Graham (author of Point2D), Gilbert Le Blanc
 */
public abstract class Polar2D implements Cloneable {
    /**
     * The <code>Float</code> class defines a point specified in float
     * precision.
     */
    public static class Float extends Polar2D {
        /**
         * The distance of this <code>Polar2D</code>.
         * 
         * @since 1.7
         */
        public float distance;
        /**
         * The theta angle of this <code>Polar2D</code>.
         * 
         * @since 1.7
         */
        public float theta;

        /**
         * Constructs and initializes a <code>Polar2D</code> with coordinates
         * (0,&nbsp;0).
         * 
         * @since 1.7
         */
        public Float() {
        }

        /**
         * Constructs and initializes a <code>Polar2D</code> with the specified
         * coordinates.
         * 
         * @param distance
         *            The distance to which to set the newly constructed
         *            <code>Polar2D</code>
         * @param theta
         *            The theta angle in radians to which to set the newly
         *            constructed <code>Polar2D</code>
         * @since 1.7
         */
        public Float(float distance, float theta) {
            this.distance = distance;
            this.theta = theta;
        }

        /**
         * Returns the distance of this <code>Polar2D</code> in
         * <code>double</code> precision.
         * 
         * @return the distance of this <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public double getDistance() {
            return (double) distance;
        }

        /**
         * Returns the theta angle in radians of this <code>Polar2D</code> in
         * <code>double</code> precision.
         * 
         * @return the theta angle in radians of this <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public double getTheta() {
            return (double) theta;
        }

        /**
         * Returns the theta angle in degrees of this <code>Polar2D</code> in
         * <code>double</code> precision.
         * 
         * @return the theta angle in degrees of this <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public double getThetaInDegrees() {
            double degrees = 180D / Math.PI * theta;
            return (degrees < 0D) ? degrees + 360D : degrees;
        }

        /**
         * Sets the location of this <code>Polar2D</code> to the specified
         * <code>double</code> distance and theta angle in radians.
         * 
         * @param distance
         *            The distance to which to set this <code>Polar2D</code>
         * @param theta
         *            The theta angle in radians to which to set this
         *            <code>Polar2D</code>
         * @since 1.7
         */
        @Override
        public void setLocation(double distance, double theta) {
            this.distance = (float) distance;
            this.theta = (float) theta;
        }

        /**
         * Sets the location of this <code>Polar2D</code> to the specified
         * <code>float</code> coordinates.
         * 
         * @param distance
         *            The distance to which to set this <code>Polar2D</code>
         * @param theta
         *            The theta angle in radians to which to set this
         *            <code>Polar2D</code>
         * @since 1.7
         */
        public void setLocation(float distance, float theta) {
            this.distance = distance;
            this.theta = theta;
        }

        /**
         * Returns a <code>String</code> that represents the value of this
         * <code>Polar2D</code>.
         * 
         * @return A <code>String</code> representation of this
         *         <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public String toString() {
            return "Polar2D.Float[" + distance + ", " + theta + "]";
        }
    }

    /**
     * The <code>Double</code> class defines a point specified in
     * <code>double</code> precision.
     */
    public static class Double extends Polar2D {
        /**
         * The distance of this <code>Polar2D</code>.
         * 
         * @since 1.7
         */
        public double distance;
        /**
         * The theta angle in radians of this <code>Polar2D</code>.
         * 
         * @since 1.7
         */
        public double theta;

        /**
         * Constructs and initializes a <code>Polar2D</code> with (0,&nbsp;0)
         * distance and theta angle in radians.
         * 
         * @since 1.7
         */
        public Double() {
        }

        /**
         * Constructs and initializes a <code>Polar2D</code> with the specified
         * coordinates.
         * 
         * @param distance
         *            The distance to which to set the newly constructed
         *            <code>Polar2D</code>
         * @param theta
         *            The theta angle in radians to which to set the newly
         *            constructed <code>Polar2D</code>
         * @since 1.7
         */
        public Double(double distance, double theta) {
            this.distance = distance;
            this.theta = theta;
        }

        /**
         * Returns the distance of this <code>Polar2D</code> in
         * <code>double</code> precision.
         * 
         * @return The distance of this <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public double getDistance() {
            return distance;
        }

        /**
         * Returns the theta angle in radians of this <code>Polar2D</code> in
         * <code>double</code> precision.
         * 
         * @return The theta angle in radians of this <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public double getTheta() {
            return theta;
        }

        /**
         * Returns the theta angle in degrees of this <code>Polar2D</code> in
         * <code>double</code> precision.
         * 
         * @return The theta angle in degrees of this <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public double getThetaInDegrees() {
            double degrees = 180D / Math.PI * theta;
            return (degrees < 0D) ? degrees + 360D : degrees;
        }

        /**
         * Sets the location of this <code>Polar2D</code> to the specified
         * <code>double</code> coordinates.
         * 
         * @param distance
         *            The distance to which to set this <code>Polar2D</code>
         * @param theta
         *            The theta angle in radians to which to set this
         *            <code>Polar2D</code>
         * @since 1.7
         */
        @Override
        public void setLocation(double distance, double theta) {
            this.distance = distance;
            this.theta = theta;
        }

        /**
         * Returns a <code>String</code> that represents the value of this
         * <code>Polar2D</code>.
         * 
         * @return A <code>String</code> representation of this
         *         <code>Polar2D</code>.
         * @since 1.7
         */
        @Override
        public String toString() {
            return "Polar2D.Double[" + distance + ", " + theta + "]";
        }
    }

    /**
     * This is an abstract class that cannot be instantiated directly.
     * Type-specific implementation subclasses are available for instantiation
     * and provide a number of formats for storing the information necessary to
     * satisfy the various accessor methods below.
     * 
     * @see java.awt.geom.Polar2D.Float
     * @see java.awt.geom.Polar2D.Double
     * @see java.awt.Point
     */
    protected Polar2D() {
    }

    /**
     * Returns the distance of this <code>Polar2D</code> in <code>double</code>
     * precision.
     * 
     * @return The distance of this <code>Polar2D</code>.
     * @since 1.7
     */
    public abstract double getDistance();

    /**
     * Returns the theta angle in radians of this <code>Polar2D</code> in
     * <code>double</code> precision.
     * 
     * @return The theta angle in radians of this <code>Polar2D</code>.
     * @since 1.7
     */
    public abstract double getTheta();

    /**
     * Returns the theta angle in degrees of this <code>Polar2D</code> in
     * <code>double</code> precision.
     * 
     * @return The theta angle in degrees of this <code>Polar2D</code>.
     * @since 1.7
     */
    public abstract double getThetaInDegrees();

    /**
     * Sets the location of this <code>Polar2D</code> to the specified
     * <code>double</code> coordinates.
     * 
     * @param distance
     *            The distance of this <code>Polar2D</code>
     * @param theta
     *            The theta angle in radians of this <code>Polar2D</code>
     * @since 1.7
     */
    public abstract void setLocation(double distance, double theta);

    /**
     * Sets the location of this <code>Polar2D</code> to the same coordinates as
     * the specified <code>Polar2D</code> object.
     * 
     * @param p
     *            the specified <code>Polar2D</code> the which to set this
     *            <code>Polar2D</code>
     * @since 1.7
     */
    public void setLocation(Polar2D p) {
        setLocation(p.getDistance(), p.getTheta());
    }

    /**
     * Returns the square of the distance between two points.
     * 
     * @param distance1
     *            The distance of the first point
     * @Parm theta1 The theta angle in radians of the first point
     * @param distance2
     *            The distance of the second point
     * @param theta2
     *            The theta angle in radians of the second point
     * @return The square of the distance between the two specified points.
     */
    public static double distanceSq(double distance1, double theta1,
            double distance2, double theta2) {
        double x1 = distance1 * Math.cos(theta1);
        double y1 = distance1 * Math.sin(theta1);
        double x2 = distance2 * Math.cos(theta2);
        double y2 = distance2 * Math.sin(theta2);
        return (x1 * x2 + y1 * y2);
    }

    /**
     * Returns the distance between two points.
     * 
     * @param distance1
     *            The distance of the first point
     * @param theta1
     *            The theta angle in radians of the first point
     * @param distance2
     *            The distance of the second point
     * @param theta2
     *            The theta angle in radians of the second point
     * @return The distance between the two specified points.
     */
    public static double distance(double distance1, double theta1,
            double distance2, double theta2) {
        double x1 = distance1 * Math.cos(theta1);
        double y1 = distance1 * Math.sin(theta1);
        double x2 = distance2 * Math.cos(theta2);
        double y2 = distance2 * Math.sin(theta2);
        return Math.sqrt(x1 * x2 + y1 * y2);
    }

    /**
     * Returns the square of the distance from this <code>Polar2D</code> to a
     * specified point.
     * 
     * @param distance
     *            The distance of the specified point
     * @param theta
     *            The theta angle in radians of the specified point
     * @return The square of the distance between this <code>Polar2D</code> and
     *         the specified point.
     */
    public double distanceSq(double distance, double theta) {
        double x1 = distance * Math.cos(theta);
        double y1 = distance * Math.sin(theta);
        double x2 = getDistance() * Math.cos(getTheta());
        double y2 = getDistance() * Math.sin(getTheta());
        return (x1 * x2 + y1 * y2);
    }

    /**
     * Returns the square of the distance from this <code>Polar2D</code> to a
     * specified <code>Polar2D</code>.
     * 
     * @param pt
     *            The specified <code>Polar2D</code>
     * @return The square of the distance between this <code>Polar2D</code> to a
     *         specified <code>Polar2D</code>.
     */
    public double distanceSq(Polar2D pt) {
        double x1 = pt.getDistance() * Math.cos(pt.getTheta());
        double y1 = pt.getDistance() * Math.sin(pt.getTheta());
        double x2 = getDistance() * Math.cos(getTheta());
        double y2 = getDistance() * Math.sin(getTheta());
        return (x1 * x2 + y1 * y2);
    }

    /**
     * Returns the distance from this <code>Polar2D</code> to a specified point.
     * 
     * @param distance
     *            The distance of the specified point
     * @param theta
     *            The theta angle in radians of the specified point
     * @return The distance between this <code>Polar2D</code> and a specified
     *         point.
     */
    public double distance(double distance, double theta) {
        double x1 = distance * Math.cos(theta);
        double y1 = distance * Math.sin(theta);
        double x2 = getDistance() * Math.cos(getTheta());
        double y2 = getDistance() * Math.sin(getTheta());
        return Math.sqrt(x1 * x2 + y1 * y2);
    }

    /**
     * Returns the distance from this <code>Polar2D</code> to a specified
     * <code>Polar2D</code>.
     * 
     * @param pt
     *            the specified <code>Polar2D</code>
     * @return The distance between this <code>Polar2D</code> and the specified
     *         <code>Polar2D</code>.
     */
    public double distance(Polar2D pt) {
        double x1 = pt.getDistance() * Math.cos(pt.getTheta());
        double y1 = pt.getDistance() * Math.sin(pt.getTheta());
        double x2 = getDistance() * Math.cos(getTheta());
        double y2 = getDistance() * Math.sin(getTheta());
        return Math.sqrt(x1 * x2 + y1 * y2);
    }

    /**
     * Creates a new object of the same class and with the same contents as this
     * object.
     * 
     * @return a clone of this instance.
     * @exception OutOfMemoryError
     *                if there is not enough memory.
     * @see java.lang.Cloneable
     * @since 1.7
     */
    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError();
        }
    }

    /**
     * Returns the hash code for this <code>Polar2D</code>.
     * 
     * @return a hash code for this <code>Polar2D</code>.
     */
    @Override
    public int hashCode() {
        long bits = java.lang.Double.doubleToLongBits(getDistance());
        bits ^= java.lang.Double.doubleToLongBits(getTheta()) * 31;
        return (((int) bits) ^ ((int) (bits >> 32)));
    }

    /**
     * Determines whether or not two points are equal. Two instances of
     * <code>Polar2D</code> are equal if the values of their <code>x</code> and
     * <code>y</code> member fields, representing their position in the
     * coordinate space, are the same.
     * 
     * @param obj
     *            an object to be compared with this <code>Polar2D</code>
     * @return <code>true</code> if the object to be compared is an instance of
     *         <code>Polar2D</code> and has the same values; <code>false</code>
     *         otherwise.
     * @since 1.7
     */
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Polar2D) {
            Polar2D p2d = (Polar2D) obj;
            return (getDistance() == p2d.getDistance())
                    && (getTheta() == p2d.getTheta());
        }
        return super.equals(obj);
    }
}
like image 30
Gilbert Le Blanc Avatar answered Sep 22 '22 21:09

Gilbert Le Blanc


One option is to use a Proxy. It lets you wrap all of the functionality of Graphics2D without needing to code each method.

The GraphicsView interface declares the additional functionality:

import java.awt.Rectangle;

public interface GraphicsView {
    void setViewport(Rectangle r);
    Rectangle getViewport();
    boolean isViewportActive();
    void setViewportActive(boolean active);
}

The Graphics2DWrapperFactory a proxy to Graphics2D + GraphicsView:

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Graphics2DWrapperFactory {
    public static Graphics2D wrapGraphics2D(final Graphics2D g) {
        GraphicsView gv = new GraphicsView() {
            // Implement new functionality here...
            private boolean active;
            Rectangle r;
            @Override
            public Rectangle getViewport() {
                System.err.println("getViewport called");
                return r;
            }
            @Override
            public void setViewport(Rectangle r) {
                this.r = r;
                System.err.println("setViewport called");
            }
            @Override
            public boolean isViewportActive() {
                System.err.println("isViewportActive called");
                return active;
            }
            @Override
            public void setViewportActive(boolean active) {
                this.active = active;
                System.err.println("setViewportActive called");
            }
        };
        InvocationHandler invocationHandler = new GraphicsWrapperInvocationHandler(g, gv);
        Class<?> interfaces[] = { Graphics2D.class, GraphicsView.class};
        return (Graphics2D) Proxy.newProxyInstance(g.getClass().getClassLoader(), interfaces, invocationHandler);
    }
    private static class GraphicsWrapperInvocationHandler implements InvocationHandler {
        private final GraphicsView gv;
        private final Graphics2D g2d;
        public GraphicsWrapperInvocationHandler(Graphics2D g, GraphicsView gv) {
            this.g2d = g;
            this.gv = gv;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            switch(methodName) {
                // If the is calling one of the GraphicsView methods, delegate to gv
                case "setViewport":
                case "getViewport":
                case "isViewportActive":
                case "setViewportActive":
                    return method.invoke(gv, args);
                // Otherwise, it's a Graphics2D methods. Delegate to g2d
                default:
                    return method.invoke(g2d, args);
            }
        }
    }
}
like image 24
Devon_C_Miller Avatar answered Sep 23 '22 21:09

Devon_C_Miller