1 18 package org.apache.batik.gvt; 19 20 import java.awt.Graphics2D ; 21 import java.awt.Shape ; 22 import java.awt.geom.AffineTransform ; 23 import java.awt.geom.Arc2D ; 24 import java.awt.geom.PathIterator ; 25 import java.awt.geom.Point2D ; 26 import java.awt.geom.Rectangle2D ; 27 import java.util.List ; 28 import java.util.Vector ; 29 30 import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; 31 import org.apache.batik.ext.awt.geom.ExtendedPathIterator; 32 import org.apache.batik.ext.awt.geom.ExtendedShape; 33 import org.apache.batik.ext.awt.geom.ShapeExtender; 34 35 41 public class MarkerShapePainter implements ShapePainter { 42 43 46 protected ExtendedShape extShape; 47 48 51 protected Marker startMarker; 52 53 56 protected Marker middleMarker; 57 58 61 protected Marker endMarker; 62 63 66 private ProxyGraphicsNode startMarkerProxy; 67 68 71 private ProxyGraphicsNode middleMarkerProxies[]; 72 73 76 private ProxyGraphicsNode endMarkerProxy; 77 78 81 private CompositeGraphicsNode markerGroup; 82 83 86 private Rectangle2D dPrimitiveBounds; 87 88 91 private Rectangle2D dGeometryBounds; 92 93 100 public MarkerShapePainter(Shape shape) { 101 if (shape == null) { 102 throw new IllegalArgumentException (); 103 } 104 if (shape instanceof ExtendedShape) { 105 this.extShape = (ExtendedShape)shape; 106 } else { 107 this.extShape = new ShapeExtender(shape); 108 } 109 } 110 111 116 public void paint(Graphics2D g2d) { 117 if (markerGroup == null) { 118 buildMarkerGroup(); 119 } 120 if (markerGroup.getChildren().size() > 0) { 121 markerGroup.paint(g2d); 122 } 123 } 124 125 128 public Shape getPaintedArea(){ 129 if (markerGroup == null) { 130 buildMarkerGroup(); 131 } 132 return markerGroup.getOutline(); 133 } 134 135 138 public Rectangle2D getPaintedBounds2D(){ 139 if (markerGroup == null) { 140 buildMarkerGroup(); 141 } 142 return markerGroup.getPrimitiveBounds(); 143 } 144 145 148 public boolean inPaintedArea(Point2D pt){ 149 if (markerGroup == null) { 150 buildMarkerGroup(); 151 } 152 GraphicsNode gn = markerGroup.nodeHitAt(pt); 153 return (gn != null); 154 } 155 156 160 public Shape getSensitiveArea() { return null; } 161 162 166 public Rectangle2D getSensitiveBounds2D() { return null; } 167 168 172 public boolean inSensitiveArea(Point2D pt) { return false; } 173 174 175 181 public void setShape(Shape shape){ 182 if (shape == null) { 183 throw new IllegalArgumentException (); 184 } 185 if (shape instanceof ExtendedShape) { 186 this.extShape = (ExtendedShape)shape; 187 } else { 188 this.extShape = new ShapeExtender(shape); 189 } 190 191 this.startMarkerProxy = null; 192 this.middleMarkerProxies = null; 193 this.endMarkerProxy = null; 194 this.markerGroup = null; 195 } 196 197 202 public ExtendedShape getExtShape(){ 203 return extShape; 204 } 205 206 211 public Shape getShape(){ 212 return extShape; 213 } 214 215 219 public Marker getStartMarker(){ 220 return startMarker; 221 } 222 223 229 public void setStartMarker(Marker startMarker){ 230 this.startMarker = startMarker; 231 this.startMarkerProxy = null; 232 this.markerGroup = null; 233 } 234 235 239 public Marker getMiddleMarker(){ 240 return middleMarker; 241 } 242 243 249 public void setMiddleMarker(Marker middleMarker){ 250 this.middleMarker = middleMarker; 251 this.middleMarkerProxies = null; 252 this.markerGroup = null; 253 } 254 255 259 public Marker getEndMarker(){ 260 return endMarker; 261 } 262 263 269 public void setEndMarker(Marker endMarker){ 270 this.endMarker = endMarker; 271 this.endMarkerProxy = null; 272 this.markerGroup = null; 273 } 274 275 279 282 protected void buildMarkerGroup(){ 283 if (startMarker != null && startMarkerProxy == null) { 284 startMarkerProxy = buildStartMarkerProxy(); 285 } 286 287 if (middleMarker != null && middleMarkerProxies == null) { 288 middleMarkerProxies = buildMiddleMarkerProxies(); 289 } 290 291 if (endMarker != null && endMarkerProxy == null) { 292 endMarkerProxy = buildEndMarkerProxy(); 293 } 294 295 CompositeGraphicsNode group = new CompositeGraphicsNode(); 296 List children = group.getChildren(); 297 if (startMarkerProxy != null) { 298 children.add(startMarkerProxy); 299 } 300 301 if (middleMarkerProxies != null) { 302 for(int i=0; i<middleMarkerProxies.length; i++){ 303 children.add(middleMarkerProxies[i]); 304 } 305 } 306 307 if (endMarkerProxy != null) { 308 children.add(endMarkerProxy); 309 } 310 311 markerGroup = group; 312 } 313 314 318 protected ProxyGraphicsNode buildStartMarkerProxy() { 319 320 ExtendedPathIterator iter = getExtShape().getExtendedPathIterator(); 321 322 double coords[] = new double[7]; 324 int segType = 0; 325 326 if (iter.isDone()) { 327 return null; 328 } 329 330 segType = iter.currentSegment(coords); 331 if (segType != ExtendedPathIterator.SEG_MOVETO) { 332 return null; 333 } 334 iter.next(); 335 336 Point2D markerPosition = new Point2D.Double (coords[0], coords[1]); 337 338 double rotation = startMarker.getOrient(); 341 if (Double.isNaN(rotation)) { 342 if (!iter.isDone()) { 343 double next[] = new double[7]; 344 int nextSegType = 0; 345 nextSegType = iter.currentSegment(next); 346 if(nextSegType == PathIterator.SEG_CLOSE){ 347 nextSegType = PathIterator.SEG_LINETO; 348 next[0] = coords[0]; 349 next[1] = coords[1]; 350 } 351 rotation = computeRotation(null, 0, coords, segType, next, nextSegType); 355 } 356 } 357 358 AffineTransform markerTxf = computeMarkerTransform(startMarker, 360 markerPosition, 361 rotation); 362 363 ProxyGraphicsNode gn = new ProxyGraphicsNode(); 364 365 gn.setSource(startMarker.getMarkerNode()); 366 gn.setTransform(markerTxf); 367 368 return gn; 369 } 370 371 375 protected ProxyGraphicsNode buildEndMarkerProxy() { 376 377 ExtendedPathIterator iter = getExtShape().getExtendedPathIterator(); 378 379 int nPoints = 0; 380 381 if (iter.isDone()) { 384 return null; 385 } 386 387 double coords[] = new double[7]; 388 double moveTo[] = new double[2]; 389 int segType = 0; 390 segType = iter.currentSegment(coords); 391 if (segType != ExtendedPathIterator.SEG_MOVETO) { 392 return null; 393 } 394 nPoints++; 395 moveTo[0] = coords[0]; 396 moveTo[1] = coords[1]; 397 398 iter.next(); 399 400 double[] lastButOne = new double[7]; 402 double[] last = {coords[0], coords[1], coords[2], 403 coords[3], coords[4], coords[5], coords[6] }; 404 double[] tmp = null; 405 int lastSegType = segType; 406 int lastButOneSegType = 0; 407 408 while (!iter.isDone()) { 409 tmp = lastButOne; 410 lastButOne = last; 411 last = tmp; 412 lastButOneSegType = lastSegType; 413 414 lastSegType = iter.currentSegment(last); 415 416 if (lastSegType == PathIterator.SEG_MOVETO) { 417 moveTo[0] = last[0]; 418 moveTo[1] = last[1]; 419 } else if (lastSegType == PathIterator.SEG_CLOSE) { 420 lastSegType = PathIterator.SEG_LINETO; 421 last[0] = moveTo[0]; 422 last[1] = moveTo[1]; 423 } 424 425 iter.next(); 426 nPoints++; 427 } 428 429 if (nPoints < 2) { 430 return null; 431 } 432 433 Point2D markerPosition = getSegmentTerminatingPoint(last, lastSegType); 435 436 double rotation = endMarker.getOrient(); 439 if (Double.isNaN(rotation)) { 440 rotation = computeRotation(lastButOne, 441 lastButOneSegType, 442 last, lastSegType, 443 null, 0); 444 } 445 446 AffineTransform markerTxf = computeMarkerTransform(endMarker, 448 markerPosition, 449 rotation); 450 451 ProxyGraphicsNode gn = new ProxyGraphicsNode(); 452 453 gn.setSource(endMarker.getMarkerNode()); 454 gn.setTransform(markerTxf); 455 456 return gn; 457 } 458 459 463 protected ProxyGraphicsNode[] buildMiddleMarkerProxies() { 464 465 ExtendedPathIterator iter = getExtShape().getExtendedPathIterator(); 466 467 double[] prev = new double[7]; 468 double[] curr = new double[7]; 469 double[] next = new double[7], tmp = null; 470 int prevSegType = 0, currSegType = 0, nextSegType = 0; 471 472 if (iter.isDone()) { 474 return null; 475 } 476 477 prevSegType = iter.currentSegment(prev); 478 double[] moveTo = new double[2]; 479 480 if (prevSegType != PathIterator.SEG_MOVETO) { 481 return null; 482 } 483 484 moveTo[0] = prev[0]; 485 moveTo[1] = prev[1]; 486 iter.next(); 487 488 if (iter.isDone()) { 489 return null; 490 } 491 492 currSegType = iter.currentSegment(curr); 493 494 if (currSegType == PathIterator.SEG_MOVETO) { 495 moveTo[0] = curr[0]; 496 moveTo[1] = curr[1]; 497 } else if (currSegType == PathIterator.SEG_CLOSE) { 498 currSegType = PathIterator.SEG_LINETO; 499 curr[0] = moveTo[0]; 500 curr[1] = moveTo[1]; 501 } 502 503 iter.next(); 504 505 Vector proxies = new Vector (); 506 while (!iter.isDone()) { 507 nextSegType = iter.currentSegment(next); 508 509 if (nextSegType == PathIterator.SEG_MOVETO) { 510 moveTo[0] = next[0]; 511 moveTo[1] = next[1]; 512 } else if (nextSegType == PathIterator.SEG_CLOSE) { 513 nextSegType = PathIterator.SEG_LINETO; 514 next[0] = moveTo[0]; 515 next[1] = moveTo[1]; 516 } 517 518 proxies.addElement(createMiddleMarker(prev, prevSegType, 519 curr, currSegType, 520 next, nextSegType)); 521 522 tmp = prev; 523 prev = curr; 524 prevSegType = currSegType; 525 curr = next; 526 currSegType = nextSegType; 527 next = tmp; 528 529 iter.next(); 530 } 531 532 ProxyGraphicsNode [] gn = new ProxyGraphicsNode[proxies.size()]; 533 proxies.copyInto(gn); 534 535 return gn; 536 } 537 538 541 private ProxyGraphicsNode createMiddleMarker 542 (double[] prev, int prevSegType, 543 double[] curr, int currSegType, 544 double[] next, int nextSegType) { 545 546 Point2D markerPosition = getSegmentTerminatingPoint(curr, currSegType); 548 549 double rotation = middleMarker.getOrient(); 552 if (Double.isNaN(rotation)) { 553 rotation = computeRotation(prev, prevSegType, 554 curr, currSegType, 555 next, nextSegType); 556 } 557 558 AffineTransform markerTxf = computeMarkerTransform(middleMarker, 560 markerPosition, 561 rotation); 562 563 ProxyGraphicsNode gn = new ProxyGraphicsNode(); 564 565 gn.setSource(middleMarker.getMarkerNode()); 566 gn.setTransform(markerTxf); 567 568 return gn; 569 } 570 571 574 private double computeRotation(double[] prev, int prevSegType, 575 double[] curr, int currSegType, 576 double[] next, int nextSegType){ 577 578 double[] inSlope = computeInSlope(prev, prevSegType, 581 curr, currSegType); 582 583 double[] outSlope = computeOutSlope(curr, currSegType, 586 next, nextSegType); 587 588 if (inSlope == null) { 589 inSlope = outSlope; 590 } 591 592 if (outSlope == null) { 593 outSlope = inSlope; 594 } 595 596 if (inSlope == null) { 597 return 0; 598 } 599 600 double dx = inSlope[0] + outSlope[0]; 601 double dy = inSlope[1] + outSlope[1]; 602 603 if (dx == 0 && dy == 0) { 604 return Math.atan2(inSlope[1], inSlope[0])*180./Math.PI + 90; 607 } else { 608 return Math.atan2(dy, dx)*180./Math.PI; 609 } 610 } 611 612 615 private double[] computeInSlope(double[] prev, int prevSegType, 616 double[] curr, int currSegType){ 617 618 Point2D currEndPoint = getSegmentTerminatingPoint(curr, currSegType); 620 621 double dx = 0; 622 double dy = 0; 623 624 switch(currSegType){ 625 case PathIterator.SEG_LINETO: { 626 Point2D prevEndPoint = 629 getSegmentTerminatingPoint(prev, prevSegType); 630 dx = currEndPoint.getX() - prevEndPoint.getX(); 631 dy = currEndPoint.getY() - prevEndPoint.getY(); 632 } 633 break; 634 case PathIterator.SEG_QUADTO: 635 dx = currEndPoint.getX() - curr[0]; 639 dy = currEndPoint.getY() - curr[1]; 640 break; 641 case PathIterator.SEG_CUBICTO: 642 dx = currEndPoint.getX() - curr[2]; 646 dy = currEndPoint.getY() - curr[3]; 647 break; 648 case ExtendedPathIterator.SEG_ARCTO: { 649 Point2D prevEndPoint = 652 getSegmentTerminatingPoint(prev, prevSegType); 653 boolean large = (curr[3]!=0.); 654 boolean goLeft = (curr[4]!=0.); 655 Arc2D arc = ExtendedGeneralPath.computeArc 656 (prevEndPoint.getX(), prevEndPoint.getY(), 657 curr[0], curr[1], curr[2], 658 large, goLeft, curr[5], curr[6]); 659 double theta = arc.getAngleStart()+arc.getAngleExtent(); 660 theta = Math.toRadians(theta); 661 dx = -arc.getWidth()/2.0*Math.sin(theta); 662 dy = arc.getHeight()/2.0*Math.cos(theta); 663 664 if (curr[2] != 0) { 667 double ang = Math.toRadians(-curr[2]); 668 double sinA = Math.sin(ang); 669 double cosA = Math.cos(ang); 670 double tdx = dx*cosA - dy*sinA; 671 double tdy = dx*sinA + dy*cosA; 672 dx = tdx; 673 dy = tdy; 674 } 675 if (goLeft) { 678 dx = -dx; 679 } else { 680 dy = -dy; 681 } 682 } 685 break; 686 case PathIterator.SEG_CLOSE: 687 throw new Error (); 689 case PathIterator.SEG_MOVETO: 690 default: 692 return null; 693 } 694 695 if (dx == 0 && dy == 0) { 696 return null; 697 } 698 699 return normalize(new double[] { dx, dy }); 700 } 701 702 705 private double[] computeOutSlope(double[] curr, int currSegType, 706 double[] next, int nextSegType){ 707 708 Point2D currEndPoint = getSegmentTerminatingPoint(curr, currSegType); 709 710 double dx = 0, dy = 0; 711 712 switch(nextSegType){ 713 case PathIterator.SEG_CLOSE: 714 break; 717 case PathIterator.SEG_CUBICTO: 718 case PathIterator.SEG_LINETO: 719 case PathIterator.SEG_QUADTO: 720 dx = next[0] - currEndPoint.getX(); 724 dy = next[1] - currEndPoint.getY(); 725 break; 726 case ExtendedPathIterator.SEG_ARCTO: { 727 boolean large = (next[3]!=0.); 730 boolean goLeft = (next[4]!=0.); 731 Arc2D arc = ExtendedGeneralPath.computeArc 732 (currEndPoint.getX(), currEndPoint.getY(), 733 next[0], next[1], next[2], 734 large, goLeft, next[5], next[6]); 735 double theta = arc.getAngleStart(); 736 theta = Math.toRadians(theta); 737 dx = -arc.getWidth()/2.0*Math.sin(theta); 738 dy = arc.getHeight()/2.0*Math.cos(theta); 739 if (next[2] != 0) { 742 double ang = Math.toRadians(-next[2]); 743 double sinA = Math.sin(ang); 744 double cosA = Math.cos(ang); 745 double tdx = dx*cosA - dy*sinA; 746 double tdy = dx*sinA + dy*cosA; 747 dx = tdx; 748 dy = tdy; 749 } 750 753 if (goLeft) { 754 dx = -dx; 755 } else { 756 dy = -dy; 757 } 758 } 761 break; 762 case PathIterator.SEG_MOVETO: 763 default: 765 return null; 766 } 767 768 if (dx == 0 && dy == 0) { 769 return null; 770 } 771 772 return normalize(new double[] { dx, dy }); 773 } 774 775 778 public double[] normalize(double[] v) { 779 double n = Math.sqrt(v[0]*v[0]+v[1]*v[1]); 780 v[0] /= n; 781 v[1] /= n; 782 return v; 783 } 784 785 789 private AffineTransform computeMarkerTransform(Marker marker, 790 Point2D markerPosition, 791 double rotation) { 792 Point2D ref = marker.getRef(); 793 798 AffineTransform txf = new AffineTransform (); 799 800 txf.translate(markerPosition.getX() - ref.getX(), 801 markerPosition.getY() - ref.getY()); 802 803 if (!Double.isNaN(rotation)) { 804 txf.rotate(rotation*Math.PI/180., ref.getX(), ref.getY()); 805 } 806 807 return txf; 808 } 809 810 813 protected Point2D getSegmentTerminatingPoint(double coords[], int segType) { 814 switch(segType){ 815 case PathIterator.SEG_CUBICTO: 816 return new Point2D.Double (coords[4], coords[5]); 817 case PathIterator.SEG_LINETO: 818 return new Point2D.Double (coords[0], coords[1]); 819 case PathIterator.SEG_MOVETO: 820 return new Point2D.Double (coords[0], coords[1]); 821 case PathIterator.SEG_QUADTO: 822 return new Point2D.Double (coords[2], coords[3]); 823 case ExtendedPathIterator.SEG_ARCTO: 824 return new Point2D.Double (coords[5], coords[6]); 825 case PathIterator.SEG_CLOSE: 826 default: 827 throw new Error (); 828 } 830 } 831 } 832 | Popular Tags |