Is there a better way to check divisibility in LaTeX?

1,125

Solution 1

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{multido}
\makeatletter
\newcommand\Ruler[2]{%
  \pstFPMul\Start{#1}{10}%
  \pstFPMul\Stop{#2}{10}%
  \def\Width{\numexpr\Stop-\Start\relax}%
  \psset{xunit=0.1\psxunit}
  \begin{pspicture}[linecap=2](\Width,1)
    \psline(\Width,0)
    \multido{\ix=0+1,\i=\Start+1}{\numexpr\Width+1}{%
       \pst@mod{\i}{5}\result
       \psline(\ix,0)(\ix,3pt)
       \ifnum\result=0  \psline(\ix,0)(\ix,6pt) \fi  % i mod 5=0
        \pst@mod{\i}{10}\result
        \ifnum\result=0
            \psline(\ix,0)(\ix,9pt)%
            \uput[90](\ix,6pt){\the\numexpr\i/10}%  i mod 10 = 0
        \fi}
  \end{pspicture}\ignorespaces}
\makeatother
\begin{document}
\Ruler{2.3}{3.9}
\end{document}

enter image description here

Solution 2

For comparison, here is Metapost code to implement such a macro (using ConTeXt; because I don't know how to embed Metapost in LaTeX).

\define[2]\drawMPruler% from to (in mm)
    {\startMPcode
        newnumeric height;   height   := 3mm;
        newnumeric distance; distance := 1mm;

        newpath tic, medium_tic, big_tic;
        tic        := origin -- (0,  height);
        medium_tic := origin -- (0, 2height);
        big_tic    := origin -- (0, 3height);

        linecap := butt;

        % Draw base
        draw (#1*distance, 0) -- (#2*distance,0);

        for i = #1 step 1 until #2 :

          % Draw tics
          draw 
            (if (i mod 10) = 0 : big_tic elseif i mod 5 = 0 : medium_tic else : tic fi)
            shifted (i*distance,0);

          % Draw label
          if i mod 10 = 0 :
            draw textext.top (decimal (i/10)) shifted (i*distance, 3*height + 2pt);
          fi
        endfor;

      \stopMPcode}

\starttext

\drawMPruler{23}{39}

\stoptext

enter image description here

Solution 3

Just a little fun with TikZ (and PGFkeys).

The ticks can also be added with the decoration.markings library which would make it possible to create a ruler along a curved line (but obviously not with a fixed length but for certain coordinates (Bézier/to).

Following SDrolet's comment, I've fixed the code so that it actually uses inches for the Inch ruler. I've also changed the ticks so that they can be easier individualized by the user of \Ruler.

Using the array function is not fast (since we could just step through the list with every iteration) but it is the shortest implementation.

Code

\documentclass[tikz,border=12pt]{standalone}
\tikzset{
  ruler from/.initial=0,
  ruler to/.initial=10,
  ruler steps/.initial=10,
  ruler ticks/.initial={9,3,3,3,3,6,3,3,3,3},
  ruler rotate/.initial=0,
  every ruler picture/.style={line cap=rect},
  % presets
  ruler/.is choice,
  ruler/cm/.style={
    x=1cm,
    ruler ticks={9,3,3,3,3,6,3,3,3,3},
    ruler steps=10},
  ruler/in/.style={
    x=1in,
    ruler ticks={9,3,5,3,7,3,5,3},
    ruler steps=8}}
\makeatletter
\newcommand\Ruler[1][]{%
\begin{tikzpicture}[
  every ruler picture/.try,#1,rotate=\pgfkeysvalueof{/tikz/ruler rotate}]
  \pgfmathtruncatemacro\ruler@steps{\pgfkeysvalueof{/tikz/ruler steps}}
  \pgfmathtruncatemacro\ruler@Start
                                {floor((\pgfkeysvalueof{/tikz/ruler from})*\ruler@steps)}
  \pgfmathtruncatemacro\ruler@End{ceil((\pgfkeysvalueof{/tikz/ruler to})*\ruler@steps)}
  \draw (\ruler@Start/\ruler@steps,0) -- (\ruler@End/\ruler@steps,0);
  \foreach \ruler@Cnt[
    evaluate={\ruler@CntMod=int(Mod(\ruler@Cnt,\ruler@steps))},
    evaluate={\ruler@CntModLength=
              array({\pgfkeysvalueof{/tikz/ruler ticks}},\ruler@CntMod)}
  ] in {\ruler@Start,...,\ruler@End}
    \draw (\ruler@Cnt/\ruler@steps,0) -- ++(up:+\ruler@CntModLength pt)
      \ifnum\ruler@CntMod=0
        node[above, text depth=+2pt, inner sep=+0pt,
             rotate=\pgfkeysvalueof{/tikz/ruler rotate}]
          {$\pgfmathprint{int(\ruler@Cnt/\ruler@steps)}$}
      \fi;
\end{tikzpicture}\ignorespaces}

\begin{document}
\Ruler[ruler from=2.3, ruler to=3.9]
\Ruler[ruler=in, ruler to=8]
\Ruler[ruler rotate=30, ruler to=-5]
\end{document}

Output (not to scale)

Snipped of a centimetre ruler

Inch ruler

Rotated centimetre ruler

Solution 4

Herbert's solution with modified algorithm to avoid redundancies.

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{multido}
\psset{unit=2cm}
\makeatletter
\newcommand\Ruler[2]{%
\pstFPMul\Start{#1}{10}%
\pstFPMul\Stop{#2}{10}%
\def\Width{\numexpr\Stop-\Start\relax}%
\psset{xunit=.1\psxunit}
\begin{pspicture}[linecap=2](\Width,.325)
    \psline(\Width,0)
    \multido{\ix=0+1,\i=\Start+1}{\numexpr\Width+1}{%
        \pst@mod{\i}{10}\rem
        \ifnum\rem=0
            \psline(\ix,0)(\ix,9pt)
            \uput[90](\ix,6pt){\the\numexpr\i/10}
        \else
            \pst@mod{\i}{5}\rem
            \ifnum\rem=0
                \psline(\ix,0)(\ix,6pt)
            \else
                \psline(\ix,0)(\ix,3pt)
            \fi
        \fi
    }
\end{pspicture}\ignorespaces
}
\makeatother

\begin{document}
\Ruler{2.3}{3.9}
\end{document}
Share:
1,125
kiss my armpit
Author by

kiss my armpit

Updated on January 04, 2022

Comments

  • kiss my armpit
    kiss my armpit over 1 year

    I want to draw a trimmed ruler, for example, from 2.3cm to 3.9cm where the distance between two consecutive marks is 1mm.

    I have difficulty to efficiently check whether or not the counter is a multiple of 5 or 10. If the counter is a multiple of 5 then the mark is 6pt long else if the counter is a multiple of 10 then the mark is 9pt long plus printing the quotient as a label.

    The following is my complete code.

    \documentclass[pstricks,border=12pt]{standalone}
    \usepackage[nomessages]{fp}
    \usepackage{multido}
    \newcommand\Ruler[2]{%
    \FPeval\start{round(10*#1:0)}%
    \FPeval\stop{round(10*#2:0)}%
    \FPeval\width{round(stop-start:0)}%
    \FPeval\count{round(\width+1:0)}
    \psset{xunit=\dimexpr\psxunit/10}
    \begin{pspicture}[linecap=2](\width,1)
        \psline(\width,0)
        \multido{\ix=0+1,\i=\start+1}{\count}{%
            \FPeval\quo{trunc(\i/5:0)}
            \FPeval\rem{round(\i-5*quo:0)}
            \psline(\ix,0)(\ix,3pt)
            \FPifzero\rem
                \psline(\ix,0)(\ix,6pt)% if \i can be defined by 5
            \fi
            \FPeval\quo{trunc(\i/10:0)}
            \FPeval\rem{round(\i-10*quo:0)}
            \FPifzero\rem
                \psline(\ix,0)(\ix,9pt)% if \i can be defined by 10
                \uput[90](\ix,6pt){\quo}% if \i can be defined by 10 and put the result of \i divided by 10
            \fi
        }
    \end{pspicture}\ignorespaces
    }
    
    \begin{document}
    \Ruler{2.3}{3.9}
    \end{document}
    

    enter image description here

    Is there a better way to check divisibility in LaTeX?

    • egreg
      egreg over 9 years
      In LaTeX3 you can do \int_compare:nTF{\int_mod:nn {#1}{5} = 0}{true}{false}. I don't think that using fixed point numbers for doing integer arithmetic is the best way to proceed.
    • Qrrbrbirlbel
      Qrrbrbirlbel over 9 years
  • SDrolet
    SDrolet over 8 years
    To make a true inch ruler, one has to scale Qrrbrbirlbel ruler: \Ruler[ruler=in, scale=2.54,ruler to=8]
  • Qrrbrbirlbel
    Qrrbrbirlbel over 1 year
    @SDrolet You're right, the use and the order of every ruler picture wasn't set up correctly. I've updated the answer to reflect that and changed the way the ticks are drawn so that it supports the inch ruler properly.