Java tutorial
/* This program is a part of the companion code for Core Java 8th ed. (http://horstmann.com/corejava) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.geom.Arc2D; import java.awt.geom.CubicCurve2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.QuadCurve2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.util.Random; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFrame; /** * This program demonstrates the various 2D shapes. * @version 1.02 2007-08-16 * @author Cay Horstmann */ public class ShapeTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { JFrame frame = new ShapeTestFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * This frame contains a combo box to select a shape and a component to draw it. */ class ShapeTestFrame extends JFrame { public ShapeTestFrame() { setTitle("ShapeTest"); setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); final ShapeComponent comp = new ShapeComponent(); add(comp, BorderLayout.CENTER); final JComboBox comboBox = new JComboBox(); comboBox.addItem(new LineMaker()); comboBox.addItem(new RectangleMaker()); comboBox.addItem(new RoundRectangleMaker()); comboBox.addItem(new EllipseMaker()); comboBox.addItem(new ArcMaker()); comboBox.addItem(new PolygonMaker()); comboBox.addItem(new QuadCurveMaker()); comboBox.addItem(new CubicCurveMaker()); comboBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { ShapeMaker shapeMaker = (ShapeMaker) comboBox.getSelectedItem(); comp.setShapeMaker(shapeMaker); } }); add(comboBox, BorderLayout.NORTH); comp.setShapeMaker((ShapeMaker) comboBox.getItemAt(0)); } private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEIGHT = 300; } /** * This component draws a shape and allows the user to move the points that define it. */ class ShapeComponent extends JComponent { public ShapeComponent() { addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent event) { Point p = event.getPoint(); for (int i = 0; i < points.length; i++) { double x = points[i].getX() - SIZE / 2; double y = points[i].getY() - SIZE / 2; Rectangle2D r = new Rectangle2D.Double(x, y, SIZE, SIZE); if (r.contains(p)) { current = i; return; } } } public void mouseReleased(MouseEvent event) { current = -1; } }); addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent event) { if (current == -1) return; points[current] = event.getPoint(); repaint(); } }); current = -1; } /** * Set a shape maker and initialize it with a random point set. * @param aShapeMaker a shape maker that defines a shape from a point set */ public void setShapeMaker(ShapeMaker aShapeMaker) { shapeMaker = aShapeMaker; int n = shapeMaker.getPointCount(); points = new Point2D[n]; for (int i = 0; i < n; i++) { double x = generator.nextDouble() * getWidth(); double y = generator.nextDouble() * getHeight(); points[i] = new Point2D.Double(x, y); } repaint(); } public void paintComponent(Graphics g) { if (points == null) return; Graphics2D g2 = (Graphics2D) g; for (int i = 0; i < points.length; i++) { double x = points[i].getX() - SIZE / 2; double y = points[i].getY() - SIZE / 2; g2.fill(new Rectangle2D.Double(x, y, SIZE, SIZE)); } g2.draw(shapeMaker.makeShape(points)); } private Point2D[] points; private static Random generator = new Random(); private static int SIZE = 10; private int current; private ShapeMaker shapeMaker; } /** * A shape maker can make a shape from a point set. Concrete subclasses must return a shape in the * makeShape method. */ abstract class ShapeMaker { /** * Constructs a shape maker. * @param aPointCount the number of points needed to define this shape. */ public ShapeMaker(int aPointCount) { pointCount = aPointCount; } /** * Gets the number of points needed to define this shape. * @return the point count */ public int getPointCount() { return pointCount; } /** * Makes a shape out of the given point set. * @param p the points that define the shape * @return the shape defined by the points */ public abstract Shape makeShape(Point2D[] p); public String toString() { return getClass().getName(); } private int pointCount; } /** * Makes a line that joins two given points. */ class LineMaker extends ShapeMaker { public LineMaker() { super(2); } public Shape makeShape(Point2D[] p) { return new Line2D.Double(p[0], p[1]); } } /** * Makes a rectangle that joins two given corner points. */ class RectangleMaker extends ShapeMaker { public RectangleMaker() { super(2); } public Shape makeShape(Point2D[] p) { Rectangle2D s = new Rectangle2D.Double(); s.setFrameFromDiagonal(p[0], p[1]); return s; } } /** * Makes a round rectangle that joins two given corner points. */ class RoundRectangleMaker extends ShapeMaker { public RoundRectangleMaker() { super(2); } public Shape makeShape(Point2D[] p) { RoundRectangle2D s = new RoundRectangle2D.Double(0, 0, 0, 0, 20, 20); s.setFrameFromDiagonal(p[0], p[1]); return s; } } /** * Makes an ellipse contained in a bounding box with two given corner points. */ class EllipseMaker extends ShapeMaker { public EllipseMaker() { super(2); } public Shape makeShape(Point2D[] p) { Ellipse2D s = new Ellipse2D.Double(); s.setFrameFromDiagonal(p[0], p[1]); return s; } } /** * Makes an arc contained in a bounding box with two given corner points, and with starting and * ending angles given by lines emanating from the center of the bounding box and ending in two * given points. To show the correctness of the angle computation, the returned shape contains the * arc, the bounding box, and the lines. */ class ArcMaker extends ShapeMaker { public ArcMaker() { super(4); } public Shape makeShape(Point2D[] p) { double centerX = (p[0].getX() + p[1].getX()) / 2; double centerY = (p[0].getY() + p[1].getY()) / 2; double width = Math.abs(p[1].getX() - p[0].getX()); double height = Math.abs(p[1].getY() - p[0].getY()); double skewedStartAngle = Math .toDegrees(Math.atan2(-(p[2].getY() - centerY) * width, (p[2].getX() - centerX) * height)); double skewedEndAngle = Math .toDegrees(Math.atan2(-(p[3].getY() - centerY) * width, (p[3].getX() - centerX) * height)); double skewedAngleDifference = skewedEndAngle - skewedStartAngle; if (skewedStartAngle < 0) skewedStartAngle += 360; if (skewedAngleDifference < 0) skewedAngleDifference += 360; Arc2D s = new Arc2D.Double(0, 0, 0, 0, skewedStartAngle, skewedAngleDifference, Arc2D.OPEN); s.setFrameFromDiagonal(p[0], p[1]); GeneralPath g = new GeneralPath(); g.append(s, false); Rectangle2D r = new Rectangle2D.Double(); r.setFrameFromDiagonal(p[0], p[1]); g.append(r, false); Point2D center = new Point2D.Double(centerX, centerY); g.append(new Line2D.Double(center, p[2]), false); g.append(new Line2D.Double(center, p[3]), false); return g; } } /** * Makes a polygon defined by six corner points. */ class PolygonMaker extends ShapeMaker { public PolygonMaker() { super(6); } public Shape makeShape(Point2D[] p) { GeneralPath s = new GeneralPath(); s.moveTo((float) p[0].getX(), (float) p[0].getY()); for (int i = 1; i < p.length; i++) s.lineTo((float) p[i].getX(), (float) p[i].getY()); s.closePath(); return s; } } /** * Makes a quad curve defined by two end points and a control point. */ class QuadCurveMaker extends ShapeMaker { public QuadCurveMaker() { super(3); } public Shape makeShape(Point2D[] p) { return new QuadCurve2D.Double(p[0].getX(), p[0].getY(), p[1].getX(), p[1].getY(), p[2].getX(), p[2].getY()); } } /** * Makes a cubic curve defined by two end points and two control points. */ class CubicCurveMaker extends ShapeMaker { public CubicCurveMaker() { super(4); } public Shape makeShape(Point2D[] p) { return new CubicCurve2D.Double(p[0].getX(), p[0].getY(), p[1].getX(), p[1].getY(), p[2].getX(), p[2].getY(), p[3].getX(), p[3].getY()); } }