In [1]:
%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import numpy as np
import random
import string
import math
import re

1 font point is $\frac{1}{72}$ of an inch

In [2]:
height = 1
width = 1

plt.figure(figsize=(width, height))
plt.axis([0, width, 0, height])
plt.text(0, 0, "A", fontsize=72)
Out[2]:
<matplotlib.text.Text at 0x1130002b0>

But what about width? Very hard with most fonts. Need monospace font

In [3]:
height = 1
width = 0.8
font = FontProperties()
font.set_family("monospace")

plt.figure(figsize=(width, height))
plt.axis([0, width, 0, height])
plt.text(0, 0, "T", fontproperties = font, fontsize=72)
Out[3]:
<matplotlib.text.Text at 0x113010f60>

It looks like the width is just about $\frac{4}{5}$ of the height.

Let's draw 100 random words on the screen.

In [4]:
height = 30
width = 30
font = FontProperties()
font.set_family("monospace")

plt.figure(figsize=(width, height))
plt.axis([0, width, 0, height])
for i in range(100):
    text = "ABC"
    size = 20 + random.random() * 210
    x = random.random() * width
    y = random.random() * height
    plt.text(x, y, text, fontproperties = font, fontsize=size)

To make them stop overlapping, we need to record where they are.

We will use an object for the Word, and calculate the bounding box.

In [5]:
class Word:
    WIDTHSCALE = 0.8
    PTSIZE = 1 / 72
    
    def __init__(self, x, y, text, size):
        self.x = x
        self.y = y
        self.text = text
        self.size = size
        
    def right(self):
        return self.x + len(self.text) * Word.WIDTHSCALE * self.size * Word.PTSIZE
        
    def top(self):
        return self.y + self.size * Word.PTSIZE
        
    def overlap(self, other):
        # http://gamemath.com/2011/09/detecting-whether-two-boxes-overlap/
        if self.right() < other.x:
            return False
        if other.right() < self.x:
            return False
        if self.top() < other.y:
            return False
        if other.top() < self.y:
            return False
        return True

First algorithm. Keep generating new positions until they fit.

In [6]:
height = 30
width = 30
font = FontProperties()
font.set_family("monospace")

plt.figure(figsize=(width, height))
plt.axis([0, width, 0, height])

words = []
for i in range(100):
    text = "ABC"
    overlap = True
    while overlap:
        overlap = False
        
        x = random.random() * width
        y = random.random() * height
        size = 20 + random.random() * 210

        w = Word(x, y, text, size)
        for ow in words:
            if w.overlap(ow):
                overlap = True
                break
                
    words.append(w)
    
for w in words:
    plt.text(w.x, w.y, w.text, fontproperties = font, fontsize=w.size)