7

I'm interested in creating this type of shape for text:

Enter image description here

How can I fit/scale some text to an upper-bound function?

I imagine a newcommand like \funcText[function]{Text}, but I am quickly stuck (question of dimension of the text to match the domain of the function + actually scaling the letter according to the function itself).

Research so far

This previous question where text was fitting into some shapes as in the Calligrams by Guillaume Apollinaire. Based on this famous answer,

draw-text-in-different-shapes

Examples

I imagine I could, for instance, apply the method to use a normal curve with text below.

Enter image description here

\documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{arrows.meta}
\usepackage{mathtools}
\begin{document}

\tikzset{
    myfonction/.style={
        >={Stealth[length=6pt]},
    },
}

\begin{tikzpicture}[
        myfonction,
        declare function={
            f(\x)=2*exp(-\x*\x/3);
            xmax=3.5;
            xmin=-3.4;
            x0=1.5;
            ymax=2.75;
        },
    ]

    \draw[thick] plot[domain=xmin:xmax,samples=51,smooth] (\x,{f(\x)});

\end{tikzpicture}
\end{document}

1 Answer 1

14

This is a proof of concept. It may need tweaking. The basic idea is to use the xstring package to count the number (\strlen) of characters in the text (UTF-8 is ok—see below) and then place and scale each one individually as a node. Horizontal placement is determined (like a Riemann sum) by calculating (xmax-xmin)/strlen. Vertical scaling is determined by the function (also like a Riemann sum).

\functext[red]{2*exp(-\x*\x/3)}{-3.5}{3.5}{HELLOWORLD!}

enter image description here

\functext[blue,font=\sffamily\bfseries]{\x*\x/8+1}{-3.5}{3.5}{TEXT UNDER A PARABOLA!}

enter image description here

The general call is

\functext[<tikz options>]{<function>}{<xmin>}{<xmax>}{<text>}

Here is the code:

\documentclass{article}

\usepackage{tikz,xstring}

\newcommand{\xfactor}{25}
\newcommand{\yfactor}{3.9}

\newcommand{\functext}[5][]{\StrLen{#5}[\strlen]\begin{tikzpicture}
    \draw[ultra thin, domain=#3:#4, smooth, variable=\x] plot (\x, {#2});
    \draw[ultra thin] (#3,0)--(#4,0);
    \foreach \n[evaluate=\n as \xt using #3+(#4-#3)/\strlen*(\n-.5),
        evaluate=\n as \x using \xt,
        evaluate={\yt=#2}]
        in {1,...,\strlen}{
    \node[inner sep=0pt,anchor=base,yscale=\yt*\yfactor,xscale=\xfactor/\strlen,#1] at (\xt,0) {\StrChar{#5}{\n}}; 
    }
    \end{tikzpicture}}

\begin{document}

\functext[red]{2*exp(-\x*\x/3)}{-3.5}{3.5}{HELLOWORLD!}

\vspace{1cm}
\functext[blue,font=\sffamily\bfseries]{\x*\x/8+1}{-3.5}{3.5}{TEXT UNDER A PARABOLA!}

\end{document}

The two global constants \xfactor and \yfactor control the width and height of the letters. Larger \xfactor makes the letters wider (overlapping if too large). Larger \yfactor makes the letters taller relative to the function (crossing it if too large). If you want space between the letters and the function, decrease this value. Font changes may require adjustment of these factors.

You can delete the first two \draw commands in the macro if you don't want the curve (and x-axis) drawn.

Lowercase letters are fine but they won't reach the function (except f, d, etc.). UTF-8 characters require \usepackage[T1]{fontenc} in the preamble, and accented uppercase letters will likely extend above the function unless you reduce \yfactor.

The same technique works to draw text between two curves. We define a new macro \funcstext that takes 6 arguments (one optional):

\funcstext[<tikz options>]{<function1>}{<function2>}{<xmin>}{<xmax>}{<text>}

\funcstext[green!50!black,font=\sffamily\bfseries]{2*exp(-\x*\x/3)}{-\x*\x/8}{-3.5}{3.5}{TEXT BETWEEN TWO CURVES!}

enter image description here

Here is the code for the macro:

\newcommand{\funcstext}[6][]{\StrLen{#6}[\strlen]\begin{tikzpicture}
    \draw[ultra thin, domain=#4:#5, smooth, variable=\x] plot (\x, {#2});
    \draw[ultra thin, domain=#4:#5, smooth, variable=\x] plot (\x, {#3});
    \foreach \n[evaluate=\n as \xt using #4+(#5-#4)/\strlen*(\n-.5),
        evaluate=\n as \x using \xt,
        evaluate={\yt=#2-#3},
        evaluate={\yb=#3}]
        in {1,...,\strlen}{
    \node[inner sep=0pt,anchor=base,yscale=\yt*\yfactor,xscale=\xfactor/\strlen,#1] at (\xt,\yb) {\StrChar{#6}{\n}}; 
    }
    \end{tikzpicture}}

This works even if the functions intersect, with possibly desirable consequences:

\funcstext[orange,font=\sffamily\bfseries]{2*exp(-\x*\x/3)}{\x*\x/8}{-3.5}{3.5}{CAN I DRAW TEXT ON A MöBIUS STRIP?}

enter image description here

4
  • 1
    Wow! Very nice implementation, Sandy! +1
    – SebGlav
    Commented Mar 24, 2022 at 10:00
  • Very nice indeed !
    – JeT
    Commented Mar 24, 2022 at 11:02
  • @SebGlav I follow you and know you're a pgfkey expert. May I ask you how you would turn the 6-parameter macro into a probably smoother key command ? That would be a great second answer.
    – JeT
    Commented Mar 24, 2022 at 19:08
  • 1
    @JeT Thanks for your kind words, but I'm no expert at all ;) You could ask this as a new question, and receive answers from many great helpers.
    – SebGlav
    Commented Mar 24, 2022 at 20:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .