Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw a directed arrow line in Java?

I want to draw a directed arrow line through Java.

At present, I am using java.awt.Line2D.Double class to draw a line

g2.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); // g2 is an instance of Graphics2D g2.draw(new Line2D.Double(x1,y1,x2,y2)); 

But only the line appears and no directed arrow appears. BasicStroke.Join_BEVEL is used to draw a directed arrow. It is applied when two line segments meet.

The line I am drawing meets the border of a rectangle but no directed arrow is drawn. Only a simple line is drawn.

Is there anything I am missing?

like image 394
Yatendra Avatar asked Jan 08 '10 12:01

Yatendra


2 Answers

Although Pete's post is awesomely comprehensive, I'm using this method to draw a very simple line with a little triangle at its end.

// create an AffineTransform  // and a triangle centered on (0,0) and pointing downward // somewhere outside Swing's paint loop AffineTransform tx = new AffineTransform(); Line2D.Double line = new Line2D.Double(0,0,100,100);  Polygon arrowHead = new Polygon();   arrowHead.addPoint( 0,5); arrowHead.addPoint( -5, -5); arrowHead.addPoint( 5,-5);  // [...] private void drawArrowHead(Graphics2D g2d) {       tx.setToIdentity();     double angle = Math.atan2(line.y2-line.y1, line.x2-line.x1);     tx.translate(line.x2, line.y2);     tx.rotate((angle-Math.PI/2d));        Graphics2D g = (Graphics2D) g2d.create();     g.setTransform(tx);        g.fill(arrowHead);     g.dispose(); } 
like image 125
Vicente Reig Avatar answered Oct 04 '22 02:10

Vicente Reig


The bevel is drawn between segments in a polyline if they are at certain angles. It has no bearing if you are drawing a line which happens to be drawn near some other pixels which are of a certain colour - once you've drawn the rectangle, the Graphics object doesn't know about the rectangle, it (in effect) only holds the pixels. ( or rather the image or OS window holds the pixels ).

To draw a simple arrow, draw a line for the stalk as you're doing, then a polyline for the vee. Nicer looking nicer arrows have curved sides and are filled.

You probably don't want to use bevel for the arrow head, as bevels are a flat; instead use the mitre option:

import java.awt.*; import java.awt.geom.*; import javax.swing.*;  public class BevelArrows {     public static void main ( String...args )     {         SwingUtilities.invokeLater ( new Runnable () {             BevelArrows arrows = new BevelArrows();              @Override             public void run () {                 JFrame frame = new JFrame ( "Bevel Arrows" );                  frame.add ( new JPanel() {                     public void paintComponent ( Graphics g ) {                         arrows.draw ( ( Graphics2D ) g, getWidth(), getHeight() );                     }                 }                 , BorderLayout.CENTER );                  frame.setSize ( 800, 400 );                 frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );                 frame.setVisible ( true );             }         } );     }      interface Arrow {         void draw ( Graphics2D g );     }      Arrow[] arrows = { new LineArrow(), new CurvedArrow() };      void draw ( Graphics2D g, int width, int height )     {         g.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );          g.setColor ( Color.WHITE );         g.fillRect ( 0, 0, width, height );          for ( Arrow arrow : arrows ) {             g.setColor ( Color.ORANGE );             g.fillRect ( 350, 20, 20, 280 );              g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL ) );             g.translate ( 0, 60 );             arrow.draw ( g );              g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER ) );             g.translate ( 0, 100 );             arrow.draw ( g );              g.setStroke ( new BasicStroke ( 20.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND ) );             g.translate ( 0, 100 );             arrow.draw ( g );              g.translate ( 400, -260 );         }     }      static class LineArrow  implements Arrow     {         public void draw ( Graphics2D g )         {             // where the control point for the intersection of the V needs calculating             // by projecting where the ends meet              float arrowRatio = 0.5f;             float arrowLength = 80.0f;              BasicStroke stroke = ( BasicStroke ) g.getStroke();              float endX = 350.0f;              float veeX;              switch ( stroke.getLineJoin() ) {                 case BasicStroke.JOIN_BEVEL:                     // IIRC, bevel varies system to system, this is approximate                     veeX = endX - stroke.getLineWidth() * 0.25f;                     break;                 default:                 case BasicStroke.JOIN_MITER:                     veeX = endX - stroke.getLineWidth() * 0.5f / arrowRatio;                     break;                 case BasicStroke.JOIN_ROUND:                     veeX = endX - stroke.getLineWidth() * 0.5f;                     break;             }              // vee             Path2D.Float path = new Path2D.Float();              path.moveTo ( veeX - arrowLength, -arrowRatio*arrowLength );             path.lineTo ( veeX, 0.0f );             path.lineTo ( veeX - arrowLength, arrowRatio*arrowLength );              g.setColor ( Color.BLUE );             g.draw ( path );              // stem for exposition only             g.setColor ( Color.YELLOW );             g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX, 0.0f ) );              // in practice, move stem back a bit as rounding errors             // can make it poke through the sides of the Vee             g.setColor ( Color.RED );             g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX - stroke.getLineWidth() * 0.25f, 0.0f ) );         }     }      static class CurvedArrow  implements Arrow     {         // to draw a nice curved arrow, fill a V shape rather than stroking it with lines         public void draw ( Graphics2D g )         {             // as we're filling rather than stroking, control point is at the apex,              float arrowRatio = 0.5f;             float arrowLength = 80.0f;              BasicStroke stroke = ( BasicStroke ) g.getStroke();              float endX = 350.0f;              float veeX = endX - stroke.getLineWidth() * 0.5f / arrowRatio;              // vee             Path2D.Float path = new Path2D.Float();              float waisting = 0.5f;              float waistX = endX - arrowLength * 0.5f;             float waistY = arrowRatio * arrowLength * 0.5f * waisting;             float arrowWidth = arrowRatio * arrowLength;              path.moveTo ( veeX - arrowLength, -arrowWidth );             path.quadTo ( waistX, -waistY, endX, 0.0f );             path.quadTo ( waistX, waistY, veeX - arrowLength, arrowWidth );              // end of arrow is pinched in             path.lineTo ( veeX - arrowLength * 0.75f, 0.0f );             path.lineTo ( veeX - arrowLength, -arrowWidth );              g.setColor ( Color.BLUE );             g.fill ( path );              // move stem back a bit             g.setColor ( Color.RED );             g.draw ( new Line2D.Float ( 50.0f, 0.0f, veeX - arrowLength * 0.5f, 0.0f ) );         }     } } 
like image 34
Pete Kirkham Avatar answered Oct 04 '22 00:10

Pete Kirkham