1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 /** 18 * @author Denis M. Kishenko 19 * @version $Revision$ 20 */ 21 22 package java.awt; 23 24 import java.awt.geom.GeneralPath; 25 import java.awt.geom.PathIterator; 26 27 import org.apache.harmony.awt.internal.nls.Messages; 28 import org.apache.harmony.misc.HashCode; 29 30 /** 31 * The BasicStroke class specifies a set of rendering attributes for the 32 * outlines of graphics primitives. The BasicStroke attributes describe the 33 * shape of the pen which draws the outline of a Shape and the decorations 34 * applied at the ends and joins of path segments of the Shape. The BasicStroke 35 * has the following rendering attributes: 36 * <p> 37 * <ul> 38 * <li>line width -the pen width which draws the outlines.</li> 39 * <li>end caps - indicates the decoration applied to the ends of unclosed 40 * subpaths and dash segments. The BasicStroke defines three different 41 * decorations: CAP_BUTT, CAP_ROUND, and CAP_SQUARE.</li> 42 * <li>line joins - indicates the decoration applied at the intersection of two 43 * path segments and at the intersection of the endpoints of a subpath. The 44 * BasicStroke defines three decorations: JOIN_BEVEL, JOIN_MITER, and 45 * JOIN_ROUND.</li> 46 * <li>miter limit - the limit to trim a line join that has a JOIN_MITER 47 * decoration.</li> 48 * <li>dash attributes - the definition of how to make a dash pattern by 49 * alternating between opaque and transparent sections</li> 50 * </ul> 51 * </p> 52 * 53 * @since Android 1.0 54 */ 55 public class BasicStroke implements Stroke { 56 57 /** 58 * The Constant CAP_BUTT indicates the ends of unclosed subpaths and dash 59 * segments have no added decoration. 60 */ 61 public static final int CAP_BUTT = 0; 62 63 /** 64 * The Constant CAP_ROUND indicates the ends of unclosed subpaths and dash 65 * segments have a round decoration. 66 */ 67 public static final int CAP_ROUND = 1; 68 69 /** 70 * The Constant CAP_SQUARE indicates the ends of unclosed subpaths and dash 71 * segments have a square projection. 72 */ 73 public static final int CAP_SQUARE = 2; 74 75 /** 76 * The Constant JOIN_MITER indicates that path segments are joined by 77 * extending their outside edges until they meet. 78 */ 79 public static final int JOIN_MITER = 0; 80 81 /** 82 * The Constant JOIN_ROUND indicates that path segments are joined by 83 * rounding off the corner at a radius of half the line width. 84 */ 85 public static final int JOIN_ROUND = 1; 86 87 /** 88 * The Constant JOIN_BEVEL indicates that path segments are joined by 89 * connecting the outer corners of their wide outlines with a straight 90 * segment. 91 */ 92 public static final int JOIN_BEVEL = 2; 93 94 /** 95 * Constants for calculating. 96 */ 97 static final int MAX_LEVEL = 20; // Maximal deepness of curve subdivision 98 99 /** 100 * The Constant CURVE_DELTA. 101 */ 102 static final double CURVE_DELTA = 2.0; // Width tolerance 103 104 /** 105 * The Constant CORNER_ANGLE. 106 */ 107 static final double CORNER_ANGLE = 4.0; // Minimum corner angle 108 109 /** 110 * The Constant CORNER_ZERO. 111 */ 112 static final double CORNER_ZERO = 0.01; // Zero angle 113 114 /** 115 * The Constant CUBIC_ARC. 116 */ 117 static final double CUBIC_ARC = 4.0 / 3.0 * (Math.sqrt(2.0) - 1); 118 119 /** 120 * Stroke width. 121 */ 122 float width; 123 124 /** 125 * Stroke cap type. 126 */ 127 int cap; 128 129 /** 130 * Stroke join type. 131 */ 132 int join; 133 134 /** 135 * Stroke miter limit. 136 */ 137 float miterLimit; 138 139 /** 140 * Stroke dashes array. 141 */ 142 float dash[]; 143 144 /** 145 * Stroke dash phase. 146 */ 147 float dashPhase; 148 149 /** 150 * The temporary pre-calculated values. 151 */ 152 double curveDelta; 153 154 /** 155 * The corner delta. 156 */ 157 double cornerDelta; 158 159 /** 160 * The zero delta. 161 */ 162 double zeroDelta; 163 164 /** 165 * The w2. 166 */ 167 double w2; 168 169 /** 170 * The fmy. 171 */ 172 double fmx, fmy; 173 174 /** 175 * The smy. 176 */ 177 double scx, scy, smx, smy; 178 179 /** 180 * The cy. 181 */ 182 double mx, my, cx, cy; 183 184 /** 185 * The temporary indicators. 186 */ 187 boolean isMove; 188 189 /** 190 * The is first. 191 */ 192 boolean isFirst; 193 194 /** 195 * The check move. 196 */ 197 boolean checkMove; 198 199 /** 200 * The temporary and destination work paths. 201 */ 202 BufferedPath dst, lp, rp, sp; 203 204 /** 205 * Stroke dasher class. 206 */ 207 Dasher dasher; 208 209 /** 210 * Instantiates a new BasicStroke with default width, cap, join, limit, dash 211 * attributes parameters. The default parameters are a solid line of width 212 * 1.0, CAP_SQUARE, JOIN_MITER, a miter limit of 10.0, null dash attributes, 213 * and a dash phase of 0.0f. 214 */ 215 public BasicStroke() { 216 this(1.0f, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f); 217 } 218 219 /** 220 * Instantiates a new BasicStroke with the specified width, caps, joins, 221 * limit, dash attributes, dash phase parameters. 222 * 223 * @param width 224 * the width of BasikStroke. 225 * @param cap 226 * the end decoration of BasikStroke. 227 * @param join 228 * the join segments decoration. 229 * @param miterLimit 230 * the limit to trim the miter join. 231 * @param dash 232 * the array with the dashing pattern. 233 * @param dashPhase 234 * the offset to start the dashing pattern. 235 */ 236 public BasicStroke(float width, int cap, int join, float miterLimit, float[] dash, 237 float dashPhase) { 238 if (width < 0.0f) { 239 // awt.133=Negative width 240 throw new IllegalArgumentException(Messages.getString("awt.133")); //$NON-NLS-1$ 241 } 242 if (cap != CAP_BUTT && cap != CAP_ROUND && cap != CAP_SQUARE) { 243 // awt.134=Illegal cap 244 throw new IllegalArgumentException(Messages.getString("awt.134")); //$NON-NLS-1$ 245 } 246 if (join != JOIN_MITER && join != JOIN_ROUND && join != JOIN_BEVEL) { 247 // awt.135=Illegal join 248 throw new IllegalArgumentException(Messages.getString("awt.135")); //$NON-NLS-1$ 249 } 250 if (join == JOIN_MITER && miterLimit < 1.0f) { 251 // awt.136=miterLimit less than 1.0f 252 throw new IllegalArgumentException(Messages.getString("awt.136")); //$NON-NLS-1$ 253 } 254 if (dash != null) { 255 if (dashPhase < 0.0f) { 256 // awt.137=Negative dashPhase 257 throw new IllegalArgumentException(Messages.getString("awt.137")); //$NON-NLS-1$ 258 } 259 if (dash.length == 0) { 260 // awt.138=Zero dash length 261 throw new IllegalArgumentException(Messages.getString("awt.138")); //$NON-NLS-1$ 262 } 263 ZERO: { 264 for (int i = 0; i < dash.length; i++) { 265 if (dash[i] < 0.0) { 266 // awt.139=Negative dash[{0}] 267 throw new IllegalArgumentException(Messages.getString("awt.139", i)); //$NON-NLS-1$ 268 } 269 if (dash[i] > 0.0) { 270 break ZERO; 271 } 272 } 273 // awt.13A=All dash lengths zero 274 throw new IllegalArgumentException(Messages.getString("awt.13A")); //$NON-NLS-1$ 275 } 276 } 277 this.width = width; 278 this.cap = cap; 279 this.join = join; 280 this.miterLimit = miterLimit; 281 this.dash = dash; 282 this.dashPhase = dashPhase; 283 } 284 285 /** 286 * Instantiates a new BasicStroke with specified width, cap, join, limit and 287 * default dash attributes parameters. 288 * 289 * @param width 290 * the width of BasikStroke. 291 * @param cap 292 * the end decoration of BasikStroke. 293 * @param join 294 * the join segments decoration. 295 * @param miterLimit 296 * the limit to trim the miter join. 297 */ 298 public BasicStroke(float width, int cap, int join, float miterLimit) { 299 this(width, cap, join, miterLimit, null, 0.0f); 300 } 301 302 /** 303 * Instantiates a new BasicStroke with specified width, cap, join and 304 * default limit and dash attributes parameters. 305 * 306 * @param width 307 * the width of BasikStroke. 308 * @param cap 309 * the end decoration of BasikStroke. 310 * @param join 311 * the join segments decoration. 312 */ 313 public BasicStroke(float width, int cap, int join) { 314 this(width, cap, join, 10.0f, null, 0.0f); 315 } 316 317 /** 318 * Instantiates a new BasicStroke with specified width and default cap, 319 * join, limit, dash attributes parameters. 320 * 321 * @param width 322 * the width of BasicStroke. 323 */ 324 public BasicStroke(float width) { 325 this(width, CAP_SQUARE, JOIN_MITER, 10.0f, null, 0.0f); 326 } 327 328 /** 329 * Gets the line width of the BasicStroke. 330 * 331 * @return the line width of the BasicStroke. 332 */ 333 public float getLineWidth() { 334 return width; 335 } 336 337 /** 338 * Gets the end cap style of the BasicStroke. 339 * 340 * @return the end cap style of the BasicStroke. 341 */ 342 public int getEndCap() { 343 return cap; 344 } 345 346 /** 347 * Gets the line join style of the BasicStroke. 348 * 349 * @return the line join style of the BasicStroke. 350 */ 351 public int getLineJoin() { 352 return join; 353 } 354 355 /** 356 * Gets the miter limit of the BasicStroke (the limit to trim the miter 357 * join). 358 * 359 * @return the miter limit of the BasicStroke. 360 */ 361 public float getMiterLimit() { 362 return miterLimit; 363 } 364 365 /** 366 * Gets the dash attributes array of the BasicStroke. 367 * 368 * @return the dash attributes array of the BasicStroke. 369 */ 370 public float[] getDashArray() { 371 return dash; 372 } 373 374 /** 375 * Gets the dash phase of the BasicStroke. 376 * 377 * @return the dash phase of the BasicStroke. 378 */ 379 public float getDashPhase() { 380 return dashPhase; 381 } 382 383 /** 384 * Returns hash code of this BasicStroke. 385 * 386 * @return the hash code of this BasicStroke. 387 */ 388 @Override 389 public int hashCode() { 390 HashCode hash = new HashCode(); 391 hash.append(width); 392 hash.append(cap); 393 hash.append(join); 394 hash.append(miterLimit); 395 if (dash != null) { 396 hash.append(dashPhase); 397 for (float element : dash) { 398 hash.append(element); 399 } 400 } 401 return hash.hashCode(); 402 } 403 404 /** 405 * Compares this BasicStroke object with the specified Object. 406 * 407 * @param obj 408 * the Object to be compared. 409 * @return true, if the Object is a BasicStroke with the same data values as 410 * this BasicStroke; false otherwise. 411 */ 412 @Override 413 public boolean equals(Object obj) { 414 if (obj == this) { 415 return true; 416 } 417 if (obj instanceof BasicStroke) { 418 BasicStroke bs = (BasicStroke)obj; 419 return bs.width == width && bs.cap == cap && bs.join == join 420 && bs.miterLimit == miterLimit && bs.dashPhase == dashPhase 421 && java.util.Arrays.equals(bs.dash, dash); 422 } 423 return false; 424 } 425 426 /** 427 * Calculates allowable curve derivation. 428 * 429 * @param width 430 * the width. 431 * @return the curve delta. 432 */ 433 double getCurveDelta(double width) { 434 double a = width + CURVE_DELTA; 435 double cos = 1.0 - 2.0 * width * width / (a * a); 436 double sin = Math.sqrt(1.0 - cos * cos); 437 return Math.abs(sin / cos); 438 } 439 440 /** 441 * Calculates the value to detect a small angle. 442 * 443 * @param width 444 * the width. 445 * @return the corner delta. 446 */ 447 double getCornerDelta(double width) { 448 return width * width * Math.sin(Math.PI * CORNER_ANGLE / 180.0); 449 } 450 451 /** 452 * Calculates value to detect a zero angle. 453 * 454 * @param width 455 * the width. 456 * @return the zero delta. 457 */ 458 double getZeroDelta(double width) { 459 return width * width * Math.sin(Math.PI * CORNER_ZERO / 180.0); 460 } 461 462 /** 463 * Creates a Shape from the outline of the specified shape drawn with this 464 * BasicStroke. 465 * 466 * @param s 467 * the specified Shape to be stroked. 468 * @return the Shape of the stroked outline. 469 * @see java.awt.Stroke#createStrokedShape(java.awt.Shape) 470 */ 471 public Shape createStrokedShape(Shape s) { 472 w2 = width / 2.0; 473 curveDelta = getCurveDelta(w2); 474 cornerDelta = getCornerDelta(w2); 475 zeroDelta = getZeroDelta(w2); 476 477 dst = new BufferedPath(); 478 lp = new BufferedPath(); 479 rp = new BufferedPath(); 480 481 if (dash == null) { 482 createSolidShape(s.getPathIterator(null)); 483 } else { 484 createDashedShape(s.getPathIterator(null)); 485 } 486 487 return dst.createGeneralPath(); 488 } 489 490 /** 491 * Generates a shape with a solid (not dashed) outline. 492 * 493 * @param p 494 * the PathIterator of source shape. 495 */ 496 void createSolidShape(PathIterator p) { 497 double coords[] = new double[6]; 498 mx = my = cx = cy = 0.0; 499 isMove = false; 500 isFirst = false; 501 checkMove = true; 502 boolean isClosed = true; 503 504 while (!p.isDone()) { 505 switch (p.currentSegment(coords)) { 506 case PathIterator.SEG_MOVETO: 507 if (!isClosed) { 508 closeSolidShape(); 509 } 510 rp.clean(); 511 mx = cx = coords[0]; 512 my = cy = coords[1]; 513 isMove = true; 514 isClosed = false; 515 break; 516 case PathIterator.SEG_LINETO: 517 addLine(cx, cy, cx = coords[0], cy = coords[1], true); 518 break; 519 case PathIterator.SEG_QUADTO: 520 addQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3]); 521 break; 522 case PathIterator.SEG_CUBICTO: 523 addCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], cx = coords[4], 524 cy = coords[5]); 525 break; 526 case PathIterator.SEG_CLOSE: 527 addLine(cx, cy, mx, my, false); 528 addJoin(lp, mx, my, lp.xMove, lp.yMove, true); 529 addJoin(rp, mx, my, rp.xMove, rp.yMove, false); 530 lp.closePath(); 531 rp.closePath(); 532 lp.appendReverse(rp); 533 isClosed = true; 534 break; 535 } 536 p.next(); 537 } 538 if (!isClosed) { 539 closeSolidShape(); 540 } 541 542 dst = lp; 543 } 544 545 /** 546 * Closes solid shape path. 547 */ 548 void closeSolidShape() { 549 addCap(lp, cx, cy, rp.xLast, rp.yLast); 550 lp.combine(rp); 551 addCap(lp, mx, my, lp.xMove, lp.yMove); 552 lp.closePath(); 553 } 554 555 /** 556 * Generates dashed stroked shape. 557 * 558 * @param p 559 * the PathIterator of source shape. 560 */ 561 void createDashedShape(PathIterator p) { 562 double coords[] = new double[6]; 563 mx = my = cx = cy = 0.0; 564 smx = smy = scx = scy = 0.0; 565 isMove = false; 566 checkMove = false; 567 boolean isClosed = true; 568 569 while (!p.isDone()) { 570 switch (p.currentSegment(coords)) { 571 case PathIterator.SEG_MOVETO: 572 573 if (!isClosed) { 574 closeDashedShape(); 575 } 576 577 dasher = new Dasher(dash, dashPhase); 578 lp.clean(); 579 rp.clean(); 580 sp = null; 581 isFirst = true; 582 isMove = true; 583 isClosed = false; 584 mx = cx = coords[0]; 585 my = cy = coords[1]; 586 break; 587 case PathIterator.SEG_LINETO: 588 addDashLine(cx, cy, cx = coords[0], cy = coords[1]); 589 break; 590 case PathIterator.SEG_QUADTO: 591 addDashQuad(cx, cy, coords[0], coords[1], cx = coords[2], cy = coords[3]); 592 break; 593 case PathIterator.SEG_CUBICTO: 594 addDashCubic(cx, cy, coords[0], coords[1], coords[2], coords[3], 595 cx = coords[4], cy = coords[5]); 596 break; 597 case PathIterator.SEG_CLOSE: 598 addDashLine(cx, cy, cx = mx, cy = my); 599 600 if (dasher.isConnected()) { 601 // Connect current and head segments 602 addJoin(lp, fmx, fmy, sp.xMove, sp.yMove, true); 603 lp.join(sp); 604 addJoin(lp, fmx, fmy, rp.xLast, rp.yLast, true); 605 lp.combine(rp); 606 addCap(lp, smx, smy, lp.xMove, lp.yMove); 607 lp.closePath(); 608 dst.append(lp); 609 sp = null; 610 } else { 611 closeDashedShape(); 612 } 613 614 isClosed = true; 615 break; 616 } 617 p.next(); 618 } 619 620 if (!isClosed) { 621 closeDashedShape(); 622 } 623 624 } 625 626 /** 627 * Closes dashed shape path. 628 */ 629 void closeDashedShape() { 630 // Add head segment 631 if (sp != null) { 632 addCap(sp, fmx, fmy, sp.xMove, sp.yMove); 633 sp.closePath(); 634 dst.append(sp); 635 } 636 if (lp.typeSize > 0) { 637 // Close current segment 638 if (!dasher.isClosed()) { 639 addCap(lp, scx, scy, rp.xLast, rp.yLast); 640 lp.combine(rp); 641 addCap(lp, smx, smy, lp.xMove, lp.yMove); 642 lp.closePath(); 643 } 644 dst.append(lp); 645 } 646 } 647 648 /** 649 * Adds cap to the work path. 650 * 651 * @param p 652 * the BufferedPath object of work path. 653 * @param x0 654 * the x coordinate of the source path. 655 * @param y0 656 * the y coordinate on the source path. 657 * @param x2 658 * the x coordinate of the next point on the work path. 659 * @param y2 660 * the y coordinate of the next point on the work path. 661 */ 662 void addCap(BufferedPath p, double x0, double y0, double x2, double y2) { 663 double x1 = p.xLast; 664 double y1 = p.yLast; 665 double x10 = x1 - x0; 666 double y10 = y1 - y0; 667 double x20 = x2 - x0; 668 double y20 = y2 - y0; 669 670 switch (cap) { 671 case CAP_BUTT: 672 p.lineTo(x2, y2); 673 break; 674 case CAP_ROUND: 675 double mx = x10 * CUBIC_ARC; 676 double my = y10 * CUBIC_ARC; 677 678 double x3 = x0 + y10; 679 double y3 = y0 - x10; 680 681 x10 *= CUBIC_ARC; 682 y10 *= CUBIC_ARC; 683 x20 *= CUBIC_ARC; 684 y20 *= CUBIC_ARC; 685 686 p.cubicTo(x1 + y10, y1 - x10, x3 + mx, y3 + my, x3, y3); 687 p.cubicTo(x3 - mx, y3 - my, x2 - y20, y2 + x20, x2, y2); 688 break; 689 case CAP_SQUARE: 690 p.lineTo(x1 + y10, y1 - x10); 691 p.lineTo(x2 - y20, y2 + x20); 692 p.lineTo(x2, y2); 693 break; 694 } 695 } 696 697 /** 698 * Adds bevel and miter join to the work path. 699 * 700 * @param p 701 * the BufferedPath object of work path. 702 * @param x0 703 * the x coordinate of the source path. 704 * @param y0 705 * the y coordinate on the source path. 706 * @param x2 707 * the x coordinate of the next point on the work path. 708 * @param y2 709 * the y coordinate of the next point on the work path. 710 * @param isLeft 711 * the orientation of work path, true if work path lies to the 712 * left from source path, false otherwise. 713 */ 714 void addJoin(BufferedPath p, double x0, double y0, double x2, double y2, boolean isLeft) { 715 double x1 = p.xLast; 716 double y1 = p.yLast; 717 double x10 = x1 - x0; 718 double y10 = y1 - y0; 719 double x20 = x2 - x0; 720 double y20 = y2 - y0; 721 double sin0 = x10 * y20 - y10 * x20; 722 723 // Small corner 724 if (-cornerDelta < sin0 && sin0 < cornerDelta) { 725 double cos0 = x10 * x20 + y10 * y20; 726 if (cos0 > 0.0) { 727 // if zero corner do nothing 728 if (-zeroDelta > sin0 || sin0 > zeroDelta) { 729 double x3 = x0 + w2 * w2 * (y20 - y10) / sin0; 730 double y3 = y0 + w2 * w2 * (x10 - x20) / sin0; 731 p.setLast(x3, y3); 732 } 733 return; 734 } 735 // Zero corner 736 if (-zeroDelta < sin0 && sin0 < zeroDelta) { 737 p.lineTo(x2, y2); 738 } 739 return; 740 } 741 742 if (isLeft ^ (sin0 < 0.0)) { 743 // Twisted corner 744 p.lineTo(x0, y0); 745 p.lineTo(x2, y2); 746 } else { 747 switch (join) { 748 case JOIN_BEVEL: 749 p.lineTo(x2, y2); 750 break; 751 case JOIN_MITER: 752 double s1 = x1 * x10 + y1 * y10; 753 double s2 = x2 * x20 + y2 * y20; 754 double x3 = (s1 * y20 - s2 * y10) / sin0; 755 double y3 = (s2 * x10 - s1 * x20) / sin0; 756 double x30 = x3 - x0; 757 double y30 = y3 - y0; 758 double miterLength = Math.sqrt(x30 * x30 + y30 * y30); 759 if (miterLength < miterLimit * w2) { 760 p.lineTo(x3, y3); 761 } 762 p.lineTo(x2, y2); 763 break; 764 case JOIN_ROUND: 765 addRoundJoin(p, x0, y0, x2, y2, isLeft); 766 break; 767 } 768 } 769 } 770 771 /** 772 * Adds round join to the work path. 773 * 774 * @param p 775 * the BufferedPath object of work path. 776 * @param x0 777 * the x coordinate of the source path. 778 * @param y0 779 * the y coordinate on the source path. 780 * @param x2 781 * the x coordinate of the next point on the work path. 782 * @param y2 783 * the y coordinate of the next point on the work path. 784 * @param isLeft 785 * the orientation of work path, true if work path lies to the 786 * left from source path, false otherwise. 787 */ 788 void addRoundJoin(BufferedPath p, double x0, double y0, double x2, double y2, boolean isLeft) { 789 double x1 = p.xLast; 790 double y1 = p.yLast; 791 double x10 = x1 - x0; 792 double y10 = y1 - y0; 793 double x20 = x2 - x0; 794 double y20 = y2 - y0; 795 796 double x30 = x10 + x20; 797 double y30 = y10 + y20; 798 799 double l30 = Math.sqrt(x30 * x30 + y30 * y30); 800 801 if (l30 < 1E-5) { 802 p.lineTo(x2, y2); 803 return; 804 } 805 806 double w = w2 / l30; 807 808 x30 *= w; 809 y30 *= w; 810 811 double x3 = x0 + x30; 812 double y3 = y0 + y30; 813 814 double cos = x10 * x20 + y10 * y20; 815 double a = Math.acos(cos / (w2 * w2)); 816 if (cos >= 0.0) { 817 double k = 4.0 / 3.0 * Math.tan(a / 4.0); 818 if (isLeft) { 819 k = -k; 820 } 821 822 x10 *= k; 823 y10 *= k; 824 x20 *= k; 825 y20 *= k; 826 827 p.cubicTo(x1 - y10, y1 + x10, x2 + y20, y2 - x20, x2, y2); 828 } else { 829 double k = 4.0 / 3.0 * Math.tan(a / 8.0); 830 if (isLeft) { 831 k = -k; 832 } 833 834 x10 *= k; 835 y10 *= k; 836 x20 *= k; 837 y20 *= k; 838 x30 *= k; 839 y30 *= k; 840 841 p.cubicTo(x1 - y10, y1 + x10, x3 + y30, y3 - x30, x3, y3); 842 p.cubicTo(x3 - y30, y3 + x30, x2 + y20, y2 - x20, x2, y2); 843 } 844 845 } 846 847 /** 848 * Adds solid line segment to the work path. 849 * 850 * @param x1 851 * the x coordinate of the start line point. 852 * @param y1 853 * the y coordinate of the start line point. 854 * @param x2 855 * the x coordinate of the end line point. 856 * @param y2 857 * the y coordinate of the end line point. 858 * @param zero 859 * if true it's allowable to add zero length line segment. 860 */ 861 void addLine(double x1, double y1, double x2, double y2, boolean zero) { 862 double dx = x2 - x1; 863 double dy = y2 - y1; 864 865 if (dx == 0.0 && dy == 0.0) { 866 if (!zero) { 867 return; 868 } 869 dx = w2; 870 dy = 0; 871 } else { 872 double w = w2 / Math.sqrt(dx * dx + dy * dy); 873 dx *= w; 874 dy *= w; 875 } 876 877 double lx1 = x1 - dy; 878 double ly1 = y1 + dx; 879 double rx1 = x1 + dy; 880 double ry1 = y1 - dx; 881 882 if (checkMove) { 883 if (isMove) { 884 isMove = false; 885 lp.moveTo(lx1, ly1); 886 rp.moveTo(rx1, ry1); 887 } else { 888 addJoin(lp, x1, y1, lx1, ly1, true); 889 addJoin(rp, x1, y1, rx1, ry1, false); 890 } 891 } 892 893 lp.lineTo(x2 - dy, y2 + dx); 894 rp.lineTo(x2 + dy, y2 - dx); 895 } 896 897 /** 898 * Adds solid quad segment to the work path. 899 * 900 * @param x1 901 * the x coordinate of the first control point. 902 * @param y1 903 * the y coordinate of the first control point. 904 * @param x2 905 * the x coordinate of the second control point. 906 * @param y2 907 * the y coordinate of the second control point. 908 * @param x3 909 * the x coordinate of the third control point. 910 * @param y3 911 * the y coordinate of the third control point. 912 */ 913 void addQuad(double x1, double y1, double x2, double y2, double x3, double y3) { 914 double x21 = x2 - x1; 915 double y21 = y2 - y1; 916 double x23 = x2 - x3; 917 double y23 = y2 - y3; 918 919 double l21 = Math.sqrt(x21 * x21 + y21 * y21); 920 double l23 = Math.sqrt(x23 * x23 + y23 * y23); 921 922 if (l21 == 0.0 && l23 == 0.0) { 923 addLine(x1, y1, x3, y3, false); 924 return; 925 } 926 927 if (l21 == 0.0) { 928 addLine(x2, y2, x3, y3, false); 929 return; 930 } 931 932 if (l23 == 0.0) { 933 addLine(x1, y1, x2, y2, false); 934 return; 935 } 936 937 double w; 938 w = w2 / l21; 939 double mx1 = -y21 * w; 940 double my1 = x21 * w; 941 w = w2 / l23; 942 double mx3 = y23 * w; 943 double my3 = -x23 * w; 944 945 double lx1 = x1 + mx1; 946 double ly1 = y1 + my1; 947 double rx1 = x1 - mx1; 948 double ry1 = y1 - my1; 949 950 if (checkMove) { 951 if (isMove) { 952 isMove = false; 953 lp.moveTo(lx1, ly1); 954 rp.moveTo(rx1, ry1); 955 } else { 956 addJoin(lp, x1, y1, lx1, ly1, true); 957 addJoin(rp, x1, y1, rx1, ry1, false); 958 } 959 } 960 961 if (x21 * y23 - y21 * x23 == 0.0) { 962 // On line curve 963 if (x21 * x23 + y21 * y23 > 0.0) { 964 // Twisted curve 965 if (l21 == l23) { 966 double px = x1 + (x21 + x23) / 4.0; 967 double py = y1 + (y21 + y23) / 4.0; 968 lp.lineTo(px + mx1, py + my1); 969 rp.lineTo(px - mx1, py - my1); 970 lp.lineTo(px - mx1, py - my1); 971 rp.lineTo(px + mx1, py + my1); 972 lp.lineTo(x3 - mx1, y3 - my1); 973 rp.lineTo(x3 + mx1, y3 + my1); 974 } else { 975 double px1, py1; 976 double k = l21 / (l21 + l23); 977 double px = x1 + (x21 + x23) * k * k; 978 double py = y1 + (y21 + y23) * k * k; 979 px1 = (x1 + px) / 2.0; 980 py1 = (y1 + py) / 2.0; 981 lp.quadTo(px1 + mx1, py1 + my1, px + mx1, py + my1); 982 rp.quadTo(px1 - mx1, py1 - my1, px - mx1, py - my1); 983 lp.lineTo(px - mx1, py - my1); 984 rp.lineTo(px + mx1, py + my1); 985 px1 = (x3 + px) / 2.0; 986 py1 = (y3 + py) / 2.0; 987 lp.quadTo(px1 - mx1, py1 - my1, x3 - mx1, y3 - my1); 988 rp.quadTo(px1 + mx1, py1 + my1, x3 + mx1, y3 + my1); 989 } 990 } else { 991 // Simple curve 992 lp.quadTo(x2 + mx1, y2 + my1, x3 + mx3, y3 + my3); 993 rp.quadTo(x2 - mx1, y2 - my1, x3 - mx3, y3 - my3); 994 } 995 } else { 996 addSubQuad(x1, y1, x2, y2, x3, y3, 0); 997 } 998 } 999 1000 /** 1001 * Subdivides solid quad curve to make outline for source quad segment and 1002 * adds it to work path. 1003 * 1004 * @param x1 1005 * the x coordinate of the first control point. 1006 * @param y1 1007 * the y coordinate of the first control point. 1008 * @param x2 1009 * the x coordinate of the second control point. 1010 * @param y2 1011 * the y coordinate of the second control point. 1012 * @param x3 1013 * the x coordinate of the third control point. 1014 * @param y3 1015 * the y coordinate of the third control point. 1016 * @param level 1017 * the maximum level of subdivision deepness. 1018 */ 1019 void addSubQuad(double x1, double y1, double x2, double y2, double x3, double y3, int level) { 1020 double x21 = x2 - x1; 1021 double y21 = y2 - y1; 1022 double x23 = x2 - x3; 1023 double y23 = y2 - y3; 1024 1025 double cos = x21 * x23 + y21 * y23; 1026 double sin = x21 * y23 - y21 * x23; 1027 1028 if (level < MAX_LEVEL && (cos >= 0.0 || (Math.abs(sin / cos) > curveDelta))) { 1029 double c1x = (x2 + x1) / 2.0; 1030 double c1y = (y2 + y1) / 2.0; 1031 double c2x = (x2 + x3) / 2.0; 1032 double c2y = (y2 + y3) / 2.0; 1033 double c3x = (c1x + c2x) / 2.0; 1034 double c3y = (c1y + c2y) / 2.0; 1035 addSubQuad(x1, y1, c1x, c1y, c3x, c3y, level + 1); 1036 addSubQuad(c3x, c3y, c2x, c2y, x3, y3, level + 1); 1037 } else { 1038 double w; 1039 double l21 = Math.sqrt(x21 * x21 + y21 * y21); 1040 double l23 = Math.sqrt(x23 * x23 + y23 * y23); 1041 w = w2 / sin; 1042 double mx2 = (x21 * l23 + x23 * l21) * w; 1043 double my2 = (y21 * l23 + y23 * l21) * w; 1044 w = w2 / l23; 1045 double mx3 = y23 * w; 1046 double my3 = -x23 * w; 1047 lp.quadTo(x2 + mx2, y2 + my2, x3 + mx3, y3 + my3); 1048 rp.quadTo(x2 - mx2, y2 - my2, x3 - mx3, y3 - my3); 1049 } 1050 } 1051 1052 /** 1053 * Adds solid cubic segment to the work path. 1054 * 1055 * @param x1 1056 * the x coordinate of the first control point. 1057 * @param y1 1058 * the y coordinate of the first control point. 1059 * @param x2 1060 * the x coordinate of the second control point. 1061 * @param y2 1062 * the y coordinate of the second control point. 1063 * @param x3 1064 * the x coordinate of the third control point. 1065 * @param y3 1066 * the y coordinate of the third control point. 1067 * @param x4 1068 * the x coordinate of the fours control point. 1069 * @param y4 1070 * the y coordinate of the fours control point. 1071 */ 1072 void addCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, 1073 double y4) { 1074 double x12 = x1 - x2; 1075 double y12 = y1 - y2; 1076 double x23 = x2 - x3; 1077 double y23 = y2 - y3; 1078 double x34 = x3 - x4; 1079 double y34 = y3 - y4; 1080 1081 double l12 = Math.sqrt(x12 * x12 + y12 * y12); 1082 double l23 = Math.sqrt(x23 * x23 + y23 * y23); 1083 double l34 = Math.sqrt(x34 * x34 + y34 * y34); 1084 1085 // All edges are zero 1086 if (l12 == 0.0 && l23 == 0.0 && l34 == 0.0) { 1087 addLine(x1, y1, x4, y4, false); 1088 return; 1089 } 1090 1091 // One zero edge 1092 if (l12 == 0.0 && l23 == 0.0) { 1093 addLine(x3, y3, x4, y4, false); 1094 return; 1095 } 1096 1097 if (l23 == 0.0 && l34 == 0.0) { 1098 addLine(x1, y1, x2, y2, false); 1099 return; 1100 } 1101 1102 if (l12 == 0.0 && l34 == 0.0) { 1103 addLine(x2, y2, x3, y3, false); 1104 return; 1105 } 1106 1107 double w, mx1, my1, mx4, my4; 1108 boolean onLine; 1109 1110 if (l12 == 0.0) { 1111 w = w2 / l23; 1112 mx1 = y23 * w; 1113 my1 = -x23 * w; 1114 w = w2 / l34; 1115 mx4 = y34 * w; 1116 my4 = -x34 * w; 1117 onLine = -x23 * y34 + y23 * x34 == 0.0; // sin3 1118 } else if (l34 == 0.0) { 1119 w = w2 / l12; 1120 mx1 = y12 * w; 1121 my1 = -x12 * w; 1122 w = w2 / l23; 1123 mx4 = y23 * w; 1124 my4 = -x23 * w; 1125 onLine = -x12 * y23 + y12 * x23 == 0.0; // sin2 1126 } else { 1127 w = w2 / l12; 1128 mx1 = y12 * w; 1129 my1 = -x12 * w; 1130 w = w2 / l34; 1131 mx4 = y34 * w; 1132 my4 = -x34 * w; 1133 if (l23 == 0.0) { 1134 onLine = -x12 * y34 + y12 * x34 == 0.0; 1135 } else { 1136 onLine = -x12 * y34 + y12 * x34 == 0.0 && -x12 * y23 + y12 * x23 == 0.0 && // sin2 1137 -x23 * y34 + y23 * x34 == 0.0; // sin3 1138 } 1139 } 1140 1141 double lx1 = x1 + mx1; 1142 double ly1 = y1 + my1; 1143 double rx1 = x1 - mx1; 1144 double ry1 = y1 - my1; 1145 1146 if (checkMove) { 1147 if (isMove) { 1148 isMove = false; 1149 lp.moveTo(lx1, ly1); 1150 rp.moveTo(rx1, ry1); 1151 } else { 1152 addJoin(lp, x1, y1, lx1, ly1, true); 1153 addJoin(rp, x1, y1, rx1, ry1, false); 1154 } 1155 } 1156 1157 if (onLine) { 1158 if ((x1 == x2 && y1 < y2) || x1 < x2) { 1159 l12 = -l12; 1160 } 1161 if ((x2 == x3 && y2 < y3) || x2 < x3) { 1162 l23 = -l23; 1163 } 1164 if ((x3 == x4 && y3 < y4) || x3 < x4) { 1165 l34 = -l34; 1166 } 1167 double d = l23 * l23 - l12 * l34; 1168 double roots[] = new double[3]; 1169 int rc = 0; 1170 if (d == 0.0) { 1171 double t = (l12 - l23) / (l12 + l34 - l23 - l23); 1172 if (0.0 < t && t < 1.0) { 1173 roots[rc++] = t; 1174 } 1175 } else if (d > 0.0) { 1176 d = Math.sqrt(d); 1177 double z = l12 + l34 - l23 - l23; 1178 double t; 1179 t = (l12 - l23 + d) / z; 1180 if (0.0 < t && t < 1.0) { 1181 roots[rc++] = t; 1182 } 1183 t = (l12 - l23 - d) / z; 1184 if (0.0 < t && t < 1.0) { 1185 roots[rc++] = t; 1186 } 1187 } 1188 1189 if (rc > 0) { 1190 // Sort roots 1191 if (rc == 2 && roots[0] > roots[1]) { 1192 double tmp = roots[0]; 1193 roots[0] = roots[1]; 1194 roots[1] = tmp; 1195 } 1196 roots[rc++] = 1.0; 1197 1198 double ax = -x34 - x12 + x23 + x23; 1199 double ay = -y34 - y12 + y23 + y23; 1200 double bx = 3.0 * (-x23 + x12); 1201 double by = 3.0 * (-y23 + y12); 1202 double cx = 3.0 * (-x12); 1203 double cy = 3.0 * (-y12); 1204 double xPrev = x1; 1205 double yPrev = y1; 1206 for (int i = 0; i < rc; i++) { 1207 double t = roots[i]; 1208 double px = t * (t * (t * ax + bx) + cx) + x1; 1209 double py = t * (t * (t * ay + by) + cy) + y1; 1210 double px1 = (xPrev + px) / 2.0; 1211 double py1 = (yPrev + py) / 2.0; 1212 lp.cubicTo(px1 + mx1, py1 + my1, px1 + mx1, py1 + my1, px + mx1, py + my1); 1213 rp.cubicTo(px1 - mx1, py1 - my1, px1 - mx1, py1 - my1, px - mx1, py - my1); 1214 if (i < rc - 1) { 1215 lp.lineTo(px - mx1, py - my1); 1216 rp.lineTo(px + mx1, py + my1); 1217 } 1218 xPrev = px; 1219 yPrev = py; 1220 mx1 = -mx1; 1221 my1 = -my1; 1222 } 1223 } else { 1224 lp.cubicTo(x2 + mx1, y2 + my1, x3 + mx4, y3 + my4, x4 + mx4, y4 + my4); 1225 rp.cubicTo(x2 - mx1, y2 - my1, x3 - mx4, y3 - my4, x4 - mx4, y4 - my4); 1226 } 1227 } else { 1228 addSubCubic(x1, y1, x2, y2, x3, y3, x4, y4, 0); 1229 } 1230 } 1231 1232 /** 1233 * Subdivides solid cubic curve to make outline for source quad segment and 1234 * adds it to work path. 1235 * 1236 * @param x1 1237 * the x coordinate of the first control point. 1238 * @param y1 1239 * the y coordinate of the first control point. 1240 * @param x2 1241 * the x coordinate of the second control point. 1242 * @param y2 1243 * the y coordinate of the second control point. 1244 * @param x3 1245 * the x coordinate of the third control point. 1246 * @param y3 1247 * the y coordinate of the third control point. 1248 * @param x4 1249 * the x coordinate of the fours control point. 1250 * @param y4 1251 * the y coordinate of the fours control point. 1252 * @param level 1253 * the maximum level of subdivision deepness. 1254 */ 1255 void addSubCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, 1256 double y4, int level) { 1257 double x12 = x1 - x2; 1258 double y12 = y1 - y2; 1259 double x23 = x2 - x3; 1260 double y23 = y2 - y3; 1261 double x34 = x3 - x4; 1262 double y34 = y3 - y4; 1263 1264 double cos2 = -x12 * x23 - y12 * y23; 1265 double cos3 = -x23 * x34 - y23 * y34; 1266 double sin2 = -x12 * y23 + y12 * x23; 1267 double sin3 = -x23 * y34 + y23 * x34; 1268 double sin0 = -x12 * y34 + y12 * x34; 1269 double cos0 = -x12 * x34 - y12 * y34; 1270 1271 if (level < MAX_LEVEL 1272 && (sin2 != 0.0 || sin3 != 0.0 || sin0 != 0.0) 1273 && (cos2 >= 0.0 || cos3 >= 0.0 || cos0 >= 0.0 1274 || (Math.abs(sin2 / cos2) > curveDelta) 1275 || (Math.abs(sin3 / cos3) > curveDelta) || (Math.abs(sin0 / cos0) > curveDelta))) { 1276 double cx = (x2 + x3) / 2.0; 1277 double cy = (y2 + y3) / 2.0; 1278 double lx2 = (x2 + x1) / 2.0; 1279 double ly2 = (y2 + y1) / 2.0; 1280 double rx3 = (x3 + x4) / 2.0; 1281 double ry3 = (y3 + y4) / 2.0; 1282 double lx3 = (cx + lx2) / 2.0; 1283 double ly3 = (cy + ly2) / 2.0; 1284 double rx2 = (cx + rx3) / 2.0; 1285 double ry2 = (cy + ry3) / 2.0; 1286 cx = (lx3 + rx2) / 2.0; 1287 cy = (ly3 + ry2) / 2.0; 1288 addSubCubic(x1, y1, lx2, ly2, lx3, ly3, cx, cy, level + 1); 1289 addSubCubic(cx, cy, rx2, ry2, rx3, ry3, x4, y4, level + 1); 1290 } else { 1291 double w, mx1, my1, mx2, my2, mx3, my3, mx4, my4; 1292 double l12 = Math.sqrt(x12 * x12 + y12 * y12); 1293 double l23 = Math.sqrt(x23 * x23 + y23 * y23); 1294 double l34 = Math.sqrt(x34 * x34 + y34 * y34); 1295 1296 if (l12 == 0.0) { 1297 w = w2 / l23; 1298 mx1 = y23 * w; 1299 my1 = -x23 * w; 1300 w = w2 / l34; 1301 mx4 = y34 * w; 1302 my4 = -x34 * w; 1303 } else if (l34 == 0.0) { 1304 w = w2 / l12; 1305 mx1 = y12 * w; 1306 my1 = -x12 * w; 1307 w = w2 / l23; 1308 mx4 = y23 * w; 1309 my4 = -x23 * w; 1310 } else { 1311 // Common case 1312 w = w2 / l12; 1313 mx1 = y12 * w; 1314 my1 = -x12 * w; 1315 w = w2 / l34; 1316 mx4 = y34 * w; 1317 my4 = -x34 * w; 1318 } 1319 1320 if (sin2 == 0.0) { 1321 mx2 = mx1; 1322 my2 = my1; 1323 } else { 1324 w = w2 / sin2; 1325 mx2 = -(x12 * l23 - x23 * l12) * w; 1326 my2 = -(y12 * l23 - y23 * l12) * w; 1327 } 1328 if (sin3 == 0.0) { 1329 mx3 = mx4; 1330 my3 = my4; 1331 } else { 1332 w = w2 / sin3; 1333 mx3 = -(x23 * l34 - x34 * l23) * w; 1334 my3 = -(y23 * l34 - y34 * l23) * w; 1335 } 1336 1337 lp.cubicTo(x2 + mx2, y2 + my2, x3 + mx3, y3 + my3, x4 + mx4, y4 + my4); 1338 rp.cubicTo(x2 - mx2, y2 - my2, x3 - mx3, y3 - my3, x4 - mx4, y4 - my4); 1339 } 1340 } 1341 1342 /** 1343 * Adds dashed line segment to the work path. 1344 * 1345 * @param x1 1346 * the x coordinate of the start line point. 1347 * @param y1 1348 * the y coordinate of the start line point. 1349 * @param x2 1350 * the x coordinate of the end line point. 1351 * @param y2 1352 * the y coordinate of the end line point. 1353 */ 1354 void addDashLine(double x1, double y1, double x2, double y2) { 1355 double x21 = x2 - x1; 1356 double y21 = y2 - y1; 1357 1358 double l21 = Math.sqrt(x21 * x21 + y21 * y21); 1359 1360 if (l21 == 0.0) { 1361 return; 1362 } 1363 1364 double px1, py1; 1365 px1 = py1 = 0.0; 1366 double w = w2 / l21; 1367 double mx = -y21 * w; 1368 double my = x21 * w; 1369 1370 dasher.init(new DashIterator.Line(l21)); 1371 1372 while (!dasher.eof()) { 1373 double t = dasher.getValue(); 1374 scx = x1 + t * x21; 1375 scy = y1 + t * y21; 1376 1377 if (dasher.isOpen()) { 1378 px1 = scx; 1379 py1 = scy; 1380 double lx1 = px1 + mx; 1381 double ly1 = py1 + my; 1382 double rx1 = px1 - mx; 1383 double ry1 = py1 - my; 1384 if (isMove) { 1385 isMove = false; 1386 smx = px1; 1387 smy = py1; 1388 rp.clean(); 1389 lp.moveTo(lx1, ly1); 1390 rp.moveTo(rx1, ry1); 1391 } else { 1392 addJoin(lp, x1, y1, lx1, ly1, true); 1393 addJoin(rp, x1, y1, rx1, ry1, false); 1394 } 1395 } else if (dasher.isContinue()) { 1396 double px2 = scx; 1397 double py2 = scy; 1398 lp.lineTo(px2 + mx, py2 + my); 1399 rp.lineTo(px2 - mx, py2 - my); 1400 if (dasher.close) { 1401 addCap(lp, px2, py2, rp.xLast, rp.yLast); 1402 lp.combine(rp); 1403 if (isFirst) { 1404 isFirst = false; 1405 fmx = smx; 1406 fmy = smy; 1407 sp = lp; 1408 lp = new BufferedPath(); 1409 } else { 1410 addCap(lp, smx, smy, lp.xMove, lp.yMove); 1411 lp.closePath(); 1412 } 1413 isMove = true; 1414 } 1415 } 1416 1417 dasher.next(); 1418 } 1419 } 1420 1421 /** 1422 * Adds dashed quad segment to the work path. 1423 * 1424 * @param x1 1425 * the x coordinate of the first control point. 1426 * @param y1 1427 * the y coordinate of the first control point. 1428 * @param x2 1429 * the x coordinate of the second control point. 1430 * @param y2 1431 * the y coordinate of the second control point. 1432 * @param x3 1433 * the x coordinate of the third control point. 1434 * @param y3 1435 * the y coordinate of the third control point. 1436 */ 1437 void addDashQuad(double x1, double y1, double x2, double y2, double x3, double y3) { 1438 1439 double x21 = x2 - x1; 1440 double y21 = y2 - y1; 1441 double x23 = x2 - x3; 1442 double y23 = y2 - y3; 1443 1444 double l21 = Math.sqrt(x21 * x21 + y21 * y21); 1445 double l23 = Math.sqrt(x23 * x23 + y23 * y23); 1446 1447 if (l21 == 0.0 && l23 == 0.0) { 1448 return; 1449 } 1450 1451 if (l21 == 0.0) { 1452 addDashLine(x2, y2, x3, y3); 1453 return; 1454 } 1455 1456 if (l23 == 0.0) { 1457 addDashLine(x1, y1, x2, y2); 1458 return; 1459 } 1460 1461 double ax = x1 + x3 - x2 - x2; 1462 double ay = y1 + y3 - y2 - y2; 1463 double bx = x2 - x1; 1464 double by = y2 - y1; 1465 double cx = x1; 1466 double cy = y1; 1467 1468 double px1, py1, dx1, dy1; 1469 px1 = py1 = dx1 = dy1 = 0.0; 1470 double prev = 0.0; 1471 1472 dasher.init(new DashIterator.Quad(x1, y1, x2, y2, x3, y3)); 1473 1474 while (!dasher.eof()) { 1475 double t = dasher.getValue(); 1476 double dx = t * ax + bx; 1477 double dy = t * ay + by; 1478 scx = t * (dx + bx) + cx; // t^2 * ax + 2.0 * t * bx + cx 1479 scy = t * (dy + by) + cy; // t^2 * ay + 2.0 * t * by + cy 1480 if (dasher.isOpen()) { 1481 px1 = scx; 1482 py1 = scy; 1483 dx1 = dx; 1484 dy1 = dy; 1485 double w = w2 / Math.sqrt(dx1 * dx1 + dy1 * dy1); 1486 double mx1 = -dy1 * w; 1487 double my1 = dx1 * w; 1488 double lx1 = px1 + mx1; 1489 double ly1 = py1 + my1; 1490 double rx1 = px1 - mx1; 1491 double ry1 = py1 - my1; 1492 if (isMove) { 1493 isMove = false; 1494 smx = px1; 1495 smy = py1; 1496 rp.clean(); 1497 lp.moveTo(lx1, ly1); 1498 rp.moveTo(rx1, ry1); 1499 } else { 1500 addJoin(lp, x1, y1, lx1, ly1, true); 1501 addJoin(rp, x1, y1, rx1, ry1, false); 1502 } 1503 } else if (dasher.isContinue()) { 1504 double px3 = scx; 1505 double py3 = scy; 1506 double sx = x2 - x23 * prev; 1507 double sy = y2 - y23 * prev; 1508 double t2 = (t - prev) / (1 - prev); 1509 double px2 = px1 + (sx - px1) * t2; 1510 double py2 = py1 + (sy - py1) * t2; 1511 1512 addQuad(px1, py1, px2, py2, px3, py3); 1513 if (dasher.isClosed()) { 1514 addCap(lp, px3, py3, rp.xLast, rp.yLast); 1515 lp.combine(rp); 1516 if (isFirst) { 1517 isFirst = false; 1518 fmx = smx; 1519 fmy = smy; 1520 sp = lp; 1521 lp = new BufferedPath(); 1522 } else { 1523 addCap(lp, smx, smy, lp.xMove, lp.yMove); 1524 lp.closePath(); 1525 } 1526 isMove = true; 1527 } 1528 } 1529 1530 prev = t; 1531 dasher.next(); 1532 } 1533 } 1534 1535 /** 1536 * Adds dashed cubic segment to the work path. 1537 * 1538 * @param x1 1539 * the x coordinate of the first control point. 1540 * @param y1 1541 * the y coordinate of the first control point. 1542 * @param x2 1543 * the x coordinate of the second control point. 1544 * @param y2 1545 * the y coordinate of the second control point. 1546 * @param x3 1547 * the x coordinate of the third control point. 1548 * @param y3 1549 * the y coordinate of the third control point. 1550 * @param x4 1551 * the x coordinate of the fours control point. 1552 * @param y4 1553 * the y coordinate of the fours control point. 1554 */ 1555 void addDashCubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, 1556 double y4) { 1557 1558 double x12 = x1 - x2; 1559 double y12 = y1 - y2; 1560 double x23 = x2 - x3; 1561 double y23 = y2 - y3; 1562 double x34 = x3 - x4; 1563 double y34 = y3 - y4; 1564 1565 double l12 = Math.sqrt(x12 * x12 + y12 * y12); 1566 double l23 = Math.sqrt(x23 * x23 + y23 * y23); 1567 double l34 = Math.sqrt(x34 * x34 + y34 * y34); 1568 1569 // All edges are zero 1570 if (l12 == 0.0 && l23 == 0.0 && l34 == 0.0) { 1571 // NOTHING 1572 return; 1573 } 1574 1575 // One zero edge 1576 if (l12 == 0.0 && l23 == 0.0) { 1577 addDashLine(x3, y3, x4, y4); 1578 return; 1579 } 1580 1581 if (l23 == 0.0 && l34 == 0.0) { 1582 addDashLine(x1, y1, x2, y2); 1583 return; 1584 } 1585 1586 if (l12 == 0.0 && l34 == 0.0) { 1587 addDashLine(x2, y2, x3, y3); 1588 return; 1589 } 1590 1591 double ax = x4 - x1 + 3.0 * (x2 - x3); 1592 double ay = y4 - y1 + 3.0 * (y2 - y3); 1593 double bx = 3.0 * (x1 + x3 - x2 - x2); 1594 double by = 3.0 * (y1 + y3 - y2 - y2); 1595 double cx = 3.0 * (x2 - x1); 1596 double cy = 3.0 * (y2 - y1); 1597 double dx = x1; 1598 double dy = y1; 1599 1600 double px1 = 0.0; 1601 double py1 = 0.0; 1602 double prev = 0.0; 1603 1604 dasher.init(new DashIterator.Cubic(x1, y1, x2, y2, x3, y3, x4, y4)); 1605 1606 while (!dasher.eof()) { 1607 1608 double t = dasher.getValue(); 1609 scx = t * (t * (t * ax + bx) + cx) + dx; 1610 scy = t * (t * (t * ay + by) + cy) + dy; 1611 if (dasher.isOpen()) { 1612 px1 = scx; 1613 py1 = scy; 1614 double dx1 = t * (t * (ax + ax + ax) + bx + bx) + cx; 1615 double dy1 = t * (t * (ay + ay + ay) + by + by) + cy; 1616 double w = w2 / Math.sqrt(dx1 * dx1 + dy1 * dy1); 1617 double mx1 = -dy1 * w; 1618 double my1 = dx1 * w; 1619 double lx1 = px1 + mx1; 1620 double ly1 = py1 + my1; 1621 double rx1 = px1 - mx1; 1622 double ry1 = py1 - my1; 1623 if (isMove) { 1624 isMove = false; 1625 smx = px1; 1626 smy = py1; 1627 rp.clean(); 1628 lp.moveTo(lx1, ly1); 1629 rp.moveTo(rx1, ry1); 1630 } else { 1631 addJoin(lp, x1, y1, lx1, ly1, true); 1632 addJoin(rp, x1, y1, rx1, ry1, false); 1633 } 1634 } else if (dasher.isContinue()) { 1635 double sx1 = x2 - x23 * prev; 1636 double sy1 = y2 - y23 * prev; 1637 double sx2 = x3 - x34 * prev; 1638 double sy2 = y3 - y34 * prev; 1639 double sx3 = sx1 + (sx2 - sx1) * prev; 1640 double sy3 = sy1 + (sy2 - sy1) * prev; 1641 double t2 = (t - prev) / (1 - prev); 1642 double sx4 = sx3 + (sx2 - sx3) * t2; 1643 double sy4 = sy3 + (sy2 - sy3) * t2; 1644 1645 double px4 = scx; 1646 double py4 = scy; 1647 double px2 = px1 + (sx3 - px1) * t2; 1648 double py2 = py1 + (sy3 - py1) * t2; 1649 double px3 = px2 + (sx4 - px2) * t2; 1650 double py3 = py2 + (sy4 - py2) * t2; 1651 1652 addCubic(px1, py1, px2, py2, px3, py3, px4, py4); 1653 if (dasher.isClosed()) { 1654 addCap(lp, px4, py4, rp.xLast, rp.yLast); 1655 lp.combine(rp); 1656 if (isFirst) { 1657 isFirst = false; 1658 fmx = smx; 1659 fmy = smy; 1660 sp = lp; 1661 lp = new BufferedPath(); 1662 } else { 1663 addCap(lp, smx, smy, lp.xMove, lp.yMove); 1664 lp.closePath(); 1665 } 1666 isMove = true; 1667 } 1668 } 1669 1670 prev = t; 1671 dasher.next(); 1672 } 1673 } 1674 1675 /** 1676 * Dasher class provides dashing for particular dash style. 1677 */ 1678 class Dasher { 1679 1680 /** 1681 * The pos. 1682 */ 1683 double pos; 1684 1685 /** 1686 * The first. 1687 */ 1688 boolean close, visible, first; 1689 1690 /** 1691 * The dash. 1692 */ 1693 float dash[]; 1694 1695 /** 1696 * The phase. 1697 */ 1698 float phase; 1699 1700 /** 1701 * The index. 1702 */ 1703 int index; 1704 1705 /** 1706 * The iter. 1707 */ 1708 DashIterator iter; 1709 1710 /** 1711 * Instantiates a new dasher. 1712 * 1713 * @param dash 1714 * the dash. 1715 * @param phase 1716 * the phase. 1717 */ 1718 Dasher(float dash[], float phase) { 1719 this.dash = dash; 1720 this.phase = phase; 1721 index = 0; 1722 pos = phase; 1723 visible = true; 1724 while (pos >= dash[index]) { 1725 visible = !visible; 1726 pos -= dash[index]; 1727 index = (index + 1) % dash.length; 1728 } 1729 pos = -pos; 1730 first = visible; 1731 } 1732 1733 /** 1734 * Inits the. 1735 * 1736 * @param iter 1737 * the iter. 1738 */ 1739 void init(DashIterator iter) { 1740 this.iter = iter; 1741 close = true; 1742 } 1743 1744 /** 1745 * Checks if is open. 1746 * 1747 * @return true, if is open. 1748 */ 1749 boolean isOpen() { 1750 return visible && pos < iter.length; 1751 } 1752 1753 /** 1754 * Checks if is continue. 1755 * 1756 * @return true, if is continue. 1757 */ 1758 boolean isContinue() { 1759 return !visible && pos > 0; 1760 } 1761 1762 /** 1763 * Checks if is closed. 1764 * 1765 * @return true, if is closed. 1766 */ 1767 boolean isClosed() { 1768 return close; 1769 } 1770 1771 /** 1772 * Checks if is connected. 1773 * 1774 * @return true, if is connected. 1775 */ 1776 boolean isConnected() { 1777 return first && !close; 1778 } 1779 1780 /** 1781 * Eof. 1782 * 1783 * @return true, if successful. 1784 */ 1785 boolean eof() { 1786 if (!close) { 1787 pos -= iter.length; 1788 return true; 1789 } 1790 if (pos >= iter.length) { 1791 if (visible) { 1792 pos -= iter.length; 1793 return true; 1794 } 1795 close = pos == iter.length; 1796 } 1797 return false; 1798 } 1799 1800 /** 1801 * Next. 1802 */ 1803 void next() { 1804 if (close) { 1805 pos += dash[index]; 1806 index = (index + 1) % dash.length; 1807 } else { 1808 // Go back 1809 index = (index + dash.length - 1) % dash.length; 1810 pos -= dash[index]; 1811 } 1812 visible = !visible; 1813 } 1814 1815 /** 1816 * Gets the value. 1817 * 1818 * @return the value. 1819 */ 1820 double getValue() { 1821 double t = iter.getNext(pos); 1822 return t < 0 ? 0 : (t > 1 ? 1 : t); 1823 } 1824 1825 } 1826 1827 /** 1828 * DashIterator class provides dashing for particular segment type. 1829 */ 1830 static abstract class DashIterator { 1831 1832 /** 1833 * The Constant FLATNESS. 1834 */ 1835 static final double FLATNESS = 1.0; 1836 1837 /** 1838 * The Class Line. 1839 */ 1840 static class Line extends DashIterator { 1841 1842 /** 1843 * Instantiates a new line. 1844 * 1845 * @param len 1846 * the len. 1847 */ 1848 Line(double len) { 1849 length = len; 1850 } 1851 1852 @Override 1853 double getNext(double dashPos) { 1854 return dashPos / length; 1855 } 1856 1857 } 1858 1859 /** 1860 * The Class Quad. 1861 */ 1862 static class Quad extends DashIterator { 1863 1864 /** 1865 * The val size. 1866 */ 1867 int valSize; 1868 1869 /** 1870 * The val pos. 1871 */ 1872 int valPos; 1873 1874 /** 1875 * The cur len. 1876 */ 1877 double curLen; 1878 1879 /** 1880 * The prev len. 1881 */ 1882 double prevLen; 1883 1884 /** 1885 * The last len. 1886 */ 1887 double lastLen; 1888 1889 /** 1890 * The values. 1891 */ 1892 double[] values; 1893 1894 /** 1895 * The step. 1896 */ 1897 double step; 1898 1899 /** 1900 * Instantiates a new quad. 1901 * 1902 * @param x1 1903 * the x1. 1904 * @param y1 1905 * the y1. 1906 * @param x2 1907 * the x2. 1908 * @param y2 1909 * the y2. 1910 * @param x3 1911 * the x3. 1912 * @param y3 1913 * the y3. 1914 */ 1915 Quad(double x1, double y1, double x2, double y2, double x3, double y3) { 1916 1917 double nx = x1 + x3 - x2 - x2; 1918 double ny = y1 + y3 - y2 - y2; 1919 1920 int n = (int)(1 + Math.sqrt(0.75 * (Math.abs(nx) + Math.abs(ny)) * FLATNESS)); 1921 step = 1.0 / n; 1922 1923 double ax = x1 + x3 - x2 - x2; 1924 double ay = y1 + y3 - y2 - y2; 1925 double bx = 2.0 * (x2 - x1); 1926 double by = 2.0 * (y2 - y1); 1927 1928 double dx1 = step * (step * ax + bx); 1929 double dy1 = step * (step * ay + by); 1930 double dx2 = step * (step * ax * 2.0); 1931 double dy2 = step * (step * ay * 2.0); 1932 double vx = x1; 1933 double vy = y1; 1934 1935 valSize = n; 1936 values = new double[valSize]; 1937 double pvx = vx; 1938 double pvy = vy; 1939 length = 0.0; 1940 for (int i = 0; i < n; i++) { 1941 vx += dx1; 1942 vy += dy1; 1943 dx1 += dx2; 1944 dy1 += dy2; 1945 double lx = vx - pvx; 1946 double ly = vy - pvy; 1947 values[i] = Math.sqrt(lx * lx + ly * ly); 1948 length += values[i]; 1949 pvx = vx; 1950 pvy = vy; 1951 } 1952 1953 valPos = 0; 1954 curLen = 0.0; 1955 prevLen = 0.0; 1956 } 1957 1958 @Override 1959 double getNext(double dashPos) { 1960 double t = 2.0; 1961 while (curLen <= dashPos && valPos < valSize) { 1962 prevLen = curLen; 1963 curLen += lastLen = values[valPos++]; 1964 } 1965 if (curLen > dashPos) { 1966 t = (valPos - 1 + (dashPos - prevLen) / lastLen) * step; 1967 } 1968 return t; 1969 } 1970 1971 } 1972 1973 /** 1974 * The Class Cubic. 1975 */ 1976 static class Cubic extends DashIterator { 1977 1978 /** 1979 * The val size. 1980 */ 1981 int valSize; 1982 1983 /** 1984 * The val pos. 1985 */ 1986 int valPos; 1987 1988 /** 1989 * The cur len. 1990 */ 1991 double curLen; 1992 1993 /** 1994 * The prev len. 1995 */ 1996 double prevLen; 1997 1998 /** 1999 * The last len. 2000 */ 2001 double lastLen; 2002 2003 /** 2004 * The values. 2005 */ 2006 double[] values; 2007 2008 /** 2009 * The step. 2010 */ 2011 double step; 2012 2013 /** 2014 * Instantiates a new cubic. 2015 * 2016 * @param x1 2017 * the x1. 2018 * @param y1 2019 * the y1. 2020 * @param x2 2021 * the x2. 2022 * @param y2 2023 * the y2. 2024 * @param x3 2025 * the x3. 2026 * @param y3 2027 * the y3. 2028 * @param x4 2029 * the x4. 2030 * @param y4 2031 * the y4. 2032 */ 2033 Cubic(double x1, double y1, double x2, double y2, double x3, double y3, double x4, 2034 double y4) { 2035 2036 double nx1 = x1 + x3 - x2 - x2; 2037 double ny1 = y1 + y3 - y2 - y2; 2038 double nx2 = x2 + x4 - x3 - x3; 2039 double ny2 = y2 + y4 - y3 - y3; 2040 2041 double max = Math.max(Math.abs(nx1) + Math.abs(ny1), Math.abs(nx2) + Math.abs(ny2)); 2042 int n = (int)(1 + Math.sqrt(0.75 * max) * FLATNESS); 2043 step = 1.0 / n; 2044 2045 double ax = x4 - x1 + 3.0 * (x2 - x3); 2046 double ay = y4 - y1 + 3.0 * (y2 - y3); 2047 double bx = 3.0 * (x1 + x3 - x2 - x2); 2048 double by = 3.0 * (y1 + y3 - y2 - y2); 2049 double cx = 3.0 * (x2 - x1); 2050 double cy = 3.0 * (y2 - y1); 2051 2052 double dx1 = step * (step * (step * ax + bx) + cx); 2053 double dy1 = step * (step * (step * ay + by) + cy); 2054 double dx2 = step * (step * (step * ax * 6.0 + bx * 2.0)); 2055 double dy2 = step * (step * (step * ay * 6.0 + by * 2.0)); 2056 double dx3 = step * (step * (step * ax * 6.0)); 2057 double dy3 = step * (step * (step * ay * 6.0)); 2058 double vx = x1; 2059 double vy = y1; 2060 2061 valSize = n; 2062 values = new double[valSize]; 2063 double pvx = vx; 2064 double pvy = vy; 2065 length = 0.0; 2066 for (int i = 0; i < n; i++) { 2067 vx += dx1; 2068 vy += dy1; 2069 dx1 += dx2; 2070 dy1 += dy2; 2071 dx2 += dx3; 2072 dy2 += dy3; 2073 double lx = vx - pvx; 2074 double ly = vy - pvy; 2075 values[i] = Math.sqrt(lx * lx + ly * ly); 2076 length += values[i]; 2077 pvx = vx; 2078 pvy = vy; 2079 } 2080 2081 valPos = 0; 2082 curLen = 0.0; 2083 prevLen = 0.0; 2084 } 2085 2086 @Override 2087 double getNext(double dashPos) { 2088 double t = 2.0; 2089 while (curLen <= dashPos && valPos < valSize) { 2090 prevLen = curLen; 2091 curLen += lastLen = values[valPos++]; 2092 } 2093 if (curLen > dashPos) { 2094 t = (valPos - 1 + (dashPos - prevLen) / lastLen) * step; 2095 } 2096 return t; 2097 } 2098 2099 } 2100 2101 /** 2102 * The length. 2103 */ 2104 double length; 2105 2106 /** 2107 * Gets the next. 2108 * 2109 * @param dashPos 2110 * the dash pos. 2111 * @return the next. 2112 */ 2113 abstract double getNext(double dashPos); 2114 2115 } 2116 2117 /** 2118 * BufferedPath class provides work path storing and processing. 2119 */ 2120 static class BufferedPath { 2121 2122 /** 2123 * The Constant bufCapacity. 2124 */ 2125 private static final int bufCapacity = 10; 2126 2127 /** 2128 * The point shift. 2129 */ 2130 static int pointShift[] = { 2131 2, // MOVETO 2132 2, // LINETO 2133 4, // QUADTO 2134 6, // CUBICTO 2135 0 2136 }; // CLOSE 2137 2138 /** 2139 * The types. 2140 */ 2141 byte[] types; 2142 2143 /** 2144 * The points. 2145 */ 2146 float[] points; 2147 2148 /** 2149 * The type size. 2150 */ 2151 int typeSize; 2152 2153 /** 2154 * The point size. 2155 */ 2156 int pointSize; 2157 2158 /** 2159 * The x last. 2160 */ 2161 float xLast; 2162 2163 /** 2164 * The y last. 2165 */ 2166 float yLast; 2167 2168 /** 2169 * The x move. 2170 */ 2171 float xMove; 2172 2173 /** 2174 * The y move. 2175 */ 2176 float yMove; 2177 2178 /** 2179 * Instantiates a new buffered path. 2180 */ 2181 public BufferedPath() { 2182 types = new byte[bufCapacity]; 2183 points = new float[bufCapacity * 2]; 2184 } 2185 2186 /** 2187 * Check buf. 2188 * 2189 * @param typeCount 2190 * the type count. 2191 * @param pointCount 2192 * the point count. 2193 */ 2194 void checkBuf(int typeCount, int pointCount) { 2195 if (typeSize + typeCount > types.length) { 2196 byte tmp[] = new byte[typeSize + Math.max(bufCapacity, typeCount)]; 2197 System.arraycopy(types, 0, tmp, 0, typeSize); 2198 types = tmp; 2199 } 2200 if (pointSize + pointCount > points.length) { 2201 float tmp[] = new float[pointSize + Math.max(bufCapacity * 2, pointCount)]; 2202 System.arraycopy(points, 0, tmp, 0, pointSize); 2203 points = tmp; 2204 } 2205 } 2206 2207 /** 2208 * Checks if is empty. 2209 * 2210 * @return true, if is empty. 2211 */ 2212 boolean isEmpty() { 2213 return typeSize == 0; 2214 } 2215 2216 /** 2217 * Clean. 2218 */ 2219 void clean() { 2220 typeSize = 0; 2221 pointSize = 0; 2222 } 2223 2224 /** 2225 * Move to. 2226 * 2227 * @param x 2228 * the x. 2229 * @param y 2230 * the y. 2231 */ 2232 void moveTo(double x, double y) { 2233 checkBuf(1, 2); 2234 types[typeSize++] = PathIterator.SEG_MOVETO; 2235 points[pointSize++] = xMove = (float)x; 2236 points[pointSize++] = yMove = (float)y; 2237 } 2238 2239 /** 2240 * Line to. 2241 * 2242 * @param x 2243 * the x. 2244 * @param y 2245 * the y. 2246 */ 2247 void lineTo(double x, double y) { 2248 checkBuf(1, 2); 2249 types[typeSize++] = PathIterator.SEG_LINETO; 2250 points[pointSize++] = xLast = (float)x; 2251 points[pointSize++] = yLast = (float)y; 2252 } 2253 2254 /** 2255 * Quad to. 2256 * 2257 * @param x1 2258 * the x1. 2259 * @param y1 2260 * the y1. 2261 * @param x2 2262 * the x2. 2263 * @param y2 2264 * the y2. 2265 */ 2266 void quadTo(double x1, double y1, double x2, double y2) { 2267 checkBuf(1, 4); 2268 types[typeSize++] = PathIterator.SEG_QUADTO; 2269 points[pointSize++] = (float)x1; 2270 points[pointSize++] = (float)y1; 2271 points[pointSize++] = xLast = (float)x2; 2272 points[pointSize++] = yLast = (float)y2; 2273 } 2274 2275 /** 2276 * Cubic to. 2277 * 2278 * @param x1 2279 * the x1. 2280 * @param y1 2281 * the y1. 2282 * @param x2 2283 * the x2. 2284 * @param y2 2285 * the y2. 2286 * @param x3 2287 * the x3. 2288 * @param y3 2289 * the y3. 2290 */ 2291 void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { 2292 checkBuf(1, 6); 2293 types[typeSize++] = PathIterator.SEG_CUBICTO; 2294 points[pointSize++] = (float)x1; 2295 points[pointSize++] = (float)y1; 2296 points[pointSize++] = (float)x2; 2297 points[pointSize++] = (float)y2; 2298 points[pointSize++] = xLast = (float)x3; 2299 points[pointSize++] = yLast = (float)y3; 2300 } 2301 2302 /** 2303 * Close path. 2304 */ 2305 void closePath() { 2306 checkBuf(1, 0); 2307 types[typeSize++] = PathIterator.SEG_CLOSE; 2308 } 2309 2310 /** 2311 * Sets the last. 2312 * 2313 * @param x 2314 * the x. 2315 * @param y 2316 * the y. 2317 */ 2318 void setLast(double x, double y) { 2319 points[pointSize - 2] = xLast = (float)x; 2320 points[pointSize - 1] = yLast = (float)y; 2321 } 2322 2323 /** 2324 * Append. 2325 * 2326 * @param p 2327 * the p. 2328 */ 2329 void append(BufferedPath p) { 2330 checkBuf(p.typeSize, p.pointSize); 2331 System.arraycopy(p.points, 0, points, pointSize, p.pointSize); 2332 System.arraycopy(p.types, 0, types, typeSize, p.typeSize); 2333 pointSize += p.pointSize; 2334 typeSize += p.typeSize; 2335 xLast = points[pointSize - 2]; 2336 yLast = points[pointSize - 1]; 2337 } 2338 2339 /** 2340 * Append reverse. 2341 * 2342 * @param p 2343 * the p. 2344 */ 2345 void appendReverse(BufferedPath p) { 2346 checkBuf(p.typeSize, p.pointSize); 2347 // Skip last point, beacause it's the first point of the second path 2348 for (int i = p.pointSize - 2; i >= 0; i -= 2) { 2349 points[pointSize++] = p.points[i + 0]; 2350 points[pointSize++] = p.points[i + 1]; 2351 } 2352 // Skip first type, beacuse it's always MOVETO 2353 int closeIndex = 0; 2354 for (int i = p.typeSize - 1; i >= 0; i--) { 2355 byte type = p.types[i]; 2356 if (type == PathIterator.SEG_MOVETO) { 2357 types[closeIndex] = PathIterator.SEG_MOVETO; 2358 types[typeSize++] = PathIterator.SEG_CLOSE; 2359 } else { 2360 if (type == PathIterator.SEG_CLOSE) { 2361 closeIndex = typeSize; 2362 } 2363 types[typeSize++] = type; 2364 } 2365 } 2366 xLast = points[pointSize - 2]; 2367 yLast = points[pointSize - 1]; 2368 } 2369 2370 /** 2371 * Join. 2372 * 2373 * @param p 2374 * the p. 2375 */ 2376 void join(BufferedPath p) { 2377 // Skip MOVETO 2378 checkBuf(p.typeSize - 1, p.pointSize - 2); 2379 System.arraycopy(p.points, 2, points, pointSize, p.pointSize - 2); 2380 System.arraycopy(p.types, 1, types, typeSize, p.typeSize - 1); 2381 pointSize += p.pointSize - 2; 2382 typeSize += p.typeSize - 1; 2383 xLast = points[pointSize - 2]; 2384 yLast = points[pointSize - 1]; 2385 } 2386 2387 /** 2388 * Combine. 2389 * 2390 * @param p 2391 * the p. 2392 */ 2393 void combine(BufferedPath p) { 2394 checkBuf(p.typeSize - 1, p.pointSize - 2); 2395 // Skip last point, beacause it's the first point of the second path 2396 for (int i = p.pointSize - 4; i >= 0; i -= 2) { 2397 points[pointSize++] = p.points[i + 0]; 2398 points[pointSize++] = p.points[i + 1]; 2399 } 2400 // Skip first type, beacuse it's always MOVETO 2401 for (int i = p.typeSize - 1; i >= 1; i--) { 2402 types[typeSize++] = p.types[i]; 2403 } 2404 xLast = points[pointSize - 2]; 2405 yLast = points[pointSize - 1]; 2406 } 2407 2408 /** 2409 * Creates the general path. 2410 * 2411 * @return the general path. 2412 */ 2413 GeneralPath createGeneralPath() { 2414 GeneralPath p = new GeneralPath(); 2415 int j = 0; 2416 for (int i = 0; i < typeSize; i++) { 2417 int type = types[i]; 2418 switch (type) { 2419 case PathIterator.SEG_MOVETO: 2420 p.moveTo(points[j], points[j + 1]); 2421 break; 2422 case PathIterator.SEG_LINETO: 2423 p.lineTo(points[j], points[j + 1]); 2424 break; 2425 case PathIterator.SEG_QUADTO: 2426 p.quadTo(points[j], points[j + 1], points[j + 2], points[j + 3]); 2427 break; 2428 case PathIterator.SEG_CUBICTO: 2429 p.curveTo(points[j], points[j + 1], points[j + 2], points[j + 3], 2430 points[j + 4], points[j + 5]); 2431 break; 2432 case PathIterator.SEG_CLOSE: 2433 p.closePath(); 2434 break; 2435 } 2436 j += pointShift[type]; 2437 } 2438 return p; 2439 } 2440 2441 } 2442 2443 } 2444