-
Notifications
You must be signed in to change notification settings - Fork 79
Expand file tree
/
Copy pathShapeTest.java
More file actions
356 lines (319 loc) · 9.01 KB
/
Copy pathShapeTest.java
File metadata and controls
356 lines (319 loc) · 9.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
package shape;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
/**
* 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.setTitle("ShapeTest");
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()
{
final ShapeComponent comp = new ShapeComponent();
add(comp, BorderLayout.CENTER);
final JComboBox<ShapeMaker> 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 = comboBox.getItemAt(comboBox.getSelectedIndex());
comp.setShapeMaker(shapeMaker);
}
});
add(comboBox, BorderLayout.NORTH);
comp.setShapeMaker((ShapeMaker) comboBox.getItemAt(0));
pack();
}
}
/**
* This component draws a shape and allows the user to move the points that define it.
*/
class ShapeComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private Point2D[] points;
private static Random generator = new Random();
private static int SIZE = 10;
private int current;
private ShapeMaker shapeMaker;
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));
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
}
/**
* A shape maker can make a shape from a point set. Concrete subclasses must return a shape in the
* makeShape method.
*/
abstract class ShapeMaker
{
public abstract Shape makeShape(Point2D[] p);
private int pointCount;
/**
* 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 String toString()
{
return getClass().getName();
}
}
/**
* 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());
}
}