contains(double,double)   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
c 0
b 0
f 0
cc 1
rs 10
1
package org.gannacademy.cdf.graphics;
2
3
import org.gannacademy.cdf.graphics.ui.DrawingPanel;
4
5
import java.awt.*;
6
import java.awt.geom.AffineTransform;
7
import java.awt.geom.PathIterator;
8
import java.awt.geom.Point2D;
9
import java.awt.geom.Rectangle2D;
10
11
/**
12
 * <p>The superclass of all drawable components</p>
13
 *
14
 * <p>This class defines most of the core operations that define what it means to be drawable (that the component is
15
 * associated with a drawing panel, has a stroke and/or fill color, perhaps a particular stroke style).</p>
16
 *
17
 * @author <a href="https://github.com/gann-cdf/graphics/issues" target="_blank">Seth Battis</a>
18
 */
19
public abstract class Drawable implements AutoCloseable {
20
  /**
21
   * A transparent color constant, to hide either stroke or fill
22
   */
23
  public static final Color TRANSPARENT = new Color(0, true);
24
25
  /**
26
   * A zero-width stroke constant, to hide the stroke (no matter the stroke color)
27
   */
28
  public static final Stroke NO_STROKE = new BasicStroke(0);
29
30
  private DrawingPanel drawingPanel;
31
  private Shape shape;
32
  private Stroke stroke = new BasicStroke();
33
  private boolean filled = false;
0 ignored issues
show
Unused Code introduced by
Consider removing the unused private field filled.
Loading history...
34
  private Color strokeColor = Color.BLACK, fillColor = TRANSPARENT;
35
36
  /**
37
   * Drawing panel on which component is drawn
38
   *
39
   * @return Drawing panel on which this component is drawn (may be {@code null})
40
   */
41
  public DrawingPanel getDrawingPanel() {
42
    return drawingPanel;
43
  }
44
45
  /**
46
   * <p>Change the drawing panel on which this component is drawn</p>
47
   *
48
   * <p>This method may also be used as a "hack" adjust the order in which drawing components are stacked. Drawable
49
   * components are drawn on the screen in the order in which they are declared, oldest to newest, with the newest in front
50
   * of the older components. (Re)setting the drawing panel of a component will pull it forward, in front of newer
51
   * components, as though it had just been declared.</p>
52
   *
53
   * @param drawingPanel on which to draw
54
   */
55
  public void setDrawingPanel(DrawingPanel drawingPanel) {
56
    if (this.drawingPanel != null) {
57
      this.drawingPanel.remove(this);
58
    }
59
    this.drawingPanel = drawingPanel;
60
    this.drawingPanel.add(this);
61
  }
62
63
  /**
64
   * <p>Remove this component from its associated drawing panel</p>
65
   *
66
   * <p>The component will no longer be drawn, but other references to the component will still be valid, and the
67
   * component may be added to a drawing panel later to be redrawn</p>
68
   */
69
  public void removeFromDrawingPanel() {
70
    getDrawingPanel().remove(this);
71
    this.drawingPanel = null;
72
  }
73
74
  /**
75
   * Underlying {@link Shape} geometry
76
   *
77
   * @return Underlying {@link Shape} geometry
78
   */
79
  public Shape getShape() {
80
    return shape;
81
  }
82
83
  /**
84
   * Replace the underlying {@link Shape} geometry of the drawable component
85
   *
86
   * @param shape of geometry
87
   * @throws DrawableException will be thrown if {@code shape} is not compatible with the component (e.g trying to
88
   *                           redefine an {@link org.gannacademy.cdf.graphics.geom.Arc} as a
89
   *                           {@link org.gannacademy.cdf.graphics.geom.Line})
90
   */
91
  public void setShape(Shape shape) throws DrawableException {
92
    this.shape = shape;
93
  }
94
95
  /**
96
   * Origin of bounding box
97
   *
98
   * @return Coordinates of bounding box origin
99
   */
100
  public Point2D getLocation() {
101
    return new Point2D.Double(getShape().getBounds2D().getX(), getShape().getBounds2D().getY());
102
  }
103
104
  /**
105
   * X-coordinate of bounding box origin
106
   *
107
   * @return X-coordinate of the origin of the enclosing bounding box of the underlying {@link Shape}
108
   * @see Shape#getBounds2D()
109
   * @see Rectangle#getX()
110
   */
111
  public double getX() {
112
    return getShape().getBounds2D().getX();
113
  }
114
115
  /**
116
   * Adjust the current X-coordinate of bounding box origin
117
   *
118
   * @param x coordinate to use
119
   */
120
  public void setX(double x) {
121
    setLocation(x, getY());
122
  }
123
124
  /**
125
   * Y-coordinate of bounding box origin
126
   *
127
   * @return Y-coordinate of the origin of the enclosing bounding box of the underlying {@link Shape}
128
   * @see Shape#getBounds2D()
129
   * @see Rectangle#getY()
130
   */
131
  public double getY() {
132
    return getShape().getBounds2D().getY();
133
  }
134
135
  /**
136
   * Adjust Y-coordinate of origin of bounding box
137
   *
138
   * @param y coordinate
139
   */
140
  public void setY(double y) {
141
    setLocation(getX(), y);
142
  }
143
144
  /**
145
   * Width of bounding box
146
   *
147
   * @return Width of the enclosing bounding box of the underlying {@link Shape}
148
   * @see Shape#getBounds2D()
149
   * @see Rectangle#getWidth()
150
   */
151
  public double getWidth() {
152
    return getShape().getBounds2D().getWidth();
153
  }
154
155
  /**
156
   * <p>Adjust width of bounding box</p>
157
   *
158
   * @param width to use
159
   */
160
  public abstract void setWidth(double width);
161
162
  /**
163
   * Height of bounding box
164
   *
165
   * @return Height of the enclosing bounding box of the underlying {@link Shape}
166
   * @see Shape#getBounds2D()
167
   * @see Rectangle#getHeight()
168
   */
169
  public double getHeight() {
170
    return getShape().getBounds2D().getHeight();
171
  }
172
173
  /**
174
   * Adjust height of bounding box
175
   *
176
   * @param height to use
177
   */
178
  public abstract void setHeight(double height);
179
180
  /**
181
   * <p>Translate the shape location</p>
182
   *
183
   * @param dx Change in X-coordinates
184
   * @param dy Change in Y-coordinates
185
   */
186
  public abstract void translate(double dx, double dy);
187
188
  /**
189
   * <p>Translate the shape to a location</p>
190
   *
191
   * @param x coordinate of shape origin at new location
192
   * @param y coordinate of shape origin at new location
193
   */
194
  public abstract void setLocation(double x, double y);
195
196
  /**
197
   * Current stroke style
198
   *
199
   * @return Current {@link Stroke}
200
   */
201
  public Stroke getStroke() {
202
    return stroke;
203
  }
204
205
  /**
206
   * <p>Change the {@link Stroke}</p>
207
   *
208
   * <p>Refer to {@link BasicStroke} documentation for information on how to define a new stroke/</p>
209
   *
210
   * @param stroke description
211
   */
212
  public void setStroke(Stroke stroke) {
213
    this.stroke = stroke;
214
  }
215
216
  /**
217
   * Current stroke color
218
   *
219
   * @return Current stroke color
220
   */
221
  public Color getStrokeColor() {
222
    return strokeColor;
223
  }
224
225
  /**
226
   * <p>Adjust stroke color</p>
227
   *
228
   * <p><img src="doc-files/stroke-fill.png" alt="Stroke and fill diagram"></p>
229
   *
230
   * @param color of stroke
231
   */
232
  public void setStrokeColor(Color color) {
233
    this.strokeColor = color;
234
  }
235
236
  /**
237
   * Current fill color
238
   *
239
   * @return Current fill color
240
   */
241
  public Color getFillColor() {
242
    return fillColor;
243
  }
244
245
  /**
246
   * <p>Adjust fill color</p>
247
   *
248
   * <p><img src="doc-files/stroke-fill.png" alt="Stroke and fill diagram"></p>
249
   *
250
   * @param color of fill
251
   */
252
  public void setFillColor(Color color) {
253
    this.fillColor = color;
254
  }
255
256
  /**
257
   * <p>Drawing instructions for this component</p>
258
   *
259
   * <p>Required by {@link DrawingPanel#draw(Graphics2D)} to render the drawable component.</p>
260
   *
261
   * @param graphics context for drawing instructions
262
   */
263
  public void draw(Graphics2D graphics) {
264
    if (getFillColor() != TRANSPARENT) {
265
      graphics.setPaint(getFillColor());
266
      graphics.fill(getShape());
267
    }
268
    if (getStroke() != NO_STROKE && getStrokeColor() != TRANSPARENT) {
269
      graphics.setPaint(getStrokeColor());
270
      graphics.setStroke(getStroke());
271
      graphics.draw(getShape());
272
    }
273
  }
274
275
  /**
276
   * Enclosing bounding box of the underlying {@link Shape}
277
   *
278
   * @return Enclosing bounding box of the underlying {@link Shape}
279
   * @see Shape#getBounds2D()
280
   */
281
  public Rectangle2D getBounds() {
282
    return getShape().getBounds2D();
283
  }
284
285
  /**
286
   * Tests if the specified coordinates are inside the boundary of the underlying {@link Shape}
287
   *
288
   * @param x coordinate of point
289
   * @param y coordinate of point
290
   * @return {@code true} if the coordinates are inside the boundary of the shape, {@code false} otherwise
291
   * @see Shape#contains(double, double)
292
   */
293
  public boolean contains(double x, double y) {
294
    return getShape().contains(x, y);
295
  }
296
297
  /**
298
   * Tests if the specified point is inside the boundary of the underlying {@link Shape}
299
   *
300
   * @param point to test
301
   * @return {@code true} if the point is inside the boundary of the shape, {@code false} otherwise
302
   * @see Shape#contains(Point2D)
303
   */
304
  public boolean contains(Point2D point) {
305
    return getShape().contains(point);
306
  }
307
308
  /**
309
   * Tests if the interior of the specified rectangle intersects the interior of the underlying {@link Shape}
310
   *
311
   * @param x      coordinate of top, left corner of the rectangle
312
   * @param y      coordinate of top, left corner of the rectangle
313
   * @param width  of the rectangle
314
   * @param height of the rectangle
315
   * @return {@code true} if the interior of the rectangle and the shape intersect, {@code false} otherwise
316
   * @see Shape#intersects(double, double, double, double)
317
   */
318
  public boolean intersects(double x, double y, double width, double height) {
319
    return getShape().intersects(x, y, width, height);
320
  }
321
322
  /**
323
   * Tests if the interior of the specified rectangle intersects the interior of the underlying {@link Shape}
324
   *
325
   * @param rectangle to test
326
   * @return {@code true} if the interior of the rectangle and the shape intersect, {@code false} otherwise
327
   * @see Shape#intersects(Rectangle2D)
328
   */
329
  public boolean intersects(Rectangle2D rectangle) {
330
    return getShape().intersects(rectangle);
331
  }
332
333
334
  /**
335
   * Tests if the interior of the underlying {@link Shape} entirely contains the interior of the specified rectangle
336
   *
337
   * @param x      coordinate of top, left corner of the rectangle
338
   * @param y      coordinate of the top, left corner of the rectangle
339
   * @param width  of the rectangle
340
   * @param height of the rectangle
341
   * @return {@code true} if the shape contains the rectangle, {@code false} otherwise
342
   * @see Shape#contains(double, double, double, double)
343
   */
344
  public boolean contains(double x, double y, double width, double height) {
345
    return getShape().contains(x, y, width, height);
346
  }
347
348
  /**
349
   * Test if the interior of the underlying {@link Shape} entirely contains the interior of the specified rectangle
350
   *
351
   * @param rectangle to test
352
   * @return {@code true} if the shape contains the rectangle, {@code false} otherwise
353
   * @see Shape#contains(Rectangle2D)
354
   */
355
  public boolean contains(Rectangle2D rectangle) {
356
    return getShape().contains(rectangle);
357
  }
358
359
  /**
360
   * <p>Provides access to the underlying geometry of the {@link Shape} outline</p>
361
   *
362
   * <p>If a {@code transformation} is provided, point coordinates are suitable transformed before being returned.</p>
363
   *
364
   * @param transformation to apply to shape before iterating
365
   * @return iterator over shape outline coordinates
366
   * @see Shape#getPathIterator(AffineTransform)
367
   */
368
  public PathIterator getPathIterator(AffineTransform transformation) {
369
    return getShape().getPathIterator(transformation);
370
  }
371
372
  /**
373
   * Perform necessary cleanup before garbage collection
374
   */
375
  @Override
376
  public void close() {
377
    if (drawingPanel != null) {
378
      drawingPanel.remove(this);
379
      drawingPanel = null;
380
    }
381
  }
382
}
383