1 /*
2 * Copyright (c) 1998, 2006, 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.awt.geom;
27
28 import java.awt.geom.Rectangle2D;
29 import java.awt.geom.PathIterator;
30 import java.awt.geom.QuadCurve2D;
31 import java.util.Vector;
32
33 final class Order3 extends Curve {
34 private double x0;
35 private double y0;
36 private double cx0;
37 private double cy0;
38 private double cx1;
39 private double cy1;
40 private double x1;
41 private double y1;
42
43 private double xmin;
44 private double xmax;
45
46 private double xcoeff0;
47 private double xcoeff1;
48 private double xcoeff2;
49 private double xcoeff3;
50
51 private double ycoeff0;
52 private double ycoeff1;
53 private double ycoeff2;
54 private double ycoeff3;
55
56 public static void insert(Vector<Curve> curves, double tmp[],
57 double x0, double y0,
58 double cx0, double cy0,
59 double cx1, double cy1,
60 double x1, double y1,
61 int direction)
62 {
63 int numparams = getHorizontalParams(y0, cy0, cy1, y1, tmp);
64 if (numparams == 0) {
65 // We are using addInstance here to avoid inserting horisontal
66 // segments
67 addInstance(curves, x0, y0, cx0, cy0, cx1, cy1, x1, y1, direction);
68 return;
69 }
70 // Store coordinates for splitting at tmp[3..10]
71 tmp[3] = x0; tmp[4] = y0;
72 tmp[5] = cx0; tmp[6] = cy0;
73 tmp[7] = cx1; tmp[8] = cy1;
74 tmp[9] = x1; tmp[10] = y1;
75 double t = tmp[0];
76 if (numparams > 1 && t > tmp[1]) {
77 // Perform a "2 element sort"...
78 tmp[0] = tmp[1];
79 tmp[1] = t;
80 t = tmp[0];
81 }
82 split(tmp, 3, t);
83 if (numparams > 1) {
84 // Recalculate tmp[1] relative to the range [tmp[0]...1]
85 t = (tmp[1] - t) / (1 - t);
86 split(tmp, 9, t);
87 }
88 int index = 3;
89 if (direction == DECREASING) {
90 index += numparams * 6;
91 }
92 while (numparams >= 0) {
93 addInstance(curves,
94 tmp[index + 0], tmp[index + 1],
95 tmp[index + 2], tmp[index + 3],
96 tmp[index + 4], tmp[index + 5],
97 tmp[index + 6], tmp[index + 7],
98 direction);
99 numparams--;
100 if (direction == INCREASING) {
101 index += 6;
102 } else {
103 index -= 6;
104 }
105 }
106 }
107
108 public static void addInstance(Vector<Curve> curves,
109 double x0, double y0,
110 double cx0, double cy0,
111 double cx1, double cy1,
112 double x1, double y1,
113 int direction) {
114 if (y0 > y1) {
115 curves.add(new Order3(x1, y1, cx1, cy1, cx0, cy0, x0, y0,
116 -direction));
117 } else if (y1 > y0) {
118 curves.add(new Order3(x0, y0, cx0, cy0, cx1, cy1, x1, y1,
119 direction));
120 }
121 }
122
123 /*
124 * Return the count of the number of horizontal sections of the
125 * specified cubic Bezier curve. Put the parameters for the
126 * horizontal sections into the specified <code>ret</code> array.
127 * <p>
128 * If we examine the parametric equation in t, we have:
129 * Py(t) = C0(1-t)^3 + 3CP0 t(1-t)^2 + 3CP1 t^2(1-t) + C1 t^3
130 * = C0 - 3C0t + 3C0t^2 - C0t^3 +
131 * 3CP0t - 6CP0t^2 + 3CP0t^3 +
132 * 3CP1t^2 - 3CP1t^3 +
133 * C1t^3
134 * Py(t) = (C1 - 3CP1 + 3CP0 - C0) t^3 +
135 * (3C0 - 6CP0 + 3CP1) t^2 +
136 * (3CP0 - 3C0) t +
137 * (C0)
138 * If we take the derivative, we get:
139 * Py(t) = Dt^3 + At^2 + Bt + C
140 * dPy(t) = 3Dt^2 + 2At + B = 0
141 * 0 = 3*(C1 - 3*CP1 + 3*CP0 - C0)t^2
142 * + 2*(3*CP1 - 6*CP0 + 3*C0)t
143 * + (3*CP0 - 3*C0)
144 * 0 = 3*(C1 - 3*CP1 + 3*CP0 - C0)t^2
145 * + 3*2*(CP1 - 2*CP0 + C0)t
146 * + 3*(CP0 - C0)
147 * 0 = (C1 - CP1 - CP1 - CP1 + CP0 + CP0 + CP0 - C0)t^2
148 * + 2*(CP1 - CP0 - CP0 + C0)t
149 * + (CP0 - C0)
150 * 0 = (C1 - CP1 + CP0 - CP1 + CP0 - CP1 + CP0 - C0)t^2
151 * + 2*(CP1 - CP0 - CP0 + C0)t
152 * + (CP0 - C0)
153 * 0 = ((C1 - CP1) - (CP1 - CP0) - (CP1 - CP0) + (CP0 - C0))t^2
154 * + 2*((CP1 - CP0) - (CP0 - C0))t
155 * + (CP0 - C0)
156 * Note that this method will return 0 if the equation is a line,
157 * which is either always horizontal or never horizontal.
158 * Completely horizontal curves need to be eliminated by other
159 * means outside of this method.
160 */
161 public static int getHorizontalParams(double c0, double cp0,
162 double cp1, double c1,
163 double ret[]) {
164 if (c0 <= cp0 && cp0 <= cp1 && cp1 <= c1) {
165 return 0;
166 }
167 c1 -= cp1;
168 cp1 -= cp0;
169 cp0 -= c0;
170 ret[0] = cp0;
171 ret[1] = (cp1 - cp0) * 2;
172 ret[2] = (c1 - cp1 - cp1 + cp0);
173 int numroots = QuadCurve2D.solveQuadratic(ret, ret);
174 int j = 0;
175 for (int i = 0; i < numroots; i++) {
176 double t = ret[i];
177 // No splits at t==0 and t==1
178 if (t > 0 && t < 1) {
179 if (j < i) {
180 ret[j] = t;
181 }
182 j++;
183 }
184 }
185 return j;
186 }
187
188 /*
189 * Split the cubic Bezier stored at coords[pos...pos+7] representing
190 * the parametric range [0..1] into two subcurves representing the
191 * parametric subranges [0..t] and [t..1]. Store the results back
192 * into the array at coords[pos...pos+7] and coords[pos+6...pos+13].
193 */
194 public static void split(double coords[], int pos, double t) {
195 double x0, y0, cx0, cy0, cx1, cy1, x1, y1;
196 coords[pos+12] = x1 = coords[pos+6];
197 coords[pos+13] = y1 = coords[pos+7];
198 cx1 = coords[pos+4];
199 cy1 = coords[pos+5];
200 x1 = cx1 + (x1 - cx1) * t;
201 y1 = cy1 + (y1 - cy1) * t;
202 x0 = coords[pos+0];
203 y0 = coords[pos+1];
204 cx0 = coords[pos+2];
205 cy0 = coords[pos+3];
206 x0 = x0 + (cx0 - x0) * t;
207 y0 = y0 + (cy0 - y0) * t;
208 cx0 = cx0 + (cx1 - cx0) * t;
209 cy0 = cy0 + (cy1 - cy0) * t;
210 cx1 = cx0 + (x1 - cx0) * t;
211 cy1 = cy0 + (y1 - cy0) * t;
212 cx0 = x0 + (cx0 - x0) * t;
213 cy0 = y0 + (cy0 - y0) * t;
214 coords[pos+2] = x0;
215 coords[pos+3] = y0;
216 coords[pos+4] = cx0;
217 coords[pos+5] = cy0;
218 coords[pos+6] = cx0 + (cx1 - cx0) * t;
219 coords[pos+7] = cy0 + (cy1 - cy0) * t;
220 coords[pos+8] = cx1;
221 coords[pos+9] = cy1;
222 coords[pos+10] = x1;
223 coords[pos+11] = y1;
224 }
225
226 public Order3(double x0, double y0,
227 double cx0, double cy0,
228 double cx1, double cy1,
229 double x1, double y1,
230 int direction)
231 {
232 super(direction);
233 // REMIND: Better accuracy in the root finding methods would
234 // ensure that cys are in range. As it stands, they are never
235 // more than "1 mantissa bit" out of range...
236 if (cy0 < y0) cy0 = y0;
237 if (cy1 > y1) cy1 = y1;
238 this.x0 = x0;
239 this.y0 = y0;
240 this.cx0 = cx0;
241 this.cy0 = cy0;
242 this.cx1 = cx1;
243 this.cy1 = cy1;
244 this.x1 = x1;
245 this.y1 = y1;
246 xmin = Math.min(Math.min(x0, x1), Math.min(cx0, cx1));
247 xmax = Math.max(Math.max(x0, x1), Math.max(cx0, cx1));
248 xcoeff0 = x0;
249 xcoeff1 = (cx0 - x0) * 3.0;
250 xcoeff2 = (cx1 - cx0 - cx0 + x0) * 3.0;
251 xcoeff3 = x1 - (cx1 - cx0) * 3.0 - x0;
252 ycoeff0 = y0;
253 ycoeff1 = (cy0 - y0) * 3.0;
254 ycoeff2 = (cy1 - cy0 - cy0 + y0) * 3.0;
255 ycoeff3 = y1 - (cy1 - cy0) * 3.0 - y0;
256 YforT1 = YforT2 = YforT3 = y0;
257 }
258
259 public int getOrder() {
260 return 3;
261 }
262
263 public double getXTop() {
264 return x0;
265 }
266
267 public double getYTop() {
268 return y0;
269 }
270
271 public double getXBot() {
272 return x1;
273 }
274
275 public double getYBot() {
276 return y1;
277 }
278
279 public double getXMin() {
280 return xmin;
281 }
282
283 public double getXMax() {
284 return xmax;
285 }
286
287 public double getX0() {
288 return (direction == INCREASING) ? x0 : x1;
289 }
290
291 public double getY0() {
292 return (direction == INCREASING) ? y0 : y1;
293 }
294
295 public double getCX0() {
296 return (direction == INCREASING) ? cx0 : cx1;
297 }
298
299 public double getCY0() {
300 return (direction == INCREASING) ? cy0 : cy1;
301 }
302
303 public double getCX1() {
304 return (direction == DECREASING) ? cx0 : cx1;
305 }
306
307 public double getCY1() {
308 return (direction == DECREASING) ? cy0 : cy1;
309 }
310
311 public double getX1() {
312 return (direction == DECREASING) ? x0 : x1;
313 }
314
315 public double getY1() {
316 return (direction == DECREASING) ? y0 : y1;
317 }
318
319 private double TforY1;
320 private double YforT1;
321 private double TforY2;
322 private double YforT2;
323 private double TforY3;
324 private double YforT3;
325
326 /*
327 * Solve the cubic whose coefficients are in the a,b,c,d fields and
328 * return the first root in the range [0, 1].
329 * The cubic solved is represented by the equation:
330 * x^3 + (ycoeff2)x^2 + (ycoeff1)x + (ycoeff0) = y
331 * @return the first valid root (in the range [0, 1])
332 */
333 public double TforY(double y) {
334 if (y <= y0) return 0;
335 if (y >= y1) return 1;
336 if (y == YforT1) return TforY1;
337 if (y == YforT2) return TforY2;
338 if (y == YforT3) return TforY3;
339 // From Numerical Recipes, 5.6, Quadratic and Cubic Equations
340 if (ycoeff3 == 0.0) {
341 // The cubic degenerated to quadratic (or line or ...).
342 return Order2.TforY(y, ycoeff0, ycoeff1, ycoeff2);
343 }
344 double a = ycoeff2 / ycoeff3;
345 double b = ycoeff1 / ycoeff3;
346 double c = (ycoeff0 - y) / ycoeff3;
347 int roots = 0;
348 double Q = (a * a - 3.0 * b) / 9.0;
349 double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0;
350 double R2 = R * R;
351 double Q3 = Q * Q * Q;
352 double a_3 = a / 3.0;
353 double t;
354 if (R2 < Q3) {
355 double theta = Math.acos(R / Math.sqrt(Q3));
356 Q = -2.0 * Math.sqrt(Q);
357 t = refine(a, b, c, y, Q * Math.cos(theta / 3.0) - a_3);
358 if (t < 0) {
359 t = refine(a, b, c, y,
360 Q * Math.cos((theta + Math.PI * 2.0)/ 3.0) - a_3);
361 }
362 if (t < 0) {
363 t = refine(a, b, c, y,
364 Q * Math.cos((theta - Math.PI * 2.0)/ 3.0) - a_3);
365 }
366 } else {
367 boolean neg = (R < 0.0);
368 double S = Math.sqrt(R2 - Q3);
369 if (neg) {
370 R = -R;
371 }
372 double A = Math.pow(R + S, 1.0 / 3.0);
373 if (!neg) {
374 A = -A;
375 }
376 double B = (A == 0.0) ? 0.0 : (Q / A);
377 t = refine(a, b, c, y, (A + B) - a_3);
378 }
379 if (t < 0) {
380 //throw new InternalError("bad t");
381 double t0 = 0;
382 double t1 = 1;
383 while (true) {
384 t = (t0 + t1) / 2;
385 if (t == t0 || t == t1) {
386 break;
387 }
388 double yt = YforT(t);
389 if (yt < y) {
390 t0 = t;
391 } else if (yt > y) {
392 t1 = t;
393 } else {
394 break;
395 }
396 }
397 }
398 if (t >= 0) {
399 TforY3 = TforY2;
400 YforT3 = YforT2;
401 TforY2 = TforY1;
402 YforT2 = YforT1;
403 TforY1 = t;
404 YforT1 = y;
405 }
406 return t;
407 }
408
409 public double refine(double a, double b, double c,
410 double target, double t)
411 {
412 if (t < -0.1 || t > 1.1) {
413 return -1;
414 }
415 double y = YforT(t);
416 double t0, t1;
417 if (y < target) {
418 t0 = t;
419 t1 = 1;
420 } else {
421 t0 = 0;
422 t1 = t;
423 }
424 double origt = t;
425 double origy = y;
426 boolean useslope = true;
427 while (y != target) {
428 if (!useslope) {
429 double t2 = (t0 + t1) / 2;
430 if (t2 == t0 || t2 == t1) {
431 break;
432 }
433 t = t2;
434 } else {
435 double slope = dYforT(t, 1);
436 if (slope == 0) {
437 useslope = false;
438 continue;
439 }
440 double t2 = t + ((target - y) / slope);
441 if (t2 == t || t2 <= t0 || t2 >= t1) {
442 useslope = false;
443 continue;
444 }
445 t = t2;
446 }
447 y = YforT(t);
448 if (y < target) {
449 t0 = t;
450 } else if (y > target) {
451 t1 = t;
452 } else {
453 break;
454 }
455 }
456 boolean verbose = false;
457 if (false && t >= 0 && t <= 1) {
458 y = YforT(t);
459 long tdiff = diffbits(t, origt);
460 long ydiff = diffbits(y, origy);
461 long yerr = diffbits(y, target);
462 if (yerr > 0 || (verbose && tdiff > 0)) {
463 System.out.println("target was y = "+target);
464 System.out.println("original was y = "+origy+", t = "+origt);
465 System.out.println("final was y = "+y+", t = "+t);
466 System.out.println("t diff is "+tdiff);
467 System.out.println("y diff is "+ydiff);
468 System.out.println("y error is "+yerr);
469 double tlow = prev(t);
470 double ylow = YforT(tlow);
471 double thi = next(t);
472 double yhi = YforT(thi);
473 if (Math.abs(target - ylow) < Math.abs(target - y) ||
474 Math.abs(target - yhi) < Math.abs(target - y))
475 {
476 System.out.println("adjacent y's = ["+ylow+", "+yhi+"]");
477 }
478 }
479 }
480 return (t > 1) ? -1 : t;
481 }
482
483 public double XforY(double y) {
484 if (y <= y0) {
485 return x0;
486 }
487 if (y >= y1) {
488 return x1;
489 }
490 return XforT(TforY(y));
491 }
492
493 public double XforT(double t) {
494 return (((xcoeff3 * t) + xcoeff2) * t + xcoeff1) * t + xcoeff0;
495 }
496
497 public double YforT(double t) {
498 return (((ycoeff3 * t) + ycoeff2) * t + ycoeff1) * t + ycoeff0;
499 }
500
501 public double dXforT(double t, int deriv) {
502 switch (deriv) {
503 case 0:
504 return (((xcoeff3 * t) + xcoeff2) * t + xcoeff1) * t + xcoeff0;
505 case 1:
506 return ((3 * xcoeff3 * t) + 2 * xcoeff2) * t + xcoeff1;
507 case 2:
508 return (6 * xcoeff3 * t) + 2 * xcoeff2;
509 case 3:
510 return 6 * xcoeff3;
511 default:
512 return 0;
513 }
514 }
515
516 public double dYforT(double t, int deriv) {
517 switch (deriv) {
518 case 0:
519 return (((ycoeff3 * t) + ycoeff2) * t + ycoeff1) * t + ycoeff0;
520 case 1:
521 return ((3 * ycoeff3 * t) + 2 * ycoeff2) * t + ycoeff1;
522 case 2:
523 return (6 * ycoeff3 * t) + 2 * ycoeff2;
524 case 3:
525 return 6 * ycoeff3;
526 default:
527 return 0;
528 }
529 }
530
531 public double nextVertical(double t0, double t1) {
532 double eqn[] = {xcoeff1, 2 * xcoeff2, 3 * xcoeff3};
533 int numroots = QuadCurve2D.solveQuadratic(eqn, eqn);
534 for (int i = 0; i < numroots; i++) {
535 if (eqn[i] > t0 && eqn[i] < t1) {
536 t1 = eqn[i];
537 }
538 }
539 return t1;
540 }
541
542 public void enlarge(Rectangle2D r) {
543 r.add(x0, y0);
544 double eqn[] = {xcoeff1, 2 * xcoeff2, 3 * xcoeff3};
545 int numroots = QuadCurve2D.solveQuadratic(eqn, eqn);
546 for (int i = 0; i < numroots; i++) {
547 double t = eqn[i];
548 if (t > 0 && t < 1) {
549 r.add(XforT(t), YforT(t));
550 }
551 }
552 r.add(x1, y1);
553 }
554
555 public Curve getSubCurve(double ystart, double yend, int dir) {
556 if (ystart <= y0 && yend >= y1) {
557 return getWithDirection(dir);
558 }
559 double eqn[] = new double[14];
560 double t0, t1;
561 t0 = TforY(ystart);
562 t1 = TforY(yend);
563 eqn[0] = x0;
564 eqn[1] = y0;
565 eqn[2] = cx0;
566 eqn[3] = cy0;
567 eqn[4] = cx1;
568 eqn[5] = cy1;
569 eqn[6] = x1;
570 eqn[7] = y1;
571 if (t0 > t1) {
572 /* This happens in only rare cases where ystart is
573 * very near yend and solving for the yend root ends
574 * up stepping slightly lower in t than solving for
575 * the ystart root.
576 * Ideally we might want to skip this tiny little
577 * segment and just fudge the surrounding coordinates
578 * to bridge the gap left behind, but there is no way
579 * to do that from here. Higher levels could
580 * potentially eliminate these tiny "fixup" segments,
581 * but not without a lot of extra work on the code that
582 * coalesces chains of curves into subpaths. The
583 * simplest solution for now is to just reorder the t
584 * values and chop out a miniscule curve piece.
585 */
586 double t = t0;
587 t0 = t1;
588 t1 = t;
589 }
590 if (t1 < 1) {
591 split(eqn, 0, t1);
592 }
593 int i;
594 if (t0 <= 0) {
595 i = 0;
596 } else {
597 split(eqn, 0, t0 / t1);
598 i = 6;
599 }
600 return new Order3(eqn[i+0], ystart,
601 eqn[i+2], eqn[i+3],
602 eqn[i+4], eqn[i+5],
603 eqn[i+6], yend,
604 dir);
605 }
606
607 public Curve getReversedCurve() {
608 return new Order3(x0, y0, cx0, cy0, cx1, cy1, x1, y1, -direction);
609 }
610
611 public int getSegment(double coords[]) {
612 if (direction == INCREASING) {
613 coords[0] = cx0;
614 coords[1] = cy0;
615 coords[2] = cx1;
616 coords[3] = cy1;
617 coords[4] = x1;
618 coords[5] = y1;
619 } else {
620 coords[0] = cx1;
621 coords[1] = cy1;
622 coords[2] = cx0;
623 coords[3] = cy0;
624 coords[4] = x0;
625 coords[5] = y0;
626 }
627 return PathIterator.SEG_CUBICTO;
628 }
629
630 public String controlPointString() {
631 return (("("+round(getCX0())+", "+round(getCY0())+"), ")+
632 ("("+round(getCX1())+", "+round(getCY1())+"), "));
633 }
634 }
--- EOF ---