Java graphics random text

前端 未结 2 550
忘了有多久
忘了有多久 2021-01-22 07:26

I\'m trying to draw random text with fontSize based on word frequency on the BufferedImage using the drawString(word, x, y) with random x,y values. Unf

2条回答
  •  一生所求
    2021-01-22 08:19

    First, fonts are drawn around the "base line", that is, the y position represents the baseline, so the text can grow above it and below it...

    So, instead of using -textheight, you should be using FontMetrics#getAscent

    Second, you need to keep track of all the previous locations text was painted, you could do this by using FontMetrics#getStringBounds, which returns a Rectangle2D and simply keep a list of this, which you can iterate over to check if the new text intersects any other, as an example...

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private BufferedImage img;
    
            public TestPane() {
                img = new BufferedImage(1200, 650, BufferedImage.TYPE_INT_ARGB);
                try (BufferedReader br = new BufferedReader(new FileReader(new File("Words.txt")))) {
                    List words = new ArrayList<>(25);
                    String text = null;
                    System.out.println("Read");
                    while ((text = br.readLine()) != null) {
                        words.add(text);
                    }
                    System.out.println("Loaded " + words.size());
    
                    Collections.sort(words);
    
                    Random rnd = new Random();
                    Font font = getFont();
                    Graphics2D g2d = img.createGraphics();
                    g2d.setColor(Color.WHITE);
                    g2d.fillRect(0, 0, 1200, 650);
    
                    List used = new ArrayList<>(25);
                    for (String word : words) {
                        int size = rnd.nextInt(37) + 11;
                        Font drawFont = font.deriveFont((float) size);
                        FontMetrics fm = g2d.getFontMetrics(drawFont);
                        Rectangle2D bounds = fm.getStringBounds(word, g2d);
                        System.out.println("Positing " + word);
                        do {
                            int xPos = rnd.nextInt(1200 - (int)bounds.getWidth());
                            int yPos = rnd.nextInt(650 - (int)bounds.getHeight());
    
                            bounds.setFrame(xPos, yPos, bounds.getWidth(), bounds.getHeight());
                        } while (collision(used, bounds));
                        used.add(bounds);
                        g2d.setFont(drawFont);
                        g2d.setColor(Color.BLACK);
                        g2d.drawString(word, (float)bounds.getX(), (float)bounds.getY() + fm.getAscent());
                        g2d.setColor(Color.RED);
                        g2d.draw(bounds);
                    }
                    g2d.dispose();
                } catch (IOException exp) {
                    exp.printStackTrace();
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(1200, 650);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.drawImage(img, 0, 0, this);
                g2d.dispose();
            }
    
            protected boolean collision(List used, Rectangle2D bounds) {
                boolean collides = false;
                for (Rectangle2D check : used) {
                    if (bounds.intersects(check)) {
                        collides = true;
                        break;
                    }
                }
                return collides;
            }
    
        }
    
    }
    

    The red rectangles are just for demonstration purposes and you can get rid of those.

    This does, however, demonstrate a small problem, not all the text fills all of the rectangle.

    A more complex solution would be to use a TextLayout to generate a Shape of the text, which would allow words to be grouped much closed together.

    Have a look at Assigning a image to a String for a demonstration of how this can be generated

提交回复
热议问题