12

How can one obtain something like in the photo bellow using Tikz?enter image description here

Look what I've got using tikz decoration library enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.fractals}

\begin{document}
\usetikzlibrary {decorations.fractals}
\begin{tikzpicture}[line width=20pt, decoration=Cantor set]
{ \sffamily \bfseries \Huge
\begin{scope}[rotate=90]
  \clip (0,.5) rectangle +(1,-4.85);
  \draw decorate{ (0,0) -- (3,0) } node[white] at (0.5,0) { C};
  \draw decorate{ decorate{ (0,-.75) -- (3,-.75) }} node[white] at (0.5,-0.75) {A};
  \draw decorate{ decorate{ decorate{ (0,-1.5) -- (3,-1.5) }}} node[white] at (0.5,-1.5) {N};
  \draw decorate{ decorate{ decorate{ decorate{ (0,-2.25) -- (3,-2.25) }}}} node[white] at (0.5,-2.25) {T};
  \draw decorate{ decorate{ decorate{ decorate{ decorate{ (0,-3) -- (3,-3) }}}}} node[white] at (0.5,-3) {O};
  \draw decorate{ decorate{ decorate{ decorate{ decorate{ decorate { (0,-3.75) -- (3,-3.75) }}}}}} node[white] at (0.5,-3.75) {R};
\end{scope}
}
\end{tikzpicture}
\end{document}

This solutions it seams to be of brute force type. I am asking you if there exists a different type of clipping to achieve this effect.

1
  • 1
    Should this only work with capital letters? If you include all letters that are smaller or have depth, the lines won't align as neat. Mar 21, 2023 at 20:14

3 Answers 3

10

Here's a recursive clipping solution.

It seems that starting with the fifth applications of the recursive calculation either the precision of TeX/PGFMath is not enough or my renderer just doesn't like it anymore.

For the row of Xs the resolution of the PDF to PNG convertes is already too low. I've added an enormous M to the examples instead.


The solution given below clips each letter individually.

Similar to dexteritas' answer it would be possible to install a clipping over the whole area but this doesn't increase the efficiency since the parts that are clipped away in A are also getting clipped away in N. Even though this solution needs a scope for each letter it doesn't appear to be any slower.

Code

\documentclass[varwidth, border=1pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc, chains}
\tikzset{
  cantor/start/.initial=0,
  cantor/picture/.style={
    start chain=going base right, node distance=+0mm, baseline=(chain-begin.base)},
  cantor/node/.style={
    text=black, inner sep=+0pt, outer sep=+0pt, node font=\sffamily\bfseries\Huge},
  cantor/cantor/.style n args={3}{insert path={
      coordinate (@1-#1) at ({$(#2)!.33333!(#3)$}-|#3)
      \ifnum#1=1 (#2) rectangle (@1-#1)
      \else      [cantor/cantor/.expanded={\pgfinteval{#1-1}}{#2}{@1-#1}] \fi
      coordinate (@2-#1) at ({$(#2)!.66667!(#3)$}-|#2)
      \ifnum#1=1 (@2-#1) rectangle (#3)
      \else      [cantor/cantor/.expanded={\pgfinteval{#1-1}}{@2-#1}{#3}] \fi}}}
\newcommand*\cantortext[2][]{%
  \begin{tikzpicture}[cantor/picture,#1]
  \foreach[count=\CNT from \pgfkeysvalueof{/tikz/cantor/start}]\LTR in{#2}{
    \node[cantor/node,on chain]{\phantom{\LTR}};
    \begin{scope}
      \ifnum\CNT>0 \clip[cantor/cantor={\CNT}{chain-end.south west}
                                             {chain-end.north east}];\fi
      \node[cantor/node]at(chain-end){\LTR};
    \end{scope}}
  \end{tikzpicture}}
\begin{document}
\Huge\bfseries\sffamily % just to have the same text in document as in TikZ
CANTOR\par
\cantortext{C, A, N, T, O, R}\par
\cantortext[cantor/start=3]{X, X, X, X, X, X}
%\cantortext[transform shape, scale=5, cantor/start=5]{M}
\end{document}

Output

enter image description here

enter image description here

0
10

I came up with this solution using plain pdfTeX. I didn't notice that you specified TikZ before I started working on this solution and by the time I had, I had already sunk some time into it. Sorry if this doesn't help answer your question.

Cantorized Cantor

%% Useful TeX macros
\bgroup
    \lccode`\?=`\p \lccode`\!=`\t
    \lowercase{\egroup\def\rmpt#1?!{#1}}
% \idim{<dimension expression>} -> the evaluation of the expression without a dimension
\def\idim#1{\expandafter\rmpt\the\dimexpr #1\relax}

\def\gobble#1{}

% \map<\macro>{<general text>} -> applies \macro to every group in <general text>
\long\def\map#1#2{\mapA{#1}#2\nul}
\long\def\mapA#1#2{%
    \ifx\nul#2%
        \let\next=\gobble%
    \else%
        #1{#2}%
        \let\next=\mapA%
    \fi%
    \next{#1}%
}

%% Helper macros for \cantorize

\def\cantorizeA#1#2#3#4{%
    \ifnum#1>0 %
        #2 #3 \cantorwd, #4 re
        \cantorizeA{\the\numexpr #1-1\relax}%
                  {\idim{#2pt + \cantorwd,pt}}%
                  {#3}%
                  {\idim{#4pt / 3}}%
        \cantorizeA{\the\numexpr #1-1\relax}%
                  {\idim{#2pt + \cantorwd,pt}}%
                  {\idim{#3pt + #4pt * 2 / 3}}%
                  {\idim{#4pt / 3}}%
    \fi%
}

% Typeset character with width \cantorwd,
\def\cantorsetwidth#1{\hbox to\cantorwd,pt{\hss#1\hss}}
\newcount\cantorlen
% Update cantorlen and cantorwd
\def\cantorgetlen#1{%
    \advance\cantorlen by 1\relax%
    \setbox0=\hbox{#1}%
    \ifdim\wd0>\cantorwd,pt\relax%
        \edef\cantorwd,{\idim{\wd0}}%
    \fi%
}
% vertical buffer around text in \cantorize
\def\cantorbuf,{.2}

\def\cantorize#1{{%
    \setbox0=\hbox{#1}%
    \def\cantorwd,{0}%
    \edef\cantorht,{\idim{\ht0 + \cantorbuf,pt * 2}}%
    \cantorlen=0\relax%
    \map\cantorgetlen{#1}%
    \quitvmode\pdfsave%
        \pdfliteral{.96859 0 0 .96859 0 0 cm \cantorizeA{\cantorlen}{0}{-\cantorbuf,}{\cantorht,} W n}%
        \rlap{\map\cantorsetwidth{#1}}%
    \pdfrestore%
    \hskip\cantorlen\dimexpr\cantorwd,pt\relax%
}}

%% Showcasing \cantorize

% Bigger font for showcasing the macro
\font\bigss=cmssbx10 at 50pt

{\bigss\cantorize{CANTOR}}

Instead of drawing white rectangles, this clips the text so you don't need to alter anything if you plan on having a background color as well.

6

Description

At first, each character is drawn in its own node. As I didn't find a way to invert the result of the cantor set decoration, I started from scratch and defined a recursive function \drawmiddleline. It draws white lines over the text. Its four parameters are described in the code. In the image below, I used different colors (from light red to dark red) to show how it is drawn step by step.

enter image description here

I saved the current level with \cslet{dml-#2}{\level} as the first recursive call would change the variable \level, that is still needed for the second recursive call.

To get the height of a letter without depth, I used:

\settoheight{\myheight}{\huge A}%

\myspacing defines the space between the character nodes.

Of course, you could change the font to something different, but I thought that's not the main problem here.

Result

enter image description here

Code

\documentclass[margin=2mm]{standalone}
\usepackage{etoolbox}
\usepackage{tikz}

\newcommand{\drawmiddleline}[4]{
    % #1 = char index
    % #2 = current level
    % #3 = current width
    % #4 = current yshift
    \ifnumequal{#2}{1}{
%       \pgfmathsetmacro{\c}{20*#1}
%       \draw[red!\c!white, line width=#3/3] ([yshift=#4] c#1.west) -- ([yshift=#4] c\n.east);
        \draw[white, line width=#3/3] ([yshift=#4] c#1.west) -- ([yshift=#4] c\n.east);
    }{
        \pgfmathsetmacro{\level}{int(#2-1)}
        \cslet{dml-#2}{\level}
        \drawmiddleline{#1}{\csuse{dml-#2}}{#3/3}{#4+#3/3}
        \drawmiddleline{#1}{\csuse{dml-#2}}{#3/3}{#4-#3/3}
    }
}

\newcommand{\cantortext}[1]{
    \foreach \char [count=\i from 0] in {#1} {
        \node[anchor=south west, inner sep=.3pt, font=\huge] (c\i) at (\i*\myspacing,0) {\char};
        \xdef\n{\i}
    }
    
    \foreach \i in {1, ..., \n} {
        \drawmiddleline{\i}{\i}{\the\myheight}{0pt}
    }
}

\begin{document}
\newlength{\myheight}%
\settoheight{\myheight}{\huge A}%
\newlength{\myspacing}%
\setlength{\myspacing}{5mm}%
\begin{tikzpicture}
    \cantortext{C,A,N,T,O,R}
\end{tikzpicture}
\end{document}

You must log in to answer this question.

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