7

I want to create a \rating command that draws a partly filled circle, according to a numeric parameter between 0 and 100:

sequence of 10 circular rating symbols

Question: How can I modify the expression for determining the angles in arc, so that the filling rises more steadily over the whole value range? Right now, ratings from 0 to 20 look empty, 80 to 100 look completely filled and only in between there is a steady rise.

Clarification: Rather than a mathematically rigoros expression, I am looking for the simplest expression that looks right, i.e. that \rating{25}, \rating{50} and \rating{75} visually look like 1/4, 2/4 and 3/4.

\documentclass{minimal}
\usepackage{tikz}

\newcommand{\rating}[1]{
\begin{tikzpicture}
\fill[lightgray] ({270-1.8*#1}:1ex) arc ({270-1.8*#1}:{270+1.8*#1}:1ex) -- cycle;
\draw[black, thin, radius=1ex] (0,0) circle;
\end{tikzpicture}}

\begin{document}
This really should scale\dots
\rating{0} \rating{10} \rating{20} \rating{30} \rating{40} \rating{50}
\rating{50} \rating{60} \rating{70} \rating{80} \rating{90} \rating{100}
\dots linearly.
\end{document}
6
  • This is a maths question rather than a TeX one.
    – jub0bs
    Aug 5, 2014 at 15:17
  • Possible duplicate: tex.stackexchange.com/a/108622/19356 Aug 5, 2014 at 15:18
  • @Jubobs: Partly agreed: I'm more interested in a working (in the TeX environment) solution than a mathematically correct one.
    – ojdo
    Aug 5, 2014 at 15:19
  • @ojdo If a simple mathematical solution exists, why not use it?
    – jub0bs
    Aug 5, 2014 at 23:13
  • 1
    @ojdo True: my solution only applies to a circle.
    – jub0bs
    Aug 6, 2014 at 8:24

5 Answers 5

6

As an aside, please see Why should the minimal class be avoided?

Here is a solution using a clipped path in TikZ. Note that the clip path cannot have extra options added, so I've drawn the circle with your specifications separately, outside the scope of the \clip path.

\documentclass{article}
\usepackage{tikz}

\newcommand{\rating}[1]{%
  \begin{tikzpicture}[x=1ex,y=1ex]
    \begin{scope}
      \clip (0,1) circle (1);
      \fill[lightgray] (-1,0) rectangle (1,#1/50);
    \end{scope}
    \draw[black, thin, radius=1] (0,1) circle;
  \end{tikzpicture}%
}

\begin{document}
This really should scale\dots
\foreach \h in {0,5,...,100} {\rating{\h}}
\dots linearly.
\end{document}

enter image description here

1
  • 1
    Aside noted, elegance of the clip path solution appreciated :-)
    – ojdo
    Aug 5, 2014 at 15:51
5

Another way with TikZ but without explicit clipping:

\documentclass[border=5]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{circle mark/.pic={
  \draw [path picture={%
    \fill (path picture bounding box.south west) |-  
      ($(path picture bounding box.south east)!#1/100!(path picture bounding box.north east)$) 
      |- cycle;}] circle [radius=1ex];
}}
\begin{document}
\foreach \i in {0,10,...,100}
  \tikz\path pic{circle mark=\i};
\end{document}

enter image description here

1
  • Can this also be done clockwise. Eg 25% is black from 12 o clock until 3 o clock etc?
    – robert
    Jan 11, 2016 at 17:09
5

All you need is a different parameterisation of the angle; see below for details.

enter image description here

Additionally,

  1. since you seem to use \rating "inline", you may want to simply use \tikz instead of a tikzpicture environment;
  2. avoid spurious spaces in the definition of \rating (instead, insert a space between calls to \rating, wherever one is needed);
  3. instead of hardcoding 1ex as the radius, make it an optional argument, for more flexibility;
  4. as already pointed out by Paul in his answer, avoid the minimal class when using tikz.

\documentclass{article}
\usepackage{tikz}
\usepackage{amsmath}

\newcommand{\rating}[2][1ex]{%
  \pgfmathsetmacro\th{asin(#2/50-1)}% (theta angle of polar coordinates)
    \tikz{%
      \fill[lightgray] (\th:#1) arc (\th:-180-\th:#1) -- cycle;
      \draw[black, thin, radius=#1] (0,0) circle;
    }%
}

\begin{document}
This really should scale\dots
\foreach \h in {0,10,...,100} {\rating{\h} }
\dots linearly.

\section*{Parameterisation explained}

Let~\(R\) be the disk radius. In polar coordinates,
%
\[
  y = R \sin \theta \,.
\]
%
Therefore,
%
\[
  \theta = \arcsin \frac{y}{R}  \,.
\]
%
Because the quantity~\(y/R\) varies over~\([-1,1]\)
but you want a parameter~\(u\) that varies over~\([0,100]\),
the right affine transformation will get you there:
%
\[
  \frac{y}{R} = \frac{u}{50} - 1 \,.
\]
%
\end{document}
4

Just for fun with PSTricks.

\documentclass[preview,border=12pt,varwidth]{standalone}
\usepackage{pstricks}


\newcommand\rating[2][.25pt]{%
\psset{unit=#1,runit=#1}
\pspicture(100,100)
\psclip{\pscircle[linestyle=none,linewidth=0](50,50){50}}
    \psframe*[linecolor=gray](100,#2)
\endpsclip
\pscircle[dimen=m](50,50){50}
\endpspicture}

\begin{document}
Linearly filled circles:\\
\foreach \i in {0,10,...,100}{\rating{\i}}
\end{document}

enter image description here

3

Run with xelatex

\documentclass{article}
\usepackage{pstricks}

\def\rateColor{black!40}
\newcommand\rating[2][]{%
  \ifx\relax#1\relax\else\gdef\rateColor{#1}\fi
  \psset{unit=1ex}%
  \pspicture[dimen=inner](-1.2,-1.2)(1.2,1.2)
    \psclip{\pscircle{1}}
      \psframe*[linecolor=\rateColor](-1,-1)(!1 #2 100 div 2 mul 1 sub)
    \endpsclip\pscircle{1}
  \endpspicture}

\begin{document}
This really should scale \dots

\tiny
\multido{\iA=0+10}{21}{\ifnum\iA>100\expandafter\rating\expandafter{\the\numexpr200-\iA}\else\rating{\iA}\fi} \par
\normalsize
\rating[red]{0} 
\multido{\iA=10+10}{20}{\ifnum\iA>100\expandafter\rating\expandafter{\the\numexpr200-\iA}\else\rating{\iA}\fi} \par
\Large
\rating[blue]{0} 
\multido{\iA=10+10}{20}{\ifnum\iA>100\expandafter\rating\expandafter{\the\numexpr200-\iA}\else\rating{\iA}\fi} \par
\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.