【问题标题】:How to draw a directed arrow line in Java?如何在 Java 中绘制有向箭头线?
【发布时间】:2022-04-11 00:10:19
【问题描述】:

我想通过 Java 画一条有向箭头线。

目前我在使用java.awt.Line2D.Double类画线

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));

但只有直线出现,没有方向箭头出现。 BasicStroke.Join_BEVEL 用于绘制有向箭头。当两条线段相交时应用。

我正在绘制的线与矩形的边框相交,但未绘制有向箭头。只画了一条简单的线。

我有什么遗漏吗?

【问题讨论】:

    标签: java drawing line java-2d


    【解决方案1】:

    虽然 Pete 的帖子非常全面,但我还是使用这种方法绘制了一条非常简单的线,其末端有一个小三角形。

    // 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();
    }
    

    【讨论】:

    • 这对我很有用。非常感谢您提供更简单的解决方案!
    【解决方案2】:

    如果多段线中的线段具有特定角度,则在它们之间绘制斜角。如果您正在绘制一条恰好在具有某种颜色的其他一些像素附近绘制的线,则它没有任何意义 - 一旦您绘制了矩形,Graphics 对象就不知道该矩形,它(实际上)只保留像素。 (或者更确切地说,图像或操作系统窗口包含像素)。

    要绘制一个简单的箭头,请按照您的操作为茎杆画一条线,然后为 vee 画一条折线。更好看更好的箭头有弯曲的侧面并被填充。

    您可能不想为箭头使用斜面,因为斜面是平面;而是使用斜接选项:

    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 ) );
            }
        }
    }
    

    【讨论】:

    • 我不明白你是怎么用这个的。
    【解决方案3】:

    这是我的方法,仅限绝对数学:

    /**
     * Draw an arrow line between two points.
     * @param g the graphics component.
     * @param x1 x-position of first point.
     * @param y1 y-position of first point.
     * @param x2 x-position of second point.
     * @param y2 y-position of second point.
     * @param d  the width of the arrow.
     * @param h  the height of the arrow.
     */
    private void drawArrowLine(Graphics g, int x1, int y1, int x2, int y2, int d, int h) {
        int dx = x2 - x1, dy = y2 - y1;
        double D = Math.sqrt(dx*dx + dy*dy);
        double xm = D - d, xn = xm, ym = h, yn = -h, x;
        double sin = dy / D, cos = dx / D;
    
        x = xm*cos - ym*sin + x1;
        ym = xm*sin + ym*cos + y1;
        xm = x;
    
        x = xn*cos - yn*sin + x1;
        yn = xn*sin + yn*cos + y1;
        xn = x;
    
        int[] xpoints = {x2, (int) xm, (int) xn};
        int[] ypoints = {y2, (int) ym, (int) yn};
    
        g.drawLine(x1, y1, x2, y2);
        g.fillPolygon(xpoints, ypoints, 3);
    }
    

    【讨论】:

      【解决方案4】:

      过去,我编写了以下方法来创建一个箭头形状,然后我可以用 ((Graphics2D) g).fill(shape); 进行填充

      public static Shape createArrowShape(Point fromPt, Point toPt) {
          Polygon arrowPolygon = new Polygon();
          arrowPolygon.addPoint(-6,1);
          arrowPolygon.addPoint(3,1);
          arrowPolygon.addPoint(3,3);
          arrowPolygon.addPoint(6,0);
          arrowPolygon.addPoint(3,-3);
          arrowPolygon.addPoint(3,-1);
          arrowPolygon.addPoint(-6,-1);
      
      
          Point midPoint = midpoint(fromPt, toPt);
      
          double rotate = Math.atan2(toPt.y - fromPt.y, toPt.x - fromPt.x);
      
          AffineTransform transform = new AffineTransform();
          transform.translate(midPoint.x, midPoint.y);
          double ptDistance = fromPt.distance(toPt);
          double scale = ptDistance / 12.0; // 12 because it's the length of the arrow polygon.
          transform.scale(scale, scale);
          transform.rotate(rotate);
      
          return transform.createTransformedShape(arrowPolygon);
      }
      
      private static Point midpoint(Point p1, Point p2) {
          return new Point((int)((p1.x + p2.x)/2.0), 
                           (int)((p1.y + p2.y)/2.0));
      }
      

      【讨论】:

        【解决方案5】:

        以防万一您想要 Fast 方式的 非编程 箭头(即用于文本目的),您可以使用 @987654322 @ 将箭头作为文本的代码,只需将您的 HTML 代码放在组件的 .setText() 方法中。我有 java 1.8u202 它工作正常。

        myLabel.setText("<html><body>←</body></html>");

        此代码← 用于向左箭头

        来自This Website的其他箭头方向HTML代码

        【讨论】:

        • 好主意!您可以设置它们的样式等:
        【解决方案6】:
            void drawArrow(Graphics g1, double x1, double y1, double x2, double y2 ) {
            Graphics2D ga = (Graphics2D) g1.create();
            ga.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
        
            double l = Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));//  line length
            double d = l / 10; // arrowhead distance from end of line. you can use your own value.
            
            double newX = ((x2 + (((x1 - x2) / (l) * d)))); // new x of arrowhead position on the line with d distance from end of the line.
            double newY = ((y2 + (((y1 - y2) / (l) * d)))); // new y of arrowhead position on the line with d distance from end of the line.
        
            double dx = x2 - x1, dy = y2 - y1;
            double angle = (Math.atan2(dy, dx)); //get angle (Radians) between ours line and x vectors line. (counter clockwise)
            angle = (-1) * Math.toDegrees(angle);// cconvert to degree and reverse it to round clock for better understand what we need to do.
            if (angle < 0) {
                angle = 360 + angle; // convert negative degrees to posative degree
            }
            angle = (-1) * angle; // convert to counter clockwise mode
            angle = Math.toRadians(angle);//  convert Degree to Radians
            AffineTransform at = new AffineTransform();
            at.translate(newX, newY);// transport cursor to draw arrowhead position.
            at.rotate(angle);
            ga.transform(at);
        
            Polygon arrowHead = new Polygon();
            arrowHead.addPoint(5, 0);
            arrowHead.addPoint(-5, 5);
            arrowHead.addPoint(-2, -0);
            arrowHead.addPoint(-5, -5);
            ga.fill(arrowHead);
            ga.drawPolygon(arrowHead);
        }
        

        【讨论】:

          【解决方案7】:

          这是来自 Vicente Reig 的出色答案的代码,稍微简化并打包为一个不错的实用程序类。

          import java.awt.*;
          import java.awt.geom.AffineTransform;
          
          public class Arrow
          {
              private final Polygon arrowHead = new Polygon ();
          
              /**
               * Create an arrow.
               *
               * @see https://stackoverflow.com/questions/2027613/how-to-draw-a-directed-arrow-line-in-java
               *
               * @param size Size of the arrow to draw.
               */
              public Arrow (int size)
              {
                  // create a triangle centered on (0,0) and pointing right
                  arrowHead.addPoint (size, 0);
                  arrowHead.addPoint (-size, -size);
                  arrowHead.addPoint (-size, size);
                  //arrowHead.addPoint (0, 0); // Another style
              }
          
              /**
               * Draw the arrow at the end of a line segment. Drawing the line segment must be done by the caller, using whatever
               * stroke and color is required.
               */
              public void drawArrowHead (Graphics2D g, double x0, double y0, double x1, double y1)
              {
                  final AffineTransform tx = AffineTransform.getTranslateInstance (x1, y1);
                  tx.rotate (Math.atan2 (y1 - y0, x1 - x0));
                  g.fill (tx.createTransformedShape (arrowHead));
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2022-01-13
            • 1970-01-01
            • 2017-09-17
            • 2018-07-30
            • 1970-01-01
            • 2018-04-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多