7

Background

In MetaPost, colours can be lightened or darkened by calling the transparency function. However, this allows other colours to show through, which isn't necessarily desirable all the time. Sometimes a colour should be lightened or darkened independently of its transparency, by changing its value.

Problem

Colours can be adjusted in MetaPost with multiplication, such as:

\definecolor[BaseColour][h=66CEF1]

\startuseMPgraphic{page:ThemeElement}
  color baseColour;
  baseColour := .5 * \MPcolor{BaseColour};
\stopuseMPgraphic

However, that changes the saturation, and possibly the hue as well.

Question

In MetaPost, how do you control a colour's value, saturation, and hue, independently?

Related

The following ConTeXt code illustrates, conceptually at least, what I'd like to do in MetaPost:

\definecolor[BaseColour][h=66CEF1]
\definespotcolor[BaseColourSaturation][BaseColour][s=.625]
\definespotcolor[BaseColourValue][BaseColour][value=.625]
\definespotcolor[BaseColourHue][BaseColour][hue=.625]

The MetaPost Applications manual defines:

SetupColors( auto-SV, shading-SV, grayscale )

From the mailing list:

It appears that these functions all render the same output when viewed in Evince (the PDF reader I am using).

From the manual you can employ a complementary factor:

.7[red,white]

For example:

fill unitsquare scaled 1cm withcolor .7[red,white];

However, this does not provide enough control.

  • 1
    Do withrgbcolor or withcmykcolor give you enough control? You can find HSV to RGB/CMYK algorithms easily enough. – Thruston Oct 8 '13 at 23:01
  • withcmykcolor is not required, MetaFun assumes a CMYK colour if a colour with four elements is provided and interprets it correctly. HSV is not supported, though (well, strictly speaking CMYK is not supported either). – Marco Oct 9 '13 at 21:55
7

ConTeXt features several colour conversions, which are built into the core:

  • CMYK to grey
  • CMYK to RGB
  • grey to HSV
  • HSV to grey
  • HSV to RGB
  • RGB to CMYK
  • RGB to grey
  • RGB to HSV

They are defined in the file attr-col.lua. Here I use the Lua function hsvtorgb to convert the HSV input to an RGB value which MetaPost understands. The interface is not pretty, but it should get you started. Feel free to create a MetaPost definition for the conversion.

%% macros=mkvi

\starttexdefinition hsvtorgb #h #s #v
  \ctxlua{context("(\letterpercent f, \letterpercent f, \letterpercent f)", attributes.colors.hsvtorgb(#h, #s, #v))}
\stoptexdefinition

\starttext

Hues

\dostepwiserecurse{0}{360}{20}{\dontleavehmode
  \startMPcode
    fill unitcircle scaled 1cm withcolor \hsvtorgb{\recurselevel}{.76}{.76};
  \stopMPcode}

Saturation

\dorecurse{19}{\dontleavehmode
  \startMPcode
    fill unitcircle scaled 1cm withcolor \hsvtorgb{120}{.04*\recurselevel}{.76};
  \stopMPcode}

Value

\dorecurse{19}{\dontleavehmode
  \startMPcode
    fill unitcircle scaled 1cm withcolor \hsvtorgb{120}{.76}{.04*\recurselevel};
  \stopMPcode}

\stoptext

screenshot

| improve this answer | |
2

Here is a solution that allows plain Metapost to use HSV colours.

prologues := 3;
outputtemplate := "%j%c.eps";

vardef hsv_color(expr h,s,v) =
    % following wikipedia article on "HSL and HSV"
    save chroma, hh, x, m;
    chroma = v*s;
    hh = h/60;
    x  = chroma * (1-abs(hh mod 2 - 1));
    m  = v - chroma;
    if     hh < 1: (chroma,x,0)+(m,m,m)
    elseif hh < 2: (x,chroma,0)+(m,m,m)
    elseif hh < 3: (0,chroma,x)+(m,m,m)
    elseif hh < 4: (0,x,chroma)+(m,m,m)
    elseif hh < 5: (x,0,chroma)+(m,m,m)
    else:          (chroma,0,x)+(m,m,m)
    fi
enddef;

beginfig(1);
label.rt("Hue",        (-12,96));
label.rt("Saturation", (-12,64));
label.rt("Value",      (-12,32));
for i=0 upto 18:
   fill fullcircle scaled 20 shifted (20i,80) withcolor hsv_color(20i,3/4,3/4);
   fill fullcircle scaled 20 shifted (20i,48) withcolor hsv_color(120,i/25,3/4);
   fill fullcircle scaled 20 shifted (20i,16) withcolor hsv_color(120,3/4,i/25);
endfor
endfig;

beginfig(2);
for h=10 step 10 until 360:
  for s = 0.2 step 0.1 until 1:
     fill fullcircle scaled 20 shifted (100s*right) rotated h  withcolor hsv_color(h,s,1);
  endfor
  label(decimal floor(1/2+h), 110 right rotated h);
endfor
endfig;
end.

The first of the example figures looks like this: enter image description here

The second shows a colour wheel with v=1, h varying round the wheel, and s decreasing nearer the middle.

an HSV colour wheel

The colours are all flat: the apparent gradients (particularly obvious in the orange-red area) are an optical illusion.

| improve this answer | |
  • Recently wrote something similar in XSL to create a pie chart that chooses wedge colours for an arbitrary number of slices, all based on a given starting colour. Wouldn't be hard to extend this to pie charts in MetaPost. – Dave Jarvis Oct 22 '14 at 19:12

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.