Как нарисовать линию направленной стрелки в Java? - PullRequest
24 голосов
/ 08 января 2010

Я хочу нарисовать линию направленной стрелки через 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 используется для рисования направленной стрелки. Применяется, когда встречаются два отрезка.

Линия, которую я рисую, пересекает границу прямоугольника, но направленная стрелка не рисуется. Нарисована только простая линия.

Есть что-то, что я пропускаю?

Ответы [ 5 ]

29 голосов
/ 08 января 2010

Скос рисуется между сегментами полилинии, если они находятся под определенными углами. Это не имеет никакого отношения, если вы рисуете линию, которая оказывается нарисованной рядом с некоторыми другими пикселями, которые имеют определенный цвет - как только вы нарисовали прямоугольник, объект Graphics не знает о прямоугольнике, это (в действительности) содержит только пиксели. (точнее, изображение или окно ОС содержит пиксели).

Чтобы нарисовать простую стрелку, нарисуйте линию для стебля, как вы делаете, затем полилинию для VE. Красивые стрелки выглядят лучше, имеют изогнутые стороны и заполнены.

Вы, вероятно, не хотите использовать скос для наконечника стрелы, поскольку скосы - это плоскость; вместо этого используйте параметр митры:

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 ) );
        }
    }
}
27 голосов
/ 22 июня 2010

Несмотря на то, что сообщение Пита является чрезвычайно исчерпывающим, я использую этот метод, чтобы нарисовать очень простую линию с маленьким треугольником в конце.

// 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();
}
14 голосов
/ 13 декабря 2014

Это мой подход, только абсолютная математика:

/**
 * 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);
}
14 голосов
/ 27 марта 2011

В прошлом я писал следующий метод для создания формы стрелки, которую затем можно заполнить ((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));
}
0 голосов
/ 19 февраля 2019

На всякий случай, если вам нужна непрограммируемая стрелка (т. Е. Для текстовых целей) Быстрая , вы можете использовать <html> код для делая стрелку как текст, просто поместите ваш HTML-код в метод .setText() для компонента. У меня java 1.8u202 работает нормально.

myLabel.setText("<html><body>&#8592;</body></html>");

этот код &#8592; предназначен для стрелки влево

другие направления стрелок HTML-код этого веб-сайта

...