0

I want to draw text around the top half of a circle using Java's Graphics2D. This can be used in Swing, but actually I want it to draw a circular logo in SVG with Apache's Batik.

Guy Smith
  • 178
  • 1
  • 12
  • [Drawing Geometric Primitives](https://docs.oracle.com/javase/tutorial/2d/geometry/primitives.html) would be a place to start – MadProgrammer Jan 09 '19 at 05:32
  • Possible duplicate [Drawing slices of a circle in java?](https://stackoverflow.com/questions/15167276/drawing-slices-of-a-circle-in-java/15168322#15168322); [Trying to draw an arc using Java](https://stackoverflow.com/questions/13426313/trying-to-draw-an-arc-using-java/13426402#13426402) – MadProgrammer Jan 09 '19 at 05:35

1 Answers1

0

The trick is to use a GlyphVector. Each letter must be individually moved and rotated.

public class HalfCircleDemo {

    @SuppressWarnings("serial")
    private static class DemoPanel extends JPanel { 

        public void paintComponent(Graphics g) {
            super.paintComponent(g);       
            Graphics2D graphics = (Graphics2D) g ; 
            graphics.setPaint(Color.yellow);
            graphics.fill(new Rectangle(0, 0, 100, 100));
            graphics.fill(new Rectangle(100, 100, 200, 200));
            graphics.setPaint(Color.BLACK);
            { /* THIS IS THE SECTION THAT DOES THE WORK  *******************************************/ 
                Font font = graphics.getFont(); 
                FontRenderContext frc = graphics.getFontRenderContext(); 
                GlyphVector glyphVector = font.createGlyphVector(frc, str); 
                int glyphCount = str.length(); 
                int radius = 50 ;
                for (int i=0 ; i<glyphCount ; i++) { 
                    double theta = Math.PI * i / (double) (glyphCount); 
                    AffineTransform transform = AffineTransform.getRotateInstance(theta-halfPi); 
                    Point2D.Double offset = new Point2D.Double(-radius*Math.cos(theta), -radius*Math.sin(theta)); 
                    glyphVector.setGlyphTransform(i, transform);
                    glyphVector.setGlyphPosition(i, offset);
                }
                graphics.drawGlyphVector(glyphVector, 100,  100);
            } /* ***********************************************************************************/ 
        }

        private static final String str = "Hello World, Hello World" ;
        private static final double halfPi = Math.PI / 2 ;

        public DemoPanel() {
            setBorder(BorderFactory.createLineBorder(Color.black));
        }

        public Dimension getPreferredSize() {
            return new Dimension(250,200);
        }  
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame f = new JFrame("Half-circle text demo");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.add(new DemoPanel());
                f.pack();
                f.setVisible(true); 
            }
        });
    }
}

There are some problems with the layout. These are fixed in the section of code below. First, in the code below, because the text is initially properly laid out (sensible letter spacing) in the GlyphVector, we use the GlyphVector's letter spacings. Second, the rotation of the last letter should be 180 degrees, so it lies flat, but its position should be less than 180 (otherwise it would be below the mid-circle). So, we compute two slightly different theta's. Thirdly, we compute the radius so the half-circle diameter is roughly the length of the original laid out text.

        Font font = graphics.getFont(); 
        FontRenderContext frc = graphics.getFontRenderContext(); 
        GlyphVector glyphVector = font.createGlyphVector(frc, str); 
        int glyphCount = str.length(); 
        double pixelLength = glyphVector.getPixelBounds(frc, 0, 0).width ; 
        double pixelLengthShort = glyphVector.getGlyphPosition(glyphCount-1).getX(); 
        double radius = pixelLength / Math.PI ;
        double halfPi = Math.PI / 2 ;
        for (int i=0 ; i<glyphCount ; i++) { 
            double glyphLinearOffset = glyphVector.getGlyphPosition(i).getX();
            double thetaRotation = Math.PI * glyphLinearOffset / (pixelLengthShort); 
            double thetaPosition = Math.PI * glyphLinearOffset / (pixelLength); 
            AffineTransform transform = AffineTransform.getRotateInstance(thetaRotation-halfPi); 
            Point2D.Double offsetVector = new Point2D.Double(-radius*Math.cos(thetaPosition), -radius*Math.sin(thetaPosition)); 
            glyphVector.setGlyphTransform(i, transform);
            glyphVector.setGlyphPosition(i, offsetVector);
        }
        graphics.drawGlyphVector(glyphVector, 100,  100);
Guy Smith
  • 178
  • 1
  • 12