43

I'm writing a review for a book at the moment, and I thought I'd be clever and show off what LaTeX is capable of by adding a x-out-of-y stars graphic. Tikz can do many things so I would have thought that a trick like this would be fairly trivial, however it seems that I cannot figure out a macro to draw the shapes at the right size, let alone fancy tricks such as half-filled stars or something similar. I think the code I have so far might be headed in the right direction:

\usepackage{tikz}
\usetikzlibrary{shapes.geometric}
\usetikzlibrary{calc,shadows}

\newcommand*\starfill{%
  \tikz[baseline=(key.base),scale=-3]
  \node[star, star points=5, star point ratio=2.25, fill=black, draw](key) {S};  
}

I've tried to start by drawing a single star shape, but ideally I'd like to define like \starsranking{number}{total} that will output the appropriate shaded number of stars out of total. Is this doable? It doesn't sound particularly difficult.

g.kov
  • 21,864
  • 1
  • 58
  • 95
Robbie
  • 2,893
  • 1
    The star, star points=5, star point ratio=2.25 are your own options, aren't there? You should have a look at \foreach. You need two: one from 1 to \starpoints to make the filled stars and one for \starpoints+1 to 5. The half-filled stars should be do-able with an clipped fill-path. – Martin Scharrer Feb 18 '11 at 09:28

5 Answers5

59

Here's the code for fully filled stars, now slightly improved thanks to Andrew Stacey's answer to the checkerboard question:

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}

\newcommand\score[2]{%
  \pgfmathsetmacro\pgfxa{#1 + 1}%
  \tikzstyle{scorestars}=[star, star points=5, star point ratio=2.25, draw, inner sep=1.3pt, anchor=outer point 3]%
  \begin{tikzpicture}[baseline]
    \foreach \i in {1, ..., #2} {
      \pgfmathparse{\i<=#1 ? "yellow" : "gray"}
      \edef\starcolor{\pgfmathresult}
      \draw (\i*1.75ex, 0) node[name=star\i, scorestars, fill=\starcolor]  {};
   }
  \end{tikzpicture}%
}

\begin{document}
\score{0}{5} A meagre result.

\score{4}{5} Much better

\score{5}{5} Perfect score!

\end{document}

ranking stars with tikz


And here's the much more elaborate, much more pointless, floating point scoring star macro (I'll leave the simple one in as well, it's a lot more usable):

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric, calc}

\newcommand\score[2]{%
  \pgfmathsetmacro\pgfxa{#1 + 1}%
  \tikzstyle{scorestars}=[star, star points=5, star point ratio=2.25, draw, inner sep=0.15em, anchor=outer point 3]%
  \begin{tikzpicture}[baseline]
    \foreach \i in {1, ..., #2} {
      \pgfmathparse{\i<=#1 ? "yellow" : "gray"}
      \edef\starcolor{\pgfmathresult}
      \draw (\i*1em, 0) node[name=star\i, scorestars, fill=\starcolor]  {};
    }
    \pgfmathparse{#1>int(#1) ? int(#1+1) : 0}
    \let\partstar=\pgfmathresult
    \ifnum\partstar>0
      \pgfmathsetmacro\starpart{#1-(int(#1)}
      \path [clip] ($(star\partstar.outer point 3)!(star\partstar.outer point 2)!(star\partstar.outer point 4)$) rectangle 
      ($(star\partstar.outer point 2 |- star\partstar.outer point 1)!\starpart!(star\partstar.outer point 1 -| star\partstar.outer point 5)$);
      \fill (\partstar*1em, 0) node[scorestars, fill=yellow]  {};
    \fi
  \end{tikzpicture}%
}

\begin{document}
\score{0}{5} That's appalling!

\small\score{2}{5} A meagre result.

\Huge{\score{4.4}{5} Wooo!}

\end{document}

enter image description here

Faheem Mitha
  • 7,778
Jake
  • 232,450
  • Like I said, very good (even better since I first said it). I didn't realize coordinates could be averaged with the ! like colors can. I had been going for a style approach so that you could type \node[scorestars,portion=0.25] and get 25% of the star filled. Looks like you could use the append after command key for that. – Matthew Leingang Feb 18 '11 at 16:06
  • 1
    Blast, I've already voted for this before your edit. I think that the floating point version is fantastic. – Andrew Stacey Feb 18 '11 at 18:24
  • That is brilliant. Wow. – Robbie Feb 20 '11 at 12:11
  • One question though: is it possible to make the stars scale to the text height? So if the text is \large the stars will also be correspondingly bigger? – Robbie Feb 20 '11 at 12:18
  • @zoqaeski: Done! Just use relative lengths ("ex" and "em") for the size and spacing. – Jake Feb 20 '11 at 20:06
  • Well thanks for that. I made a few slight changes, I might go digging around in the various options to see if I can turn this into a little package I can use for my reviews on things. – Robbie Feb 21 '11 at 13:18
  • Great stuff! Always amazed what's (easily) possible with TikZ – Habi Feb 21 '11 at 14:23
16

enter image description here

Asymptote version stars.asy:

size(200);
real sc=20;

picture score(real scoreMark=0,guide star=scale(sc)*unitcircle, int maxscore=5, 
  pen linePen=nullpen, pen bgPen=darkblue, pen scorePen=orange){
  picture pic;
  guide[] g;  
  for(int i=0;i<maxscore;++i){
    g.push(shift((2sc*i,0))*star);
  }
  assert(maxscore>0 && scoreMark>=0 && scoreMark<=maxscore,"***** Wrong score.");
  fill(pic,box((-sc,-sc),(maxscore*2sc-sc,sc)),bgPen);
  fill(pic,box((-sc,-sc),(scoreMark*2sc-sc,sc)),scorePen);
  clip(pic,g);
  draw(pic,g,linePen);
  return pic;
}

guide star;
pair p;
for(int i=0;i<5;++i){
  p=rotate(72*i)*N;
  star=star--p;
  star=star--(scale(0.382)*rotate(72*i+36)*N);
}
star=scale(sc)*(star--cycle);

add(score(scoreMark=1,star,maxscore=7),(0,0));
add(score(scoreMark=2,star,maxscore=7,linePen=lightred,bgPen=lightblue),(0,-3sc));
add(score(scoreMark=3.5,star,maxscore=5,linePen=lightred,bgPen=lightblue),(0,-6sc));
add(score(scoreMark=3.75,star,maxscore=4,linePen=olive,bgPen=white,scorePen=lightgreen),(0,-9sc));

add(score(4.2,bgPen=green+opacity(0.3),scorePen=red+opacity(0.5)),(0,3sc));

To get a standalone stars.pdf run asy -f pdf stars.asy.

g.kov
  • 21,864
  • 1
  • 58
  • 95
14

With PSTricks.

enter image description here

\documentclass[preview,border=12pt,varwidth]{standalone}
\usepackage{pstricks}
\usepackage{multido}
\SpecialCoor
\makeatletter
\def\LoadPSVars{\pstVerb{/ptcm {\pst@number\psunit div} bind def}}
\makeatother

\def\points{}
\def\Star{%
    \xdef\points{}% cleaning
    \multido{\iR=0+72,\ir=36+72}{5}{\xdef\points{\points (10pt;\iR)(5pt;\ir)}}
    \expandafter\pspolygon\points}

\def\Rating#1{% #1: percentage
    \psscalebox{0.35}{%
    \begin{pspicture}(11pt,-11pt)(111pt,11pt)
    \LoadPSVars
    \psclip{\pscustom{\psLoop{5}{\translate(20pt,0)\Star}}}
        \psframe*[linecolor=yellow](11pt,-11pt)(!#1 11 add ptcm 11 ptcm)
    \endpsclip
    \pscustom{\psLoop{5}{\translate(20pt,0)\Star}}
    \end{pspicture}}}

\begin{document}
\begin{enumerate}
    \item \Rating{100} PSTricks
    \item \Rating{50} Asymptote
    \item \Rating{20} Metapost
    \item \Rating{5} TikZ
\end{enumerate}
\end{document}

Warning:

  • \rput will not work inside \pscustom. Use \translate instead!
  • standalone discards my ptcm operator defined in the preamble, so I have to load it manually inside pspicture. It is sad!
11

I have written a package for ConTeXt that draws X out of Y graphics. It is primarily meant to draw numbers in presentations. It offers stars as one of the options:

enter image description here

\usemodule[visualcounter]

\definepalet
  [star-colors]
  [active=yellow,
     past=yellow,
   future=gray]

\definevisualcounter
  [stars]
  [markers]
  [mpsetups=visualcounter::markers:star, 
   width=1.5EmWidth,
   distance=0.25EmWidth,
   palette=star-colors, 
  ]

\starttext

\startTEXpage[offset=2mm]
\startitemize
  \item \usevisualcounter[n=1, last=5]{stars} That's appalling!
  \item \usevisualcounter[n=2, last=5]{stars} A meagre result.
  \item \usevisualcounter[n=4, last=5]{stars} Woo!
\stopitemize
\stopTEXpage
\stoptext

See documentation for more details.

As the primary purpose of this module is to display page numbers, it does not support fractional values.

Aditya
  • 62,301
  • Hi Aditya, I'm interested in learning a little about MetaPost, and this looks like a nice application of that. However, I wish that your module did support fractional values. How hard would it be to add? – Faheem Mitha Jan 11 '20 at 06:43
  • @faheem: it is relatively easy to add support from fractional values. The module provides a few different counters and fractional values don't make sense for some of them, but for makers it is relatively easy to get. I'll upload an updated version of the module and then reply here – Aditya Jan 11 '20 at 14:46
  • Thank you very much, Aditya. I look forward to experimenting with MetaPost. Or is it MetaFun now? – Faheem Mitha Jan 11 '20 at 16:09
6

Jake's answer is very good. Here's the extra half star:

\node[scorestars,fill=gray] {};
\path node[scorestars,fill=yellow] (s) {} [clip] (s.south west) rectangle (s.north);
Matthew Leingang
  • 44,937
  • 14
  • 131
  • 195