1 26 27 package org.nightlabs.editor2d.j2d; 28 29 import java.awt.Shape ; 30 import java.awt.geom.AffineTransform ; 31 import java.awt.geom.FlatteningPathIterator ; 32 import java.awt.geom.IllegalPathStateException ; 33 import java.awt.geom.PathIterator ; 34 import java.awt.geom.Point2D ; 35 import java.awt.geom.Rectangle2D ; 36 import java.io.Serializable ; 37 import java.util.ArrayList ; 38 import java.util.Iterator ; 39 import java.util.LinkedList ; 40 import java.util.List ; 41 42 import sun.awt.geom.Crossings; 43 import sun.awt.geom.Curve; 44 45 46 68 public class GeneralShape 69 implements Shape , Cloneable , Serializable 70 { 71 75 public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; 76 77 81 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; 82 83 private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO; 86 private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO; 87 private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO; 88 private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO; 89 private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE; 90 91 protected byte[] pointTypes; 92 protected float[] pointCoords; 93 int numTypes; 94 int numCoords; 95 int windingRule; 96 97 public byte[] getPointTypes() { 99 return pointTypes; 100 } 101 public void setPointTypes(byte[] _pointTypes) { 102 pointTypes = _pointTypes; 103 bounds = null; 104 } 105 106 public int getNumCoords() { 107 return numCoords; 108 } 109 public void setNumCoords(int numCoords) { 110 this.numCoords = numCoords; 111 bounds = null; 112 } 113 114 public int getNumTypes() { 115 return numTypes; 116 } 117 public void setNumTypes(int numTypes) { 118 this.numTypes = numTypes; 119 bounds = null; 120 } 121 122 public int getSize() { 123 return numTypes; 124 } 125 126 public float[] getPointCoords() { 127 return pointCoords; 128 } 129 public void setPointCoords(float[] pointCoords) { 130 this.pointCoords = pointCoords; 131 bounds = null; 132 } 133 135 static final int INIT_SIZE = 20; 136 static final int EXPAND_MAX = 500; 137 138 145 public GeneralShape() { 146 this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE); 147 } 148 149 157 public GeneralShape(int rule) { 158 this(rule, INIT_SIZE, INIT_SIZE); 159 } 160 161 173 public GeneralShape(int rule, int initialCapacity) { 174 this(rule, initialCapacity, initialCapacity); 175 } 176 177 192 GeneralShape(int rule, int initialTypes, int initialCoords) { 193 setWindingRule(rule); 194 pointTypes = new byte[initialTypes]; 195 pointCoords = new float[initialCoords * 2]; 196 } 197 198 205 public GeneralShape(Shape s) { 206 this(WIND_NON_ZERO, INIT_SIZE, INIT_SIZE); 207 PathIterator pi = s.getPathIterator(null); 208 setWindingRule(pi.getWindingRule()); 209 append(pi, false); 210 } 211 212 private void needRoom(int newTypes, int newCoords, boolean needMove) { 213 if (needMove && numTypes == 0) { 214 throw new IllegalPathStateException ("missing initial moveto "+ 215 "in path definition"); 216 } 217 int size = pointCoords.length; 218 if (numCoords + newCoords > size) { 219 int grow = size; 220 if (grow > EXPAND_MAX * 2) { 221 grow = EXPAND_MAX * 2; 222 } 223 if (grow < newCoords) { 224 grow = newCoords; 225 } 226 float[] arr = new float[size + grow]; 227 System.arraycopy(pointCoords, 0, arr, 0, numCoords); 228 pointCoords = arr; 229 } 230 size = pointTypes.length; 231 if (numTypes + newTypes > size) { 232 int grow = size; 233 if (grow > EXPAND_MAX) { 234 grow = EXPAND_MAX; 235 } 236 if (grow < newTypes) { 237 grow = newTypes; 238 } 239 byte[] arr = new byte[size + grow]; 240 System.arraycopy(pointTypes, 0, arr, 0, numTypes); 241 pointTypes = arr; 242 } 243 } 244 245 250 public synchronized void moveTo(float x, float y) { 251 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { 252 pointCoords[numCoords - 2] = x; 253 pointCoords[numCoords - 1] = y; 254 } else { 255 needRoom(1, 2, false); 256 pointTypes[numTypes++] = SEG_MOVETO; 257 pointCoords[numCoords++] = x; 258 pointCoords[numCoords++] = y; 259 } 260 pathSegments = null; 261 } 262 263 268 public synchronized void lineTo(float x, float y) { 269 needRoom(1, 2, true); 270 pointTypes[numTypes++] = SEG_LINETO; 271 pointCoords[numCoords++] = x; 272 pointCoords[numCoords++] = y; 273 pathSegments = null; 274 bounds = null; 275 } 276 277 287 public synchronized void quadTo(float x1, float y1, float x2, float y2) { 288 needRoom(1, 4, true); 289 pointTypes[numTypes++] = SEG_QUADTO; 290 pointCoords[numCoords++] = x1; 291 pointCoords[numCoords++] = y1; 292 pointCoords[numCoords++] = x2; 293 pointCoords[numCoords++] = y2; 294 pathSegments = null; 295 bounds = null; 296 } 297 298 310 public synchronized void curveTo(float x1, float y1, 311 float x2, float y2, 312 float x3, float y3) { 313 needRoom(1, 6, true); 314 pointTypes[numTypes++] = SEG_CUBICTO; 315 pointCoords[numCoords++] = x1; 316 pointCoords[numCoords++] = y1; 317 pointCoords[numCoords++] = x2; 318 pointCoords[numCoords++] = y2; 319 pointCoords[numCoords++] = x3; 320 pointCoords[numCoords++] = y3; 321 pathSegments = null; 322 bounds = null; 323 } 324 325 330 public synchronized void closePath() 331 { 332 if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) { 333 needRoom(1, 0, true); 334 pointTypes[numTypes++] = SEG_CLOSE; 335 } 336 bounds = null; 337 } 338 339 359 public void append(Shape s, boolean connect) { 360 PathIterator pi = s.getPathIterator(null); 361 append(pi,connect); 362 } 363 364 385 public void append(PathIterator pi, boolean connect) { 386 float coords[] = new float[6]; 387 while (!pi.isDone()) { 388 switch (pi.currentSegment(coords)) { 389 case SEG_MOVETO: 390 if (!connect || numTypes < 1 || numCoords < 2) { 391 moveTo(coords[0], coords[1]); 392 break; 393 } 394 if (pointTypes[numTypes - 1] != SEG_CLOSE && 395 pointCoords[numCoords - 2] == coords[0] && 396 pointCoords[numCoords - 1] == coords[1]) 397 { 398 break; 400 } 401 case SEG_LINETO: 403 lineTo(coords[0], coords[1]); 404 break; 405 case SEG_QUADTO: 406 quadTo(coords[0], coords[1], 407 coords[2], coords[3]); 408 break; 409 case SEG_CUBICTO: 410 curveTo(coords[0], coords[1], 411 coords[2], coords[3], 412 coords[4], coords[5]); 413 break; 414 case SEG_CLOSE: 415 closePath(); 416 break; 417 } 418 pi.next(); 419 connect = false; 420 } 421 pathSegments = null; 422 bounds = null; 423 } 424 425 432 public synchronized int getWindingRule() { 433 return windingRule; 434 } 435 436 448 public void setWindingRule(int rule) { 449 if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { 450 throw new IllegalArgumentException ("winding rule must be "+ 451 "WIND_EVEN_ODD or "+ 452 "WIND_NON_ZERO"); 453 } 454 windingRule = rule; 455 } 456 457 464 public synchronized Point2D getCurrentPoint() { 465 if (numTypes < 1 || numCoords < 2) { 466 return null; 467 } 468 int index = numCoords; 469 if (pointTypes[numTypes - 1] == SEG_CLOSE) { 470 loop: 471 for (int i = numTypes - 2; i > 0; i--) { 472 switch (pointTypes[i]) { 473 case SEG_MOVETO: 474 break loop; 475 case SEG_LINETO: 476 index -= 2; 477 break; 478 case SEG_QUADTO: 479 index -= 4; 480 break; 481 case SEG_CUBICTO: 482 index -= 6; 483 break; 484 case SEG_CLOSE: 485 break; 486 } 487 } 488 } 489 return new Point2D.Float (pointCoords[index - 2], 490 pointCoords[index - 1]); 491 } 492 493 498 public synchronized void reset() { 499 numTypes = numCoords = 0; 500 bounds = null; 501 } 502 503 protected List transformListeners; 504 public void addTransformListener(TransformListener listener) 505 { 506 if (transformListeners == null) 507 transformListeners = new ArrayList (); 508 509 transformListeners.add(listener); 510 } 511 public void removeTransformListener(TransformListener listener) 512 { 513 if (transformListeners == null) 514 transformListeners = new ArrayList (); 515 516 transformListeners.remove(listener); 517 } 518 519 protected void fireTransformChanged(AffineTransform _at) 531 { 532 if (transformListeners == null) 533 return; 534 535 for (Iterator it = new LinkedList (transformListeners).iterator(); it.hasNext(); ) 536 { 537 TransformListener listener = (TransformListener) it.next(); 538 listener.transformChanged(_at); 539 } 540 } 541 542 public boolean hasTransformListener() 543 { 544 if (transformListeners != null) 545 return true; 546 else 547 return false; 548 } 549 550 557 public void transform(AffineTransform at) 558 { 559 at.transform(pointCoords, 0, pointCoords, 0, numCoords / 2); 560 pathSegments = null; 561 bounds = null; 562 fireTransformChanged(at); 563 } 564 565 572 public synchronized Shape createTransformedShape(AffineTransform at) { 573 GeneralShape gp = (GeneralShape) clone(); 574 if (at != null) { 575 gp.transform(at); 576 } 577 return gp; 578 } 579 580 585 public java.awt.Rectangle getBounds() { 586 return getBounds2D().getBounds(); 587 } 588 589 protected Rectangle2D bounds; 590 591 596 public synchronized Rectangle2D getBounds2D() 597 { 598 if (bounds == null) 599 { 600 float x1, y1, x2, y2; 601 int i = numCoords; 602 if (i > 0) { 603 y1 = y2 = pointCoords[--i]; 604 x1 = x2 = pointCoords[--i]; 605 while (i > 0) { 606 float y = pointCoords[--i]; 607 float x = pointCoords[--i]; 608 if (x < x1) x1 = x; 609 if (y < y1) y1 = y; 610 if (x > x2) x2 = x; 611 if (y > y2) y2 = y; 612 } 613 } else { 614 x1 = y1 = x2 = y2 = 0.0f; 615 } 616 return new Rectangle2D.Float (x1, y1, x2 - x1, y2 - y1); 617 } 618 else 619 return bounds; 620 } 621 622 629 public boolean contains(double x, double y) { 630 if (numTypes < 2) { 631 return false; 632 } 633 int cross = Curve.crossingsForPath(getPathIterator(null), x, y); 634 if (windingRule == WIND_NON_ZERO) { 635 return (cross != 0); 636 } else { 637 return ((cross & 1) != 0); 638 } 639 } 640 641 648 public boolean contains(Point2D p) { 649 return contains(p.getX(), p.getY()); 650 } 651 652 661 public boolean contains(double x, double y, double w, double h) { 662 Crossings c = Crossings.findCrossings(getPathIterator(null), 663 x, y, x+w, y+h); 664 return (c != null && c.covers(y, y+h)); 665 } 666 667 674 public boolean contains(Rectangle2D r) { 675 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 676 } 677 678 688 public boolean intersects(double x, double y, double w, double h) { 689 Crossings c = Crossings.findCrossings(getPathIterator(null), 690 x, y, x+w, y+h); 691 return (c == null || !c.isEmpty()); 692 } 693 694 702 public boolean intersects(Rectangle2D r) { 703 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 704 } 705 706 720 public PathIterator getPathIterator(AffineTransform at) { 721 return new GeneralShapeIterator(this, at); 722 } 723 724 740 public PathIterator getPathIterator(AffineTransform at, double flatness) { 741 return new FlatteningPathIterator (getPathIterator(at), flatness); 742 } 743 744 752 public Object clone() 753 { 754 int pointTypesLength = this.pointTypes.length; 757 byte[] copyPointTypes = new byte[pointTypesLength]; 758 System.arraycopy(pointTypes, 0, copyPointTypes, 0, pointTypesLength); 762 int pointCoordsLength = this.pointCoords.length; 763 float[] copyPointCoords = new float[pointCoordsLength]; 764 System.arraycopy(pointCoords, 0, copyPointCoords, 0, pointCoordsLength); 768 GeneralShape copy = new GeneralShape(); 770 copy.windingRule = this.windingRule; 771 copy.numTypes = this.numTypes; 772 copy.pointTypes = copyPointTypes; 773 copy.numCoords = this.numCoords; 774 copy.pointCoords = copyPointCoords; 775 return copy; 776 } 782 783 GeneralShape(int windingRule, 784 byte[] pointTypes, 785 int numTypes, 786 float[] pointCoords, 787 int numCoords) 788 { 789 this.windingRule = windingRule; 791 this.pointTypes = pointTypes; 792 this.numTypes = numTypes; 793 this.pointCoords = pointCoords; 794 this.numCoords = numCoords; 795 } 796 797 public boolean equals(Object o) 798 { 799 if (o instanceof GeneralShape) 800 { 801 GeneralShape gs = (GeneralShape) o; 802 803 if (this.windingRule != gs.getWindingRule()) 805 return false; 806 807 if (this.numCoords != gs.getNumCoords()) 809 return false; 810 811 if (this.numTypes != gs.getNumTypes()) 813 return false; 814 815 if (this.pointCoords.length != gs.getPointCoords().length) { 817 return false; 818 } 819 else { 820 float[] gsPointCoords = gs.getPointCoords(); 821 for (int i=0; i<pointCoords.length; i++) { 822 float f = pointCoords[i]; 823 if (f != gsPointCoords[i]) 824 return false; 825 } 826 } 827 828 if (this.pointTypes.length != gs.pointTypes.length) { 830 return false; 831 } 832 else { 833 byte[] gsPointTypes = gs.getPointTypes(); 834 for (int i=0; i<pointTypes.length; i++) { 835 byte b = pointTypes[i]; 836 if (b != gsPointTypes[i]) 837 return false; 838 } 839 } 840 841 } else { 843 return false; 844 } 845 return true; 846 } 847 848 855 public synchronized void setLastPoint(float x, float y) 856 { 858 if (numTypes < 1 || numCoords < 2) { 859 return; 860 } 861 int index = numCoords; 862 if (pointTypes[numTypes - 1] == PathIterator.SEG_CLOSE) { 863 loop: 864 for (int i = numTypes - 2; i > 0; i--) { 865 switch (pointTypes[i]) { 866 case PathIterator.SEG_MOVETO: 867 break loop; 868 case PathIterator.SEG_LINETO: 869 index -= 2; 870 break; 871 case PathIterator.SEG_QUADTO: 872 index -= 4; 873 break; 874 case PathIterator.SEG_CUBICTO: 875 index -= 6; 876 break; 877 case PathIterator.SEG_CLOSE: 878 break; 879 } 880 } 881 } 882 pointCoords[index - 2] = x; 883 pointCoords[index - 1] = y; 884 bounds = null; 885 } 886 887 public synchronized void setPathSegment(int type, int index, float[] coords) 888 { 889 System.out.println("GeneralShape.setPathSegment"); 891 switch (type) 892 { 893 case (PathIterator.SEG_MOVETO): 894 pointCoords[index] = coords[0]; 895 pointCoords[index+1] = coords[1]; 896 break; 897 case (PathIterator.SEG_LINETO): 898 pointCoords[index] = coords[0]; 899 pointCoords[index+1] = coords[1]; 900 break; 901 case (PathIterator.SEG_QUADTO): 902 pointCoords[index] = coords[0]; 903 pointCoords[index+1] = coords[1]; 904 pointCoords[index+2] = coords[2]; 905 pointCoords[index+3] = coords[3]; 906 break; 907 case (PathIterator.SEG_CUBICTO): 908 pointCoords[index] = coords[0]; 909 pointCoords[index+1] = coords[1]; 910 pointCoords[index+2] = coords[2]; 911 pointCoords[index+3] = coords[3]; 912 pointCoords[index+4] = coords[4]; 913 pointCoords[index+5] = coords[5]; 914 break; 915 } 916 bounds = null; 917 } 918 919 936 962 980 protected void initPathSegments() 981 { 982 pathSegments = new ArrayList (); 983 int index = 0; 984 float[] coords = new float[6]; 985 for (PathIterator pi = getPathIterator(null); !pi.isDone(); pi.next()) 986 { 987 int segType = pi.currentSegment(coords); 988 switch (segType) 989 { 990 case (PathIterator.SEG_MOVETO): 991 PathSegment ps = new PathSegment(segType, index, coords, this); 992 pathSegments.add(ps); 993 index = index + 2; 994 break; 995 case (PathIterator.SEG_LINETO): 996 PathSegment ps1 = new PathSegment(segType, index, coords, this); 997 pathSegments.add(ps1); 998 index = index + 2; 999 break; 1000 case (PathIterator.SEG_QUADTO): 1001 PathSegment ps2 = new PathSegment(segType, index, coords, this); 1002 pathSegments.add(ps2); 1003 index = index + 4; 1004 break; 1005 case (PathIterator.SEG_CUBICTO): 1006 PathSegment ps3 = new PathSegment(segType, index, coords, this); 1007 pathSegments.add(ps3); 1008 index = index + 6; 1009 break; 1010 case (PathIterator.SEG_CLOSE): 1011 break; 1015 } 1016 } 1017 } 1018 1019 protected List pathSegments; 1020 1021 public List getPathSegments() 1022 { 1023 if (pathSegments == null) 1024 initPathSegments(); 1025 1026 return pathSegments; 1027 } 1028 1029 public PathSegment getPathSegment(int index) 1030 { 1031 if (pathSegments == null) 1032 initPathSegments(); 1033 1034 return (PathSegment) pathSegments.get(index); 1035 } 1036 1037 public PathSegment getPathSegmentAt(int x, int y) 1038 { 1039 for (Iterator it = getPathSegments().iterator(); it.hasNext(); ) { 1040 PathSegment ps = (PathSegment) it.next(); 1041 int contains = ps.contains(x,y); 1042 if (contains != PathSegment.NO_POINT) 1043 return ps; 1044 } 1045 return null; 1046 } 1047 1048 1060 public int getPointCount() 1061 { 1062 int counter = 0; 1063 for (PathIterator pi = getPathIterator(null); !pi.isDone(); pi.next()) { 1064 counter++; 1065 } 1066 return counter; 1067 } 1068 1069} 1070 1071 | Popular Tags |