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)=2exp(-\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}

JeT
  • 3,020

1 Answers1

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]{2exp(-\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

Sandy G
  • 42,558
  • 1
    Wow! Very nice implementation, Sandy! +1 – SebGlav Mar 24 '22 at 10:00
  • Very nice indeed ! – JeT Mar 24 '22 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 Mar 24 '22 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 Mar 24 '22 at 20:13