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}
ojdo
  • 2,125
  • This is a maths question rather than a TeX one. – jub0bs Aug 05 '14 at 15:17
  • Possible duplicate: http://tex.stackexchange.com/a/108622/19356 – kiss my armpit Aug 05 '14 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 05 '14 at 15:19
  • @ojdo If a simple mathematical solution exists, why not use it? – jub0bs Aug 05 '14 at 23:13
  • @Jubobs: I accepteded the clip answer because it would work even for other shapes without the need for asin etc. Frankly, I am overwhelmed by the number of options I was offered. I will probably adapt the optional parameter idea of your answer. – ojdo Aug 05 '14 at 23:15
  • 1
    @ojdo True: my solution only applies to a circle. – jub0bs Aug 06 '14 at 08:24

5 Answers5

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

Paul Gessler
  • 29,607
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}
jub0bs
  • 58,916
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

Mark Wibrow
  • 70,437
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