3

I'm trying to write a funny style writing targeting a young audience. At some point, I would like some words to have "wave" effect on each character according to their position.

I've already found this article that use pgf/TikZ to simulate WordArt, but I would like to be able to do it without pgf.

So the idea should be to be able to write something like this

I can write a \bubbletext{bubble-like} wavy text.

So far, based on this example, I was able to at least get a randomized effect. My problem comes from the fact that I would like something more consistant.

\ExplSyntaxOn

\NewDocumentCommand{\bubblechar}{ O{0.25} m }
{
    \group_begin:
    \tl_set:Nn \l_tmpa_tl { #2 }
    \tl_replace_all:Nnn \l_tmpa_tl { ~ } { \c_space_tl }
    \tl_map_inline:Nn \l_tmpa_tl { 
        \int_case:nnF { \int_mod:nn {\int_rand:nn{0}{400}} {4} }
        {
            {1} {\raisebox { #1 ex} { ##1 } }
            {3} {\raisebox {-#1 ex} { ##1 } }
        }
        {##1}
    }
    \group_end:
}

\ExplSyntaxOff

To achieve that, I have to replace the \int_rand:nn{0}{400} with the actual position I currently am in the list, but I don't find how to reach that value. So basically, the effect I want will have a similar effect that if I had writen this line

I can write a b\raisebox{-0.25ex}ub\raisebox{0.25ex}bl\raisebox{-.25ex}e-\raisebox{0.25ex}li\raisebox{-0.25ex}ke wavy text.

With the 400, I'm sometime lucky, but it's not always the case (far from it). (In the following example, it was pretty close / almost perfect, but most of the time, the effect is barely visible.)

Expected result vs reality based on luck

Thanks in advance.

1 Answer 1

3

Instead of randomizing the shift the following uses an integer to track the offset that should be applied to each element in the token list. I also added a step between base line and top/bottom aligned with 0.707 times the displacement (roughly cos(pi/4), might change it to your liking).

\documentclass{article}

\ExplSyntaxOn
\keys_define:nn { wavychars }
  {
     displace    .tl_set:N   = \l__wavychars_displace_tl
    ,displace    .initial:n  = 0.25ex
    ,halfstep    .fp_set:N   = \l__wavychars_halfstep_fp
    ,halfstep    .initial:n  = 0.707
    ,no-halfstep .bool_set:N = \l__wavychars_no_halfstep_bool
    ,unknown     .code:n     =
      \tl_set:Nx \l__wavychars_displace_tl { \l_keys_key_str }
  }
\tl_new:N \l__wavychars_input_tl
\int_new:N \l__wavychars_state_int
\cs_new:Npn \__wavychars_halfstep:
  { \fp_eval:n { \l__wavychars_displace_tl * \l__wavychars_halfstep_fp } pt }
\cs_new_protected:Npn \__wavychars_with_halfstep:n #1
  {
    \tl_if_eq:nnTF { \c_space_tl } {#1}
      { \c_space_tl }
      {
        \int_incr:N \l__wavychars_state_int
        \int_case:nn \l__wavychars_state_int
          {
            1 {#1}
            2 { \raisebox { \__wavychars_halfstep: }     {#1} }
            3 { \raisebox { \l__wavychars_displace_tl }  {#1} }
            4 { \raisebox { \__wavychars_halfstep: }     {#1} }
            5 {#1}
            6 { \raisebox { -\__wavychars_halfstep: }    {#1} }
            7 { \raisebox { -\l__wavychars_displace_tl } {#1} }
            8 {
                \raisebox { -\__wavychars_halfstep: }    {#1}
                \int_zero:N \l__wavychars_state_int
              }
          }
      }
  }
\cs_new_protected:Npn \__wavychars_no_halfstep:n #1
  {
    \tl_if_eq:nnTF { \c_space_tl } {#1}
      { \c_space_tl }
      {
        \int_incr:N \l__wavychars_state_int
        \int_case:nn \l__wavychars_state_int
          {
            1 {#1}
            2 { \raisebox { \l__wavychars_displace_tl }  {#1} }
            3 {#1}
            4 {
                \raisebox { -\l__wavychars_displace_tl } {#1}
                \int_zero:N \l__wavychars_state_int
              }
          }
      }
  }
\cs_new_protected:Npn \wavychars:nn #1#2
  {
    \group_begin:
      \keys_set:nn { wavychars } {#1}
      % though \text_expand:n should be preferred for user input, the following
      % loop wouldn't work anyway with input containing argument grabbing macros
      \tl_set:Nx \l__wavychars_input_tl { #2 }
      \tl_replace_all:Nnn \l__wavychars_input_tl { ~ } { \c_space_tl }
      \int_zero:N \l__wavychars_state_int
      \bool_if:NTF \l__wavychars_no_halfstep_bool
        {
          \tl_map_function:NN \l__wavychars_input_tl \__wavychars_no_halfstep:n
        }
        {
          \tl_map_function:NN \l__wavychars_input_tl
            \__wavychars_with_halfstep:n
        }
    \group_end:
  }
\NewDocumentCommand \wavychars { O{} m } { \wavychars:nn {#1} {#2} }
\ExplSyntaxOff

\begin{document}
\wavychars{This is in wavy style} and this is normal style.

\wavychars[halfstep=0.5]{This is in wavy style} and this is normal style.

% short syntax for displacement
\wavychars[1ex]{This is in wavy style} and this is normal style.

% short syntax doesn't work if displacement contains a macro
\newcommand*\mydisplacement{1ex}
\wavychars[displace=\mydisplacement]{This is in wavy style} and this is normal style.

\wavychars[no-halfstep]{This is in wavy style} and this is normal style.
\end{document}

enter image description here

1
  • I don't know why I didn't think to create my own position counter, but that's perfect. I also kept the idea for the keys, it may come handy. And your answer is even more complete than what I really needed, but it may help other in the future. Thanks a lot. Commented Aug 23, 2022 at 12:44

You must log in to answer this question.

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