/*
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());
}
}
|