1

I'm trying to draw an arrow in a circle(like a clock pointer), but I can't align the tip of the arrow with the rest of the line.

I made the "arrow" based on this answer, but I can't make it is correctly positioned with the line drawing.

The arrow is more to the left of the line, as follows in the image:

print screen

Here my mcve:

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class LineArrowTest extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPanel contentPane;
    private JPanel DrawPanel;

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new LineArrowTest().setVisible(true);
        });
    }

    public LineArrowTest() {
        initComponents();
        pack();
    }

    private void initComponents() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(400, 300));
        this.contentPane = new JPanel(new BorderLayout(0, 0));
        this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(this.contentPane);

        this.DrawPanel = new JPanel() {

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                LineArrow line = new LineArrow(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight(),
                        Color.black, 3);
                line.draw(g);
            }
        };
        this.contentPane.add(this.DrawPanel, BorderLayout.CENTER);
    }

    class LineArrow {

        int x;
        int y;
        int endX;
        int endY;
        Color color;
        int thickness;

        public LineArrow(int x, int y, int x2, int y2, Color color, int thickness) {
            super();
            this.x = x;
            this.y = y;
            this.endX = x2;
            this.endY = y2;

            this.color = color;
            this.thickness = thickness;
        }

        public void draw(Graphics g) {
            Graphics2D g2 = (Graphics2D) g.create();

            g2.setColor(color);
            g2.setStroke(new BasicStroke(thickness));
            g2.drawLine(x, y, endX, endY);
            ;
            drawArrowHead(g2);
            g2.dispose();
        }

        private void drawArrowHead(Graphics2D g2) {

            Polygon arrowHead = new Polygon();
            AffineTransform tx = new AffineTransform();

            arrowHead.addPoint(0, 5);
            arrowHead.addPoint(-5, -5);
            arrowHead.addPoint(5, -5);

            tx.setToIdentity();
            double angle = Math.atan2(endY - y, endX - x);
            tx.translate(endX, endY);
            tx.rotate(angle - Math.PI / 2d);

            g2.setTransform(tx);
            g2.fill(arrowHead);
        }

    }

}

1 Answers1

3

You are resetting the transformation.

Change AffineTransform tx = new AffineTransform(); to AffineTransform tx = g2.getTransform(); and remove the tx.setToIdentity(); call.

The below code was also rearranged to keep related statements together.

private void drawArrowHead(Graphics2D g2) {
    double angle = Math.atan2(endY - y, endX - x);
    AffineTransform tx = g2.getTransform();
    tx.translate(endX, endY);
    tx.rotate(angle - Math.PI / 2d);
    g2.setTransform(tx);

    Polygon arrowHead = new Polygon();
    arrowHead.addPoint(0, 5);
    arrowHead.addPoint(-5, -5);
    arrowHead.addPoint(5, -5);
    g2.fill(arrowHead);
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Not wanting to abuse your goodwill, but why does it occur? If I remove `this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));` the arrow becomes normal. – Blastoise Opressor Oct 09 '17 at 18:15
  • The border is exactly the problem. The border is 5 pixels wide. When drawing the component, the border is part of the component, so the coordinate (0, 0) would be "outside" the border. When drawing the line, the default Transformation of Graphics2D compensates for this effect by adding a translation of (5, 5) (= the width of the border). But when you set your Transformation, you first set it to the Identity, and then are missing this additional translation, leading to a mis-alignment of exactly the border width. – cello Oct 09 '17 at 18:22
  • Javadoc of [`Graphics2D`](https://docs.oracle.com/javase/8/docs/api/java/awt/Graphics2D.html): *When creating a Graphics2D object, the GraphicsConfiguration specifies the default transform for the target of the Graphics2D (a Component or Image). This default transform maps the user space coordinate system to screen and printer device coordinates such that the origin maps to the upper left hand corner of the target region of the device with increasing X coordinates extending to the right and increasing Y coordinates extending downward* – Andreas Oct 09 '17 at 18:23
  • @cello Interesting. I'm starting to delve into java2d now, did not know of the importance of the AffineTransform class in the construction of the component. Thanks for explanation. Thanks to Andreas to, for answer :D – Blastoise Opressor Oct 09 '17 at 23:20