1 /*
2 * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.java2d.marlin;
27
28 final class DCurve {
29
30 double ax, ay, bx, by, cx, cy, dx, dy;
31 double dax, day, dbx, dby;
32
33 DCurve() {
34 }
35
36 void set(double[] points, int type) {
37 switch(type) {
38 case 8:
39 set(points[0], points[1],
40 points[2], points[3],
41 points[4], points[5],
42 points[6], points[7]);
43 return;
44 case 6:
45 set(points[0], points[1],
46 points[2], points[3],
47 points[4], points[5]);
48 return;
49 default:
50 throw new InternalError("Curves can only be cubic or quadratic");
51 }
52 }
53
54 void set(double x1, double y1,
55 double x2, double y2,
56 double x3, double y3,
57 double x4, double y4)
58 {
59 final double dx32 = 3.0d * (x3 - x2);
60 final double dy32 = 3.0d * (y3 - y2);
61 final double dx21 = 3.0d * (x2 - x1);
62 final double dy21 = 3.0d * (y2 - y1);
63 ax = (x4 - x1) - dx32;
64 ay = (y4 - y1) - dy32;
65 bx = (dx32 - dx21);
66 by = (dy32 - dy21);
67 cx = dx21;
68 cy = dy21;
69 dx = x1;
70 dy = y1;
71 dax = 3.0d * ax; day = 3.0d * ay;
72 dbx = 2.0d * bx; dby = 2.0d * by;
73 }
74
75 void set(double x1, double y1,
76 double x2, double y2,
77 double x3, double y3)
78 {
79 final double dx21 = (x2 - x1);
80 final double dy21 = (y2 - y1);
81 ax = 0.0d; ay = 0.0d;
82 bx = (x3 - x2) - dx21;
83 by = (y3 - y2) - dy21;
84 cx = 2.0d * dx21;
85 cy = 2.0d * dy21;
86 dx = x1;
87 dy = y1;
88 dax = 0.0d; day = 0.0d;
89 dbx = 2.0d * bx; dby = 2.0d * by;
90 }
91
92 double xat(double t) {
93 return t * (t * (t * ax + bx) + cx) + dx;
94 }
95 double yat(double t) {
96 return t * (t * (t * ay + by) + cy) + dy;
97 }
98
99 double dxat(double t) {
100 return t * (t * dax + dbx) + cx;
101 }
102
103 double dyat(double t) {
104 return t * (t * day + dby) + cy;
105 }
106
107 int dxRoots(double[] roots, int off) {
108 return DHelpers.quadraticRoots(dax, dbx, cx, roots, off);
109 }
110
111 int dyRoots(double[] roots, int off) {
112 return DHelpers.quadraticRoots(day, dby, cy, roots, off);
113 }
114
115 int infPoints(double[] pts, int off) {
116 // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
117 // Fortunately, this turns out to be quadratic, so there are at
118 // most 2 inflection points.
119 final double a = dax * dby - dbx * day;
120 final double b = 2.0d * (cy * dax - day * cx);
121 final double c = cy * dbx - cx * dby;
122
123 return DHelpers.quadraticRoots(a, b, c, pts, off);
124 }
125
126 // finds points where the first and second derivative are
127 // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
128 // * is a dot product). Unfortunately, we have to solve a cubic.
129 private int perpendiculardfddf(double[] pts, int off) {
130 assert pts.length >= off + 4;
131
132 // these are the coefficients of some multiple of g(t) (not g(t),
133 // because the roots of a polynomial are not changed after multiplication
134 // by a constant, and this way we save a few multiplications).
135 final double a = 2.0d * (dax*dax + day*day);
136 final double b = 3.0d * (dax*dbx + day*dby);
137 final double c = 2.0d * (dax*cx + day*cy) + dbx*dbx + dby*dby;
138 final double d = dbx*cx + dby*cy;
139 return DHelpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
140 }
141
142 // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
143 // a variant of the false position algorithm to find the roots. False
144 // position requires that 2 initial values x0,x1 be given, and that the
145 // function must have opposite signs at those values. To find such
146 // values, we need the local extrema of the ROC function, for which we
147 // need the roots of its derivative; however, it's harder to find the
148 // roots of the derivative in this case than it is to find the roots
149 // of the original function. So, we find all points where this curve's
150 // first and second derivative are perpendicular, and we pretend these
151 // are our local extrema. There are at most 3 of these, so we will check
152 // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
153 // points, so roc-w can have at least 6 roots. This shouldn't be a
154 // problem for what we're trying to do (draw a nice looking curve).
155 int rootsOfROCMinusW(double[] roots, int off, final double w, final double err) {
156 // no OOB exception, because by now off<=6, and roots.length >= 10
157 assert off <= 6 && roots.length >= 10;
158 int ret = off;
159 int numPerpdfddf = perpendiculardfddf(roots, off);
160 double t0 = 0.0d, ft0 = ROCsq(t0) - w*w;
161 roots[off + numPerpdfddf] = 1.0d; // always check interval end points
162 numPerpdfddf++;
163 for (int i = off; i < off + numPerpdfddf; i++) {
164 double t1 = roots[i], ft1 = ROCsq(t1) - w*w;
165 if (ft0 == 0.0d) {
166 roots[ret++] = t0;
167 } else if (ft1 * ft0 < 0.0d) { // have opposite signs
168 // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
169 // ROC(t) >= 0 for all t.
170 roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
171 }
172 t0 = t1;
173 ft0 = ft1;
174 }
175
176 return ret - off;
177 }
178
179 private static double eliminateInf(double x) {
180 return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
181 (x == Double.NEGATIVE_INFINITY ? Double.MIN_VALUE : x));
182 }
183
184 // A slight modification of the false position algorithm on wikipedia.
185 // This only works for the ROCsq-x functions. It might be nice to have
186 // the function as an argument, but that would be awkward in java6.
187 // TODO: It is something to consider for java8 (or whenever lambda
188 // expressions make it into the language), depending on how closures
189 // and turn out. Same goes for the newton's method
190 // algorithm in DHelpers.java
191 private double falsePositionROCsqMinusX(double x0, double x1,
192 final double x, final double err)
193 {
194 final int iterLimit = 100;
195 int side = 0;
196 double t = x1, ft = eliminateInf(ROCsq(t) - x);
197 double s = x0, fs = eliminateInf(ROCsq(s) - x);
198 double r = s, fr;
199 for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
200 r = (fs * t - ft * s) / (fs - ft);
201 fr = ROCsq(r) - x;
202 if (sameSign(fr, ft)) {
203 ft = fr; t = r;
204 if (side < 0) {
205 fs /= (1 << (-side));
206 side--;
207 } else {
208 side = -1;
209 }
210 } else if (fr * fs > 0) {
211 fs = fr; s = r;
212 if (side > 0) {
213 ft /= (1 << side);
214 side++;
215 } else {
216 side = 1;
217 }
218 } else {
219 break;
220 }
221 }
222 return r;
223 }
224
225 private static boolean sameSign(double x, double y) {
226 // another way is to test if x*y > 0. This is bad for small x, y.
227 return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d);
228 }
229
230 // returns the radius of curvature squared at t of this curve
231 // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
232 private double ROCsq(final double t) {
233 // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
234 final double dx = t * (t * dax + dbx) + cx;
235 final double dy = t * (t * day + dby) + cy;
236 final double ddx = 2.0d * dax * t + dbx;
237 final double ddy = 2.0d * day * t + dby;
238 final double dx2dy2 = dx*dx + dy*dy;
239 final double ddx2ddy2 = ddx*ddx + ddy*ddy;
240 final double ddxdxddydy = ddx*dx + ddy*dy;
241 return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
242 }
243 }
--- EOF ---