Comment dessiner une ligne de flèche dirigée en Java?

je veux tracer une ligne de flèche dirigée à travers Java.

actuellement, j'utilise la classe java.awt.Line2D.Double pour tracer une ligne

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

mais seule la ligne apparaît et aucune flèche dirigée n'apparaît. BasicStroke.Join_BEVEL est utilisé pour tirer une flèche. Il est appliqué lorsque deux segments de ligne se rencontrent.

la ligne que je dessine rencontre la bordure d'un rectangle mais aucune flèche dirigée n'est dessinée. Seule une ligne simple est tracée.

y a-t-il quelque chose que je manque?

22
demandé sur Machavity 2010-01-08 15:50:03

4 réponses

le biseau est tiré entre des segments dans un polyligne s'ils sont à certains angles. Il n'a aucun rapport si vous dessinez une ligne qui se trouve être tracée près d'autres pixels qui sont d'une certaine couleur - une fois que vous avez dessiné le rectangle, l'objet graphique ne sait pas au sujet du rectangle, il (en effet) tient seulement les pixels. ( ou plutôt la fenêtre image ou OS contient les pixels).

pour dessiner une flèche simple, tracer une ligne pour la tige comme vous le faites, puis une polyligne pour les vé. Les flèches plus belles ont les côtés courbés et sont remplies.

vous ne voulez probablement pas utiliser de biseau pour la tête de flèche, car les biseaux sont un plat; utilisez plutôt l'option mitre:

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 ) );
        }
    }
}
28
répondu Pete Kirkham 2010-01-08 13:54:16

bien que le post de Pete soit très complet, j'utilise cette méthode pour tracer une ligne très simple avec un petit triangle à son extrémité.

// 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();
}
24
répondu Vicente Reig 2010-06-22 19:52:50

dans le passé, j'ai écrit la méthode suivante pour créer une forme de flèche, que je peux ensuite remplir avec ((Graphics2D) g).de remplissage(forme);

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));
}
13
répondu Michael Zlatkovsky - Microsoft 2011-03-26 23:59:06

C'est mon approche, mathématiques absolues seulement:

/**
 * 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);
}
12
répondu phibao37 2017-10-23 09:20:02