1

I am working on a compass in java. First I created a UI prototyp for my compass and created a working SSCCE of it, where you can click on the panel and the needle is pointed to another direction, which differs about 15 degree. This is the code:

public class TestFrame extends JFrame {

    public TestFrame() {
        initComponents();
    }

    public void initComponents() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocation(new Point((int) (Toolkit.getDefaultToolkit().getScreenSize().width / 2) - 400, (int) (Toolkit.getDefaultToolkit().getScreenSize().height / 2) - 250));
        setSize(500, 500);
        setVisible(true);
        CompassPanel c = new CompassPanel();
        add(c, BorderLayout.CENTER);

    }

    public class CompassPanel extends JPanel {
        Image bufImage;
        Graphics bufG;
        private int circleX, circleY, circleRadius;
        private int[] xPoints, yPoints;
        private double rotationAngle = Math.toRadians(0);

        public CompassPanel() {
            setVisible(true);

            addMouseListener(new MouseListener() {
                @Override
                public void mouseReleased(MouseEvent e) {
                }

                @Override
                public void mousePressed(MouseEvent e) {
                }

                @Override
                public void mouseExited(MouseEvent e) {
                }

                @Override
                public void mouseEntered(MouseEvent e) {
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    rotationAngle = rotationAngle + Math.toRadians(15);
                    repaint();
                }
            });

        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            circleRadius = (int) (getWidth() * 0.7);
            circleX = 50;
            circleY = 50;

            g2d.setColor(Color.BLACK);
            for (int angle = 0; angle <= 360; angle += 5) {
                double sin = Math.sin(Math.toRadians(angle));
                double cos = Math.cos(Math.toRadians(angle));
                int x1 = (int) ((circleX + circleRadius / 2) - cos  * (circleRadius * 0.37) - sin * (circleRadius * 0.37));
                int y1 = (int) ((circleY + circleRadius / 2) + sin  * (circleRadius * 0.37) - cos * (circleRadius * 0.37));
                g2d.setColor(Color.BLACK);
                g2d.drawLine(x1, y1, (circleX + circleRadius / 2), (circleY + circleRadius / 2));
            }

            g2d.setFont(new Font("Arial", Font.BOLD, 11));
            g2d.drawString("WEST", circleX - 45, circleY + circleRadius / 2 + 4);
            g2d.drawString("EAST", circleX + circleRadius + 13, circleY + circleRadius / 2 + 4);
            g2d.drawString("NORTH", circleX + circleRadius / 2 - 14, circleY - 15);
            g2d.drawString("SOUTH", circleX + circleRadius / 2 - 14, circleY + circleRadius + 25);
            g2d.setColor(Color.WHITE);
            g2d.fillOval(circleX, circleY, circleRadius, circleRadius);
            g2d.setColor(Color.BLACK);
            g2d.drawOval(circleX, circleY, circleRadius, circleRadius);

            xPoints = new int[] { (int) (circleX + circleRadius / 2),
                    (int) (circleX + circleRadius * 0.25),
                    (int) (circleX + circleRadius / 2),
                    (int) (circleX + circleRadius * 0.75) };

            yPoints = new int[] { (int) (circleY + 30),
                    (int) (circleY + circleRadius * 0.85),
                    (int) (circleY + circleRadius * 0.6),
                    (int) (circleY + circleRadius * 0.85) };

            Polygon fillPoly = new Polygon(xPoints, yPoints, 4);
            Polygon outerPoly = new Polygon(xPoints, yPoints, 4);

            int rotationX = circleX + (circleRadius / 2);
            int rotationY = circleX + (circleRadius / 2);

            g2d.setColor(Color.green);
            g2d.fillOval(rotationX, rotationY, 5, 5);

            AffineTransform a = g2d.getTransform().getRotateInstance(rotationAngle, rotationX, rotationY);
            g2d.setTransform(a);

            g2d.setColor(Color.RED);
            g2d.fillPolygon(fillPoly);

            g2d.setColor(Color.black);
            g2d.draw(outerPoly);
        }

        @Override
        public void update(Graphics g) {
            int w = this.getSize().width;
            int h = this.getSize().height;

            if (bufImage == null) {
                bufImage = this.createImage(w, h);
                bufG = bufImage.getGraphics();
            }

            bufG.setColor(this.getBackground());
            bufG.fillRect(0, 0, w, h);
            bufG.setColor(this.getForeground());

            paint(bufG);
            g.drawImage(bufImage, 0, 0, this);
        }

        public void setRotationAngle(int angle) {
            rotationAngle = angle;
        }
    }

    public static void main(String[] args) {
        new TestFrame();
    }
}

When I implement this panel into my application, the needle is not drawn as it is in the SSCCE. It is drawn a bit higher and on the left of the position it is meant to be. When I click on the panel and rotate the needle, the rotation works fine and the needle is painted where it belongs.

I add the panel like this to my application. The sensorPanel is a JPanel in a TabbedPane.

public JPanel createSensorPanel(){
    sensorPanel = new JPanel(new MigLayout("fill, insets 3"));
    CompassPanel compassPanel = new CompassPanel();
    sensorPanel.add(compassPanel, "wrap");
    return sensorPanel;
}

Why is the Polygon of the needle not drawn at the position it is drawn in the SSCCE?

EDIT:

Here is a picture of the problem. picture

htz
  • 1,037
  • 1
  • 13
  • 37
  • Could you post a screenshot with the problem..? – Sorceror Jan 03 '13 at 19:26
  • `setLocation(new Point((int) (Toolkit.getDefaultToolkit().getScreenSize().width / 2) - 400, (int) (Toolkit.getDefaultToolkit().getScreenSize().height / 2) - 250)); setSize(500, 500);` Replace that lot with `setLocationByPlatform(true);` The result can be seen [here](http://stackoverflow.com/a/7143398/418556). – Andrew Thompson Jan 03 '13 at 23:57

1 Answers1

2

If you look closer what's going on on your image, than you'll see, that affine transform is the problem.

enter image description here

Looks like that affine transforms are related to top parent not the current child component (see java Affine Transform correct order).

The quick and dirty solution would be to translate the arrow as the parents are translated (based on this answer)

AffineTransform a = g2d.getTransform().
    getRotateInstance(rotationAngle, rotationX, rotationY)
    .translate(parent.getX(), parent.getY()) // use instance of real parent instead
    .translate(tabpane.getX(), tabpane.getY()); // use instance of JTabbedPane instead

But much more convenient would be some common solution, something like this one.

Community
  • 1
  • 1
Sorceror
  • 4,835
  • 2
  • 21
  • 35