MetaPost outputs PostScript, and PostScript draws solid areas (I think). So, transparency may not be achieved without drawing anew background picture. That's heavy, memory consuming and certainly not elegant.
Suppose that a currentpicture
is given.
You want to put on this picture some filled cycle path
c
with color yourcolor
and
transparency factor transparency
.
What would be nice is extracting exactly what in
currentpicture
is enclosed within
the path c
; then draw this with a
color which would be intermediate between
yourcolor
and the original color.
If it was possible to remove the previous picture
enclosed within the path c
as it
may happen for pixelized output, it would be very
nice nice since it would mean that the output's size
wouldn't grow to much.
In one of John Hobby's manuals is described the way to extract informations from the currentpicture, especially the control sequence
for <symbolic token> within <picture>
.
I have currentpicture
, a cycle path
c
, a transparency factor
transparency
and a color
yourcolor
. What I may do is:
c
with the background color
and take care that it will be at the back most level. The reason for
that will be clear later.currentpicture
,
draw this subpicture into a temporary picture with
some intermediate color.c
.currentpicture
.
The reason for having added some blank picture at the begining
is that when the path covers the background, it should be visible.
So that this part must exist within the currentpicture
.
% % settings % color yourcolor; yourcolor=red; transparency:=0.75; % % usage: transparentfill <cyclic path>; % vardef transparentfill expr c= save p_; picture p_; p_=currentpicture; currentpicture:=nullpicture; fill c withcolor background; addto currentpicture also p_; p_:=nullpicture; for p within currentpicture: addto p_ also p withcolor transparency[yourcolor,(redpart p,greenpart p,bluepart p)]; endfor clip p_ to c; draw p_; enddef;
A sample. Just the one on the right.
Why is it so bad? Try to fill two transparent areas that overlay. Their intersection will be opaque. Let's see why.
Suppose given a usual picture (no clipped area).
First fill cyclic path c1
with the transparentfill
control
sequence. This will add on top of the
currentpicture
a plain subpicture
p1
whose nature is quite different
from usual pictures (solid filled areas, strokes,
...): it is a clipped picture.
Now fill cyclic path
c2
with the transparentfill
control sequence. Processing transparentfill
,
first will come the original currentpicture
,
say originalpicture
,
which will be treated right as before. At last will
come p1
which will be seen a single
object whose contour is c1
and color
(0,0,0)
.
Now you can guess what will
happen: to the temporary picture p_
is added the contour c1
filled with
an intermediate color between black and
yourcolor
. Then p_
is
clipped to c2
to obtain, say, p2
and added to currentpicture
which is now equal
to originalpicture + p1 + p2
(order matters),
and which is not what you wanted it to be.
Fortunately, there exists a boolean valued operator
named clipped
, so that it is possible
in the previous loop to detect whether p
is a clipped picture or not. If it is, one has to
examin it deeper (and here comes recursivity).
The following code makes that
stuff. It is quite heavy and non-elegant. In fact,
since there is a very little documentation about
MetaPost processing, I just made some
random-binary-script-writting. The fact is that it
seems to work (it will be improved).
color yourcolor; yourcolor:=blue; picture AlphaPict; AlphaPict=nullpicture; Alpha:=0.25; def AlphaFill expr c= AlphaPict:=nullpicture; AlphaPicture(currentpicture,c); addto currentpicture also AlphaPict; enddef; vardef AlphaPicture(expr p,c)= save p_; picture p_; p_=nullpicture; addto p_ contour c withcolor Alpha[background,yourcolor]; for p__ within p: if (not clipped p__) and (not bounded p__): addto p_ also p__ withcolor Alpha[(redpart p__,greenpart p__,bluepart p__),yourcolor]; else: begingroup save AlphaPict; picture AlphaPict; AlphaPict=nullpicture; AlphaPicture(p__,pathpart p__); addto p_ also AlphaPict; endgroup; fi endfor clip p_ to c; addto AlphaPict also p_; enddef;
A sample.
Just the one on the right.
That's my usual Dangerous sign. I've filled
(AlphaFill <path>
)
four partially overlapping squares.
A simple thing to do in order to improve previous code is to draw subpicture if and only if its bounding box intersects the bounding box of the path. Here is more or less the code implemented in my statsmac package.
picture alphapict_; alphapict_=nullpicture; color fillcolor; fillcolor=red; fgalpha := 0.5; % usual alpha parameter bgalpha:= 1; % alpha parameter with respect to the background vardef alphafill expr c = alphapict_ := nullpicture; alphafill_(currentpicture, c); addto currentpicture also alphapict_; enddef; def alphafill_(expr p, c) = begingroup save p_, xmax_, xmin_, ymax_, ymin_; picture p_; p_ = nullpicture; (xmin_, ymin_) = llcorner c; (xmax_, ymax_) = urcorner c; addto p_ contour c withcolor bgalpha[background, fillcolor]; for p__ within p: numeric xmin__, xmax__, ymin__, ymax__; (xmin__, ymin__) = llcorner p__; (xmax__, ymax__) = urcorner p__; if (xmax__<= xmin_) or (xmin__ >= xmax_): else: if (ymax__<= ymin_) or (ymin__ >= ymax_): else: if (not clipped p__) and (not bounded p__): addto p_ also p__ withcolor fgalpha[(redpart p__, greenpart p__, bluepart p__), fillcolor]; else: begingroup save alphapict_; picture alphapict_; alphapict_ = nullpicture; alphafill_(p__, pathpart p__); addto p_ also alphapict_; endgroup; fi fi fi endfor clip p_ to c; addto alphapict_ also p_; endgroup; enddef;
A sample. Just the one on the right. That's some local building. I replace the sphere by a transparent cube (lighter). One should notice that the text is made of clipped and transformed text pictures (that's postscript fonts, I did not try bitmap fonts yet), so that the macro seems to work quite well.
If you have some better ideas, let me know!