translate(double,double)   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 7
c 0
b 0
f 0
cc 1
rs 10
1
package org.gannacademy.cdf.graphics.geom;
2
3
import org.gannacademy.cdf.graphics.Drawable;
4
import org.gannacademy.cdf.graphics.DrawableException;
5
import org.gannacademy.cdf.graphics.ui.DrawingPanel;
6
7
import java.awt.*;
8
import java.awt.geom.CubicCurve2D;
9
import java.awt.geom.Point2D;
10
11
/**
12
 * <p>Draw a cubic Bézier curve</p>
13
 *
14
 * <p>Bezier curves are computed using a deceptively simple interpolation technique. A linear Bézier curve is computed by
15
 * plotting the points between Point 1 and Point 2 &mdash; resulting in a straight line:</p>
16
 *
17
 * <p><img src="doc-files/Line.png" alt="Linear Bézier curve"></p>
18
 *
19
 * <p>A quadratic Bézier curve uses a control point in addition to Points 1 and 2. The points on the curve are plotted
20
 * by drawing a line from a point on the line from Point 1 to the control point and connecting to a point on the line
21
 * connecting the control point to point 2. This is done in a fractional progression: we connect the point, say, 20% of
22
 * the way between Point 1 and the control point to a point 20% of the way from the control point to Point 2, and find
23
 * the point on the curve 20% of the way along our interpolated line.</p>
24
 *
25
 * <table>
26
 * <tr>
27
 * <td><img src="doc-files/Bezier-QuadCurve-fig2.png" alt="Quadratic Bézier curve interpolation"></td>
28
 * <td><img src="doc-files/Bezier-QuadCurve-fig3.png" alt="Quadratic Bézier curve interpolated"></td>
29
 * <td><img src="doc-files/Bezier-QuadCurve-fig4.png" alt="Quadratic Bézier curve"></td>
30
 * </tr>
31
 * <caption>Quadratic Bézier curve</caption>
32
 * </table>
33
 *
34
 * <p>We can extend this process to higher dimensions by using an increasing number of intermediate control points
35
 * between Points 1 and 2, and increasing the levels of interpolation between those points. A cubic Bézier curve has
36
 * two control points.</p>
37
 *
38
 * <p><img src="doc-files/Bezier-CubicCurve-fig1.png" alt="Cubic Bézier curve with control points"></p>
39
 *
40
 * <p>We have three imaginary lines: from Point 1 to the first control point, from the first control point to the
41
 * second control point, and from the second control point to Point 2. We interpolate lines connecting these lines in
42
 * the same manner as a quadratic curve.</p>
43
 *
44
 * <p><img src="doc-files/Bezier-CubicCurve-fig2.png" alt="First order interpolation of cubic Bézier curve"></p>
45
 *
46
 * <p>We now interpolate our interpolations and choose points on these second order interpolations as the points of our
47
 * curve. To make this somewhat clearer, we start by reducing the number of interpolations for somewhat better clarity.</p>
48
 *
49
 * <table>
50
 * <tr>
51
 * <td><img src="doc-files/Bezier-CubicCurve-fig3.png" alt="Second order interpolation of cubic Bézier curve"></td>
52
 * <td><img src="doc-files/Bezier-CubicCurve-fig4.png" alt="Second order interpolation of cubic Bézier curve (increased detail)"></td>
53
 * <td><img src="doc-files/Bezier-CubicCurve-fig5.png" alt="Cubic Bézier curve"></td>
54
 * </tr>
55
 * <caption>Cubic Bézier curve</caption>
56
 * </table>
57
 *
58
 * <p>Refer to the <a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve">Wikipedia Bézier curve article</a> for some
59
 * lovely animated GIFs of this process.</p>
60
 *
61
 * @author <a href="https://github.com/gann-cdf/graphics/issues" target="_blank">Seth Battis</a>
62
 */
63
public class CubicCurve extends Drawable {
64
  /**
65
   * <p>Construct a new cubic curve.</p>
66
   *
67
   * <p>A cubic curve is a curve that is shaped like a cubic function (i.e. <i>f(x)</i>&nbsp;=&nbsp;<i>x</i><sup>3</sup>).
68
   * The curve is defined by two end points and two control points. The curve is drawn by finding the curve between
69
   * the two end points that passes closest to the control points, so adjusting their positions will change the shape of
70
   * the curve.</p>
71
   *
72
   * <p><img src="doc-files/CubicCurve.png" alt="Diagram of CubicCurve parameters"></p>
73
   *
74
   * <p>All window coordinates are measured in pixels, with the X-axis increasing from left to right and the Y-axis
75
   * increasing from top to bottom. All window coordinates exist in the first quadrant.</p>
76
   *
77
   * <p><img src="../doc-files/window-coordinates.png" alt="Diagram of window coordinates"></p>
78
   *
79
   * @param x1           X-coordinate of starting point
80
   * @param y1           Y-coordinate of staring point
81
   * @param ctrlX1       X-coordinate of Control Point 1
82
   * @param ctrlY1       Y-coordinate of Control Point 1
83
   * @param ctrlX2       X-coordinate of Control Point 2
84
   * @param ctrlY2       Y-coordinate of Control Point 2
85
   * @param x2           X-coordinate of end point
86
   * @param y2           Y-coordinate of end point
87
   * @param drawingPanel on which to draw
88
   */
89
  public CubicCurve(double x1, double y1, double ctrlX1, double ctrlY1, double ctrlX2, double ctrlY2, double x2, double y2, DrawingPanel drawingPanel) {
0 ignored issues
show
Comprehensibility introduced by
Constructor has 9 parameters, which is greater than 7 authorized.
Loading history...
90
    try {
91
      setShape(new CubicCurve2D.Double(x1, y1, ctrlX1, ctrlY1, ctrlX2, ctrlY2, x2, y2));
92
      setDrawingPanel(drawingPanel);
93
    } catch (DrawableException e) {
94
        System.err.println(e.getMessage());
95
      e.printStackTrace();
0 ignored issues
show
Best Practice introduced by
Throwable.printStackTrace writes to the console which might not be available at runtime. Using a logger is preferred.
Loading history...
96
    }
97
  }
98
99
  /**
100
   * @return Underlying {@link CubicCurve2D} geometry
101
   */
102
  protected CubicCurve2D getShapeAsCubicCurve() {
103
    return (CubicCurve2D) getShape();
104
  }
105
106
  @Override
107
  public void setShape(Shape shape) throws DrawableException {
108
    if (shape instanceof CubicCurve2D) {
109
      super.setShape(shape);
110
    } else {
111
      throw new DrawableException("Attempt to set CubicCurve's underlying shape to a non-CubicCurve2D instance");
112
    }
113
  }
114
115 View Code Duplication
  @Override
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
116
  public void setWidth(double width) {
117
    // FIXME this feels hacktacular
0 ignored issues
show
Code Smell introduced by
Tasks associated with this FIXME comment should be completed and this comment removed.
Loading history...
118
    double scale = width / getWidth();
119
    setCurve(
120
            getX() + (getX1() - getX()) * scale, getY1(),
121
            getX() + (getCtrlX1() - getX()) * scale, getCtrlY1(),
122
            getX() + (getCtrlX2() - getX()) * scale, getCtrlY2(),
123
            getX() + (getX2() - getX()) * scale, getY2()
124
    );
125
  }
126
127 View Code Duplication
  @Override
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
128
  public void setHeight(double height) {
129
    // FIXME this feels hacktackular
0 ignored issues
show
Code Smell introduced by
Tasks associated with this FIXME comment should be completed and this comment removed.
Loading history...
130
    double scale = height / getHeight();
131
    setCurve(
132
            getX1(), getY() + (getY1() - getY()) * scale,
133
            getCtrlX1(), getY() + (getCtrlY1() - getY()) * scale,
134
            getCtrlX2(), getY() + (getCtrlY2() - getY()) * scale,
135
            getX2(), getY() + (getY2() - getY()) * scale
136
    );
137
  }
138
139
  /**
140
   * Starting point
141
   *
142
   * @return Coordinates of starting point
143
   * @see CubicCurve2D#getP1()
144
   */
145
  public Point2D getP1() {
146
    return getShapeAsCubicCurve().getP1();
147
  }
148
149
  /**
150
   * Ending point
151
   *
152
   * @return Coordinates of ending point
153
   * @see CubicCurve2D#getP2()
154
   */
155
  public Point2D getP2() {
156
    return getShapeAsCubicCurve().getP2();
157
  }
158
159
  /**
160
   * <p>Set the points describing the curve</p>
161
   *
162
   * <p>This will leave other characteristics (e.g. fill color or stroke) unchanged</p>
163
   *
164
   * <p><img src="doc-files/CubicCurve.png" alt="Diagram of setCurve() Parameters"></p>
165
   *
166
   * @param x1     X-coordinate of starting point
167
   * @param y1     Y-coordinate of starting point
168
   * @param ctrlX1 X-coordinate of first control point
169
   * @param ctrlY1 Y-coordinate of first control point
170
   * @param ctrlX2 X-coordinate of second control point
171
   * @param ctrlY2 Y-coordinate of second control point
172
   * @param x2     X-coordinate of ending point
173
   * @param y2     Y-coordinate of ending point
174
   */
175
  public void setCurve(double x1, double y1, double ctrlX1, double ctrlY1, double ctrlX2, double ctrlY2, double x2, double y2) {
0 ignored issues
show
Comprehensibility introduced by
Method has 8 parameters, which is greater than 7 authorized.
Loading history...
176
    getShapeAsCubicCurve().setCurve(x1, y1, ctrlX1, ctrlY1, ctrlX2, ctrlY2, x2, y2);
177
  }
178
179
  /**
180
   * X-coordinate of starting point
181
   *
182
   * @return X-coordinate of starting point
183
   * @see CubicCurve2D#getX1()
184
   */
185
  public double getX1() {
186
    return getShapeAsCubicCurve().getX1();
187
  }
188
189
  /**
190
   * Y-coordinate of starting point
191
   *
192
   * @return Y-coordinate of starting point
193
   * @see CubicCurve2D#getY1()
194
   */
195
  public double getY1() {
196
    return getShapeAsCubicCurve().getY1();
197
  }
198
199
  /**
200
   * X-coordinate of first control point
201
   *
202
   * @return X-coordinate of first control point
203
   * @see CubicCurve2D#getCtrlX1()
204
   */
205
  public double getCtrlX1() {
206
    return getShapeAsCubicCurve().getCtrlX1();
207
  }
208
209
  /**
210
   * Y-coordinate of first control point
211
   *
212
   * @return Y-coordinate of first control point
213
   * @see CubicCurve2D#getCtrlY1()
214
   */
215
  public double getCtrlY1() {
216
    return getShapeAsCubicCurve().getCtrlY1();
217
  }
218
219
  /**
220
   * First control point
221
   *
222
   * @return First control point
223
   * @see CubicCurve2D#getCtrlP1()
224
   */
225
  public Point2D getCtrlP1() {
226
    return getShapeAsCubicCurve().getCtrlP1();
227
  }
228
229
  /**
230
   * X-coordinate of second control point
231
   *
232
   * @return X-coordinate of first control point
233
   * @see CubicCurve2D#getCtrlX2()
234
   */
235
  public double getCtrlX2() {
236
    return getShapeAsCubicCurve().getCtrlX2();
237
  }
238
239
  /**
240
   * Y-coordinate of second control point
241
   *
242
   * @return Y-coordinate of second control point
243
   * @see CubicCurve2D#getCtrlY2()
244
   */
245
  public double getCtrlY2() {
246
    return getShapeAsCubicCurve().getCtrlY2();
247
  }
248
249
  /**
250
   * Ending point
251
   *
252
   * @return Coordinates of ending point
253
   * @see CubicCurve2D#getP2()
254
   */
255
  public Point2D getCtrlP2() {
256
    return getShapeAsCubicCurve().getCtrlP2();
257
  }
258
259
  /**
260
   * X-coordinate of ending point
261
   *
262
   * @return X-coordinate of ending point
263
   * @see CubicCurve2D#getX2()
264
   */
265
  public double getX2() {
266
    return getShapeAsCubicCurve().getX2();
267
  }
268
269
  /**
270
   * Y-coordinate of ending point
271
   *
272
   * @return Y-coordinate of ending point
273
   * @see CubicCurve2D#getY2()
274
   */
275
  public double getY2() {
276
    return getShapeAsCubicCurve().getY2();
277
  }
278
279
  @Override
280
  public void translate(double dx, double dy) {
281
    getShapeAsCubicCurve().setCurve(
282
            getX1() + dx, getY1() + dy,
283
            getCtrlX1() + dx, getCtrlY1() + dy,
284
            getCtrlX2() + dx, getCtrlY2() + dy,
285
            getX2() + dx, getY2() + dy
286
    );
287
  }
288
289
  @Override
290
  public void setLocation(double x, double y) {
291
    translate(x - getX(), y - getY());
292
  }
293
}
294