1 package prefuse.util; 2 3 import java.awt.BasicStroke ; 4 import java.awt.Color ; 5 import java.awt.Graphics2D ; 6 import java.awt.Shape ; 7 import java.awt.Stroke ; 8 import java.awt.geom.AffineTransform ; 9 import java.awt.geom.Ellipse2D ; 10 import java.awt.geom.GeneralPath ; 11 import java.awt.geom.Line2D ; 12 import java.awt.geom.Point2D ; 13 import java.awt.geom.Rectangle2D ; 14 import java.awt.geom.RectangularShape ; 15 import java.awt.geom.RoundRectangle2D ; 16 17 import prefuse.render.AbstractShapeRenderer; 18 import prefuse.visual.VisualItem; 19 20 27 public class GraphicsLib { 28 29 30 public static final int NO_INTERSECTION = 0; 31 32 public static final int COINCIDENT = -1; 33 34 public static final int PARALLEL = -2; 35 36 44 public static int intersectLineLine(Line2D a, Line2D b, Point2D intersect) { 45 double a1x = a.getX1(), a1y = a.getY1(); 46 double a2x = a.getX2(), a2y = a.getY2(); 47 double b1x = b.getX1(), b1y = b.getY1(); 48 double b2x = b.getX2(), b2y = b.getY2(); 49 return intersectLineLine(a1x,a1y,a2x,a2y,b1x,b1y,b2x,b2y,intersect); 50 } 51 52 66 public static int intersectLineLine(double a1x, double a1y, double a2x, 67 double a2y, double b1x, double b1y, double b2x, double b2y, 68 Point2D intersect) 69 { 70 double ua_t = (b2x-b1x)*(a1y-b1y)-(b2y-b1y)*(a1x-b1x); 71 double ub_t = (a2x-a1x)*(a1y-b1y)-(a2y-a1y)*(a1x-b1x); 72 double u_b = (b2y-b1y)*(a2x-a1x)-(b2x-b1x)*(a2y-a1y); 73 74 if ( u_b != 0 ) { 75 double ua = ua_t / u_b; 76 double ub = ub_t / u_b; 77 78 if ( 0 <= ua && ua <= 1 && 0 <= ub && ub <= 1 ) { 79 intersect.setLocation(a1x+ua*(a2x-a1x), a1y+ua*(a2y-a1y)); 80 return 1; 81 } else { 82 return NO_INTERSECTION; 83 } 84 } else { 85 return ( ua_t == 0 || ub_t == 0 ? COINCIDENT : PARALLEL ); 86 } 87 } 88 89 99 public static int intersectLineRectangle(Point2D a1, Point2D a2, Rectangle2D r, Point2D [] pts) { 100 double a1x = a1.getX(), a1y = a1.getY(); 101 double a2x = a2.getX(), a2y = a2.getY(); 102 double mxx = r.getMaxX(), mxy = r.getMaxY(); 103 double mnx = r.getMinX(), mny = r.getMinY(); 104 105 if ( pts[0] == null ) pts[0] = new Point2D.Double (); 106 if ( pts[1] == null ) pts[1] = new Point2D.Double (); 107 108 int i = 0; 109 if ( intersectLineLine(mnx,mny,mxx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 110 if ( intersectLineLine(mxx,mny,mxx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 111 if ( i == 2 ) return i; 112 if ( intersectLineLine(mxx,mxy,mnx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 113 if ( i == 2 ) return i; 114 if ( intersectLineLine(mnx,mxy,mnx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 115 return i; 116 } 117 118 127 public static int intersectLineRectangle(Line2D l, Rectangle2D r, Point2D [] pts) { 128 double a1x = l.getX1(), a1y = l.getY1(); 129 double a2x = l.getX2(), a2y = l.getY2(); 130 double mxx = r.getMaxX(), mxy = r.getMaxY(); 131 double mnx = r.getMinX(), mny = r.getMinY(); 132 133 if ( pts[0] == null ) pts[0] = new Point2D.Double (); 134 if ( pts[1] == null ) pts[1] = new Point2D.Double (); 135 136 int i = 0; 137 if ( intersectLineLine(mnx,mny,mxx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 138 if ( intersectLineLine(mxx,mny,mxx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 139 if ( i == 2 ) return i; 140 if ( intersectLineLine(mxx,mxy,mnx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 141 if ( i == 2 ) return i; 142 if ( intersectLineLine(mnx,mxy,mnx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++; 143 return i; 144 } 145 146 158 public static double[] convexHull(double[] pts, int len) { 159 if (len < 6) { 160 throw new IllegalArgumentException ( 161 "Input must have at least 3 points"); 162 } 163 int plen = len/2-1; 164 float[] angles = new float[plen]; 165 int[] idx = new int[plen]; 166 int[] stack = new int[len/2]; 167 return convexHull(pts, len, angles, idx, stack); 168 } 169 170 181 public static double[] convexHull(double[] pts, int len, 182 float[] angles, int[] idx, int[] stack) 183 { 184 int plen = len/2 - 1; 186 if (len < 6) { 187 throw new IllegalArgumentException ( 188 "Input must have at least 3 points"); 189 } 190 if (angles.length < plen || idx.length < plen || stack.length < len/2) { 191 throw new IllegalArgumentException ( 192 "Pre-allocated data structure too small"); 193 } 194 195 int i0 = 0; 196 for ( int i=2; i < len; i += 2 ) { 198 if ( pts[i+1] < pts[i0+1] ) { 199 i0 = i; 200 } else if ( pts[i+1] == pts[i0+1] ) { 201 i0 = (pts[i] < pts[i0] ? i : i0); 202 } 203 } 204 205 for ( int i=0, j=0; i < len; i+=2 ) { 207 if ( i == i0 ) continue; 208 angles[j] = (float)Math.atan2(pts[i+1]-pts[i0+1], pts[i]-pts[i0]); 209 idx[j++] = i; 210 } 211 ArrayLib.sort(angles,idx,plen); 212 213 float angle = angles[0]; 215 int ti = 0, tj = idx[0]; 216 for ( int i=1; i<plen; i++ ) { 217 int j = idx[i]; 218 if ( angle == angles[i] ) { 219 double x1 = pts[tj] - pts[i0]; 222 double y1 = pts[tj+1] - pts[i0+1]; 223 double x2 = pts[j] - pts[i0]; 224 double y2 = pts[j+1] - pts[i0+1]; 225 double d1 = x1*x1 + y1*y1; 226 double d2 = x2*x2 + y2*y2; 227 if ( d1 >= d2 ) { 228 idx[i] = -1; 229 } else { 230 idx[ti] = -1; 231 angle = angles[i]; 232 ti = i; 233 tj = j; 234 } 235 } else { 236 angle = angles[i]; 237 ti = i; 238 tj = j; 239 } 240 } 241 242 int sp = 0; 244 stack[sp++] = i0; 245 int j = 0; 246 for ( int k=0; k<2; j++ ) { 247 if ( idx[j] != -1 ) { 248 stack[sp++] = idx[j]; 249 k++; 250 } 251 } 252 253 for ( ; j < plen; j++ ) { 255 if ( idx[j] == -1 ) continue; while ( isNonLeft(i0, stack[sp-2], stack[sp-1], idx[j], pts) ) { 257 sp--; 258 } 259 stack[sp++] = idx[j]; 260 } 261 262 double[] hull = new double[2*sp]; 264 for ( int i=0; i<sp; i++ ) { 265 hull[2*i] = pts[stack[i]]; 266 hull[2*i+1] = pts[stack[i]+1]; 267 } 268 269 return hull; 270 } 271 272 275 private static boolean isNonLeft(int i0, int i1, int i2, int i3, double[] pts) { 276 double l1, l2, l4, l5, l6, angle1, angle2, angle; 277 278 l1 = Math.sqrt(Math.pow(pts[i2+1]-pts[i1+1],2) + Math.pow(pts[i2]-pts[i1],2)); 279 l2 = Math.sqrt(Math.pow(pts[i3+1]-pts[i2+1],2) + Math.pow(pts[i3]-pts[i2],2)); 280 l4 = Math.sqrt(Math.pow(pts[i3+1]-pts[i0+1],2) + Math.pow(pts[i3]-pts[i0],2)); 281 l5 = Math.sqrt(Math.pow(pts[i1+1]-pts[i0+1],2) + Math.pow(pts[i1]-pts[i0],2)); 282 l6 = Math.sqrt(Math.pow(pts[i2+1]-pts[i0+1],2) + Math.pow(pts[i2]-pts[i0],2)); 283 284 angle1 = Math.acos( ( (l2*l2)+(l6*l6)-(l4*l4) ) / (2*l2*l6) ); 285 angle2 = Math.acos( ( (l6*l6)+(l1*l1)-(l5*l5) ) / (2*l6*l1) ); 286 287 angle = (Math.PI - angle1) - angle2; 288 289 if (angle <= 0.0) { 290 return(true); 291 } else { 292 return(false); 293 } 294 } 295 296 302 public static float[] centroid(float pts[], int len) { 303 float[] c = new float[] {0, 0}; 304 for ( int i=0; i < len; i+=2 ) { 305 c[0] += pts[i]; 306 c[1] += pts[i+1]; 307 } 308 c[0] /= len/2; 309 c[1] /= len/2; 310 return c; 311 } 312 313 322 public static void growPolygon(float pts[], int len, float amt) { 323 float[] c = centroid(pts, len); 324 for ( int i=0; i < len; i+=2 ) { 325 float vx = pts[i]-c[0]; 326 float vy = pts[i+1]-c[1]; 327 float norm = (float)Math.sqrt(vx*vx+vy*vy); 328 pts[i] += amt*vx/norm; 329 pts[i+1] += amt*vy/norm; 330 } 331 } 332 333 346 public static GeneralPath cardinalSpline(float pts[], float slack, boolean closed) { 347 GeneralPath path = new GeneralPath (); 348 path.moveTo(pts[0], pts[1]); 349 return cardinalSpline(path, pts, slack, closed, 0f, 0f); 350 } 351 352 367 public static GeneralPath cardinalSpline(float pts[], int start, int npoints, 368 float slack, boolean closed) 369 { 370 GeneralPath path = new GeneralPath (); 371 path.moveTo(pts[start], pts[start+1]); 372 return cardinalSpline(path, pts, start, npoints, slack, closed, 0f, 0f); 373 } 374 375 391 public static GeneralPath cardinalSpline(GeneralPath p, 392 float pts[], float slack, boolean closed, float tx, float ty) 393 { 394 int npoints = 0; 395 for ( ; npoints<pts.length; ++npoints ) 396 if ( Float.isNaN(pts[npoints]) ) break; 397 return cardinalSpline(p, pts, 0, npoints/2, slack, closed, tx, ty); 398 } 399 400 418 public static GeneralPath cardinalSpline(GeneralPath p, 419 float pts[], int start, int npoints, 420 float slack, boolean closed, float tx, float ty) 421 { 422 int len = 2*npoints; 424 int end = start+len; 425 426 if ( len < 6 ) { 427 throw new IllegalArgumentException ( 428 "To create spline requires at least 3 points"); 429 } 430 431 float dx1, dy1, dx2, dy2; 432 433 if ( closed ) { 435 dx2 = pts[start+2]-pts[end-2]; 436 dy2 = pts[start+3]-pts[end-1]; 437 } else { 438 dx2 = pts[start+4]-pts[start]; 439 dy2 = pts[start+5]-pts[start+1]; 440 } 441 442 int i; 444 for ( i=start+2; i<end-2; i+=2 ) { 445 dx1 = dx2; dy1 = dy2; 446 dx2 = pts[i+2]-pts[i-2]; 447 dy2 = pts[i+3]-pts[i-1]; 448 p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1, 449 tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2, 450 tx+pts[i], ty+pts[i+1]); 451 } 452 453 if ( closed ) { 455 dx1 = dx2; dy1 = dy2; 456 dx2 = pts[start]-pts[i-2]; 457 dy2 = pts[start+1]-pts[i-1]; 458 p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1, 459 tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2, 460 tx+pts[i], ty+pts[i+1]); 461 462 dx1 = dx2; dy1 = dy2; 463 dx2 = pts[start+2]-pts[end-2]; 464 dy2 = pts[start+3]-pts[end-1]; 465 p.curveTo(tx+pts[end-2]+slack*dx1, ty+pts[end-1]+slack*dy1, 466 tx+pts[0] -slack*dx2, ty+pts[1] -slack*dy2, 467 tx+pts[0], ty+pts[1]); 468 p.closePath(); 469 } else { 470 p.curveTo(tx+pts[i-2]+slack*dx2, ty+pts[i-1]+slack*dy2, 471 tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2, 472 tx+pts[i], ty+pts[i+1]); 473 } 474 return p; 475 } 476 477 494 public static GeneralPath stackSpline(GeneralPath p, float[] pts, 495 float epsilon, float slack, boolean closed, float tx, float ty) 496 { 497 int npoints = 0; 498 for ( ; npoints<pts.length; ++npoints ) 499 if ( Float.isNaN(pts[npoints]) ) break; 500 return stackSpline(p,pts,0,npoints/2,epsilon,slack,closed,tx,ty); 501 } 502 503 522 public static GeneralPath stackSpline(GeneralPath p, 523 float pts[], int start, int npoints, float epsilon, 524 float slack, boolean closed, float tx, float ty) 525 { 526 int len = 2*npoints; 528 int end = start+len; 529 530 if ( len < 6 ) { 531 throw new IllegalArgumentException ( 532 "To create spline requires at least 3 points"); 533 } 534 535 float dx1, dy1, dx2, dy2; 536 if ( closed ) { 538 dx2 = pts[start+2]-pts[end-2]; 539 dy2 = pts[start+3]-pts[end-1]; 540 } else { 541 dx2 = pts[start+4]-pts[start]; 542 dy2 = pts[start+5]-pts[start+1]; 543 } 544 545 int i; 547 for ( i=start+2; i<end-2; i+=2 ) { 548 dx1 = dx2; dy1 = dy2; 549 dx2 = pts[i+2]-pts[i-2]; 550 dy2 = pts[i+3]-pts[i-1]; 551 if ( Math.abs(pts[i] -pts[i-2]) < epsilon || 552 Math.abs(pts[i+1]-pts[i-1]) < epsilon ) 553 { 554 p.lineTo(tx+pts[i], ty+pts[i+1]); 555 } else { 556 p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1, 557 tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2, 558 tx+pts[i], ty+pts[i+1]); 559 } 560 } 561 562 dx1 = dx2; dy1 = dy2; 564 dx2 = pts[start]-pts[i-2]; 565 dy2 = pts[start+1]-pts[i-1]; 566 if ( Math.abs(pts[i] -pts[i-2]) < epsilon || 567 Math.abs(pts[i+1]-pts[i-1]) < epsilon ) 568 { 569 p.lineTo(tx+pts[i], ty+pts[i+1]); 570 } else { 571 p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1, 572 tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2, 573 tx+pts[i], ty+pts[i+1]); 574 } 575 576 if ( closed ) { 578 if ( Math.abs(pts[end-2]-pts[0]) < epsilon || 579 Math.abs(pts[end-1]-pts[1]) < epsilon ) 580 { 581 p.lineTo(tx+pts[0], ty+pts[1]); 582 } else { 583 dx1 = dx2; dy1 = dy2; 584 dx2 = pts[start+2]-pts[end-2]; 585 dy2 = pts[start+3]-pts[end-1]; 586 p.curveTo(tx+pts[end-2]+slack*dx1, ty+pts[end-1]+slack*dy1, 587 tx+pts[0] -slack*dx2, ty+pts[1] -slack*dy2, 588 tx+pts[0], ty+pts[1]); 589 } 590 p.closePath(); 591 } 592 return p; 593 } 594 595 600 public static void expand(Rectangle2D r, double amount) { 601 r.setRect(r.getX()-amount, r.getY()-amount, 602 r.getWidth()+2*amount, r.getHeight()+2*amount); 603 } 604 605 607 617 public static void setBounds(VisualItem item, 618 Shape shape, BasicStroke stroke) 619 { 620 double x, y, w, h, lw, lw2; 621 622 if ( shape instanceof RectangularShape ) { 623 RectangularShape r = (RectangularShape )shape; 625 x = r.getX(); 626 y = r.getY(); 627 w = r.getWidth(); 628 h = r.getHeight(); 629 } else if ( shape instanceof Line2D ) { 630 Line2D l = (Line2D )shape; 632 x = l.getX1(); 633 y = l.getY1(); 634 w = l.getX2(); 635 h = l.getY2(); 636 if ( w < x ) { 637 lw = x; 638 x = w; 639 w = lw-x; 640 } else { 641 w = w-x; 642 } 643 if ( h < y ) { 644 lw = y; 645 y = h; 646 h = lw-y; 647 } else { 648 h = h-y; 649 } 650 } else { 651 Rectangle2D r = shape.getBounds2D(); 654 x = r.getX(); 655 y = r.getY(); 656 w = r.getWidth(); 657 h = r.getHeight(); 658 } 659 660 if ( stroke != null && (lw=stroke.getLineWidth()) > 1 ) { 662 lw2 = lw/2.0; 663 x -= lw2; y -= lw2; w += lw; h += lw; 664 } 665 item.setBounds(x, y, w, h); 666 } 667 668 688 public static void paint(Graphics2D g, VisualItem item, 689 Shape shape, BasicStroke stroke, int type) 690 { 691 if ( type == AbstractShapeRenderer.RENDER_TYPE_NONE ) 693 return; 694 695 Color strokeColor = ColorLib.getColor(item.getStrokeColor()); 697 Color fillColor = ColorLib.getColor(item.getFillColor()); 698 boolean sdraw = (type == AbstractShapeRenderer.RENDER_TYPE_DRAW || 699 type == AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL) && 700 strokeColor.getAlpha() != 0; 701 boolean fdraw = (type == AbstractShapeRenderer.RENDER_TYPE_FILL || 702 type == AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL) && 703 fillColor.getAlpha() != 0; 704 if ( !(sdraw || fdraw) ) return; 705 706 Stroke origStroke = null; 707 if ( sdraw ) { 708 origStroke = g.getStroke(); 709 g.setStroke(stroke); 710 } 711 712 int x, y, w, h, aw, ah; 713 double xx, yy, ww, hh; 714 715 AffineTransform at = g.getTransform(); 721 double scale = Math.max(at.getScaleX(), at.getScaleY()); 722 if ( scale > 1.5 ) { 723 if (fdraw) { g.setPaint(fillColor); g.fill(shape); } 724 if (sdraw) { g.setPaint(strokeColor); g.draw(shape); } 725 } 726 else if ( shape instanceof RectangularShape ) 727 { 728 RectangularShape r = (RectangularShape )shape; 729 xx = r.getX(); ww = r.getWidth(); 730 yy = r.getY(); hh = r.getHeight(); 731 732 x = (int)xx; 733 y = (int)yy; 734 w = (int)(ww+xx-x); 735 h = (int)(hh+yy-y); 736 737 if ( shape instanceof Rectangle2D ) { 738 if (fdraw) { 739 g.setPaint(fillColor); 740 g.fillRect(x, y, w, h); 741 } 742 if (sdraw) { 743 g.setPaint(strokeColor); 744 g.drawRect(x, y, w, h); 745 } 746 } else if ( shape instanceof RoundRectangle2D ) { 747 RoundRectangle2D rr = (RoundRectangle2D )shape; 748 aw = (int)rr.getArcWidth(); 749 ah = (int)rr.getArcHeight(); 750 if (fdraw) { 751 g.setPaint(fillColor); 752 g.fillRoundRect(x, y, w, h, aw, ah); 753 } 754 if (sdraw) { 755 g.setPaint(strokeColor); 756 g.drawRoundRect(x, y, w, h, aw, ah); 757 } 758 } else if ( shape instanceof Ellipse2D ) { 759 if (fdraw) { 760 g.setPaint(fillColor); 761 g.fillOval(x, y, w, h); 762 } 763 if (sdraw) { 764 g.setPaint(strokeColor); 765 g.drawOval(x, y, w, h); 766 } 767 } else { 768 if (fdraw) { g.setPaint(fillColor); g.fill(shape); } 769 if (sdraw) { g.setPaint(strokeColor); g.draw(shape); } 770 } 771 } else if ( shape instanceof Line2D ) { 772 if (sdraw) { 773 Line2D l = (Line2D )shape; 774 x = (int)(l.getX1()+0.5); 775 y = (int)(l.getY1()+0.5); 776 w = (int)(l.getX2()+0.5); 777 h = (int)(l.getY2()+0.5); 778 g.setPaint(strokeColor); 779 g.drawLine(x, y, w, h); 780 } 781 } else { 782 if (fdraw) { g.setPaint(fillColor); g.fill(shape); } 783 if (sdraw) { g.setPaint(strokeColor); g.draw(shape); } 784 } 785 if ( sdraw ) { 786 g.setStroke(origStroke); 787 } 788 } 789 790 } | Popular Tags |