4
$\begingroup$

Problem outline

Let's suppose we have charts placed in a common coordinate system in such a way that the colored areas of these charts overlap.

enter image description here

We want to ensure that all the plots and their overlapping areas are clearly visible — they must not obscure or merge into one another. Mathematica allows you to make overlapping colored areas visible by setting partial transparency (using Opacity[o] with $o<1$) for individual colors. I think this method of combining colors is called $\alpha$-mixing.

enter image description here

However, this solution is unsatisfactory because transparency causes the background to show through. (If the background is set to be transparent, the final image will contain semitransparent color fields, and if the background is white, the colors will appear washed out.)

Instead, I would prefer the colors to be fully opaque, and in places where the colored areas overlap, for them to be mixed according to some recipe other than $\alpha$-mixing — for example, additive color mixing in the RGB model. How can this be achieved?

Elaboration

The specific form of the chart, let alone the functions it delineates, are irrelevant here. In Mathematica's 2D graphics, whenever fields of different colours are superimposed, either simply the top one is visible or $\alpha$-mixing is employed. So let's prepare some example functions and their plot that demonstrate this issue clearly.

Preparation of sample functions and their overlapping plots

funs = {Sin[1/(x/3)^2] + x, -Sin[1/(x/3)^2] - x, 2 Sin[1/((2 x)^3)]}; Plot[funs, {x, -Pi/2, Pi/2}, PlotPoints -> 2^8, ImageSize -> Large] 

enter image description here (The functions and their parameters were chosen so that the colour fields of their plots are clearly overlapping).

(I do not insist that the example shown is the best possible. Perhaps the need to make areas of overlap visible is even better seen from the first illustration (obtained from real data).)

Unsatisfactory solution

An obvious way to show areas of colour field overlap is the use of transparency.

Plot[funs, {x, -Pi/2, Pi/2}, PlotStyle -> Opacity[0.5], PlotPoints -> 2^8, ImageSize -> Large] 

enter image description here This solution has the following drawbacks:

  • Transparency is applied to entire plots, not just their overlapping parts. Therefore, the background shows through from underneath (directly, when opaque, or by a value $<1$ on the $\alpha$-channel, when transparent).
  • The areas of overlap are not sufficiently distinct.
  • The order in which the plots are overlaid matters.

Aim

I am looking for a systematic way to blend layer colours only where the colour fields overlap, while leaving the plot colours completely opaque. As a first attempt, I would like to try additive colour mixing in the RGB model. Ultimately, a solution is sought that allows any colour blending recipe to be given as a function.

$\endgroup$
4
  • 6
    $\begingroup$ Please show some sample Mathematica Code. $\endgroup$ Commented Mar 8 at 1:38
  • $\begingroup$ @A.Kato, I think the code that led to the creation of these graphics is irrelevant. This is just an example of what I am talking about. In Mathematica, whenever fields of different colours are superimposed, either the top one is visible or alpha-mixing is used. I myself am looking for code that would allow the use of different layer colour blending. $\endgroup$ Commented Mar 8 at 15:44
  • 3
    $\begingroup$ Even if that is the case, you are likely to get an answer more quickly if you include some sample code. $\endgroup$ Commented Mar 9 at 0:41
  • 1
    $\begingroup$ I think I have now made the issue as clear as possible. I have prepared sample code and charts showing the problem we are discussing; I have provided an example of an apparent solution, which, however, turns out to be unsatisfactory, what I have justified; I have clarified what I am looking for. I kindly ask to reopen the question. $\endgroup$ Commented Mar 11 at 11:48

2 Answers 2

6
$\begingroup$

What you are talking about are different blend modes. They are quite common in graphics editing programs, but it seems that Mathematica front-end does not have this implemented – or it is some obscure undocumented low-level option for boxes.

If we forget about vector graphics*, then it is quite trivial to implement other blending modes for raster images by taking pixel data with ImageData and applying custom blending function. It gets a bit clunky for plotting curves, but it's still manageable.

Below is an implementation of blendPlots and two simple blending modes (RGB-additive and HSB-additive). The first argument is a Plot[...], and the second argument is a blending function, which should expect arbitrary number of colors as RGBA lists and return the resulting color as an RGBA list.

SetAttributes[blendPlots, HoldAll]; Options[blendPlots] = {ImageResolution -> 150, ImageSize -> 150}; blendPlots[Plot[funs_, spec_, plotOpts___], fBlend_, OptionsPattern[]] := Module[{plotBackground, plotCurves, rasterize, optsAll}, rasterize[p_, o___] := Rasterize[p, o, Background -> None, ImageResolution -> OptionValue[ImageResolution], ImageResolution -> OptionValue[ImageSize]]; optsAll = Join[{plotOpts}, AbsoluteOptions@Plot[funs, spec, plotOpts]]; plotBackground = rasterize[With[{o = optsAll}, Plot[{}, spec, o]], Background -> None]; plotCurves = rasterize /@ Table[With[{fns = ReplacePart[funs, Except[i] -> None], o = optsAll}, Plot[fns, spec, Axes -> None, o]], {i, 1, Length[funs]}]; ImageCompose[ Image[MapThread[fBlend, ImageData /@ plotCurves, 2], ColorSpace -> "RGB"], plotBackground] ] (*RGB-additive mixing*) blendRGB[colors__] := Total[{colors}]; (*HSB-additive mixing*) blendHSB[colors__] := ColorConvert[Total[List @@@ ColorConvert[{colors}, "RGB" -> "HSB"]], "HSB" -> "RGB"] 

Usage on your set of exemplary functions:

blendPlots[Plot[funs, {x, -π/2, π/2}, PlotPoints -> 2^8], blendRGB] blendPlots[Plot[funs, {x, -π/2, π/2}, PlotPoints -> 2^8], blendHSB] blendPlots[Plot[funs, {x, -π/2, π/2}, PlotPoints -> 2^8, PlotStyle -> {Red, Green, Blue}], blendRGB] 

enter image description here

Obviously, for most pleasing results, you will have to choose the initial colors of curves and the blending function wisely, but I believe this is outside the scope of your question.


* A possible approach for maintaining vectorised output of your plots would be to extract the curves as (thick) regions, find their intersections, then plot them again with intersections colored differently.

$\endgroup$
11
  • $\begingroup$ Thanks for the valuable reply! However, I do see one weakness in it. Your colour mixing is always done for two arguments. Meanwhile, there are places where 3 layers overlap, and there could be more. This can be seen well in the case of additive mixing in the RGB model (fig.2), in the middle, where all three colours should add up - here R, G and B, which should give white, meanwhile we have something else. I think I've found a way to add any number of layers. I will try to present it. $\endgroup$ Commented Mar 11 at 18:36
  • $\begingroup$ @Druid, note that my blending method is not additive, but averaging, that is why there is no get white. As I've written, you can change your blending function as you need. If you want to have an $n$-ary blending function, you can change the line with Fold[...]. $\endgroup$ Commented Mar 11 at 19:22
  • $\begingroup$ The mean of R, G and B should be gray but here it is not. The problem lies not in normalization, but in the number of arguments. $\endgroup$ Commented Mar 11 at 20:00
  • $\begingroup$ @Druid, I have now update my answer with $n$-ary blending function, and added an example of RGB-additive and HSB-additive blending. $\endgroup$ Commented Mar 11 at 20:45
  • $\begingroup$ Maybe there is something wrong in your code or I do not understand how it works, but why if I use Mean instead of Total like so blendRGB[colors__] := Mean[{colors}]. The final image is like if also the color of white background was mixed in. $\endgroup$ Commented Mar 11 at 21:13
0
$\begingroup$

Step 1 - Color mixing functions

Let us construct example colour mixing functions for any number of raster images.

Additive RGB mixing

rgbadd[images_] := Module[{rgbaimages, alldata, allrgb, allalpha, allweightedrgb, totweightedrgb, effalpha, tot}, rgbaimages = Map[ColorConvert[#, "RGB"] &, images]; alldata = Map[ImageData, rgbaimages]; dim = Dimensions[alldata]; allrgb = alldata[[All, All, All, 1 ;; 3]]; allalpha = If[dim[[4]] == 4, alldata[[All, All, All, 4]], ConstantArray[1, dim[[1 ;; 3]]]];(*Protected against the absence of an alpha channel*) allweightedrgb = allrgb*allalpha;(*The alpha channel is used to weight the components of the sum when totalizing the RGB coordinates*) totweightedrgb = Total[allweightedrgb, {1}]; effalpha = Map[{#} &, 1 - Apply[Times, 1 - allalpha], {2}];(*A physically meaningful method of combining layer opacity.*) tot = Join[totweightedrgb, effalpha, 3]; Image[tot, ColorSpace -> "RGB"] ] 

Testing with simple images

To begin with, let's test this function on simple raster images (unrelated to plots) in primary colours with an $\alpha$-channel.

points = Table[{Cos[\[Phi]], Sin[\[Phi]]}/4, {\[Phi], 0, 2 Pi, 2 Pi/3}]; imdef = {{Red, Disk[points[[1]], 1/Sqrt[2]]}, {Green, Disk[points[[2]], 1/Sqrt[2]]}, {Blue, Disk[points[[3]], 1/Sqrt[2]]}}; images = Map[Rasterize[Graphics[{#}, PlotRange -> 1], RasterSize -> 2^8, Background -> None] &, imdef, {1}] 
rgbadd[images] 

RGB add

RGB averaging

Let the second mixing function be RGB averaging.

rgbmean[images_] := Module[{rgbimages, alldata, dim, allrgb, allalpha, allweightedrgb, totweightedrgb, effalpha, totalpha, meanweightedrgb, tot}, rgbimages = Map[ColorConvert[#, "RGB"] &, images]; alldata = Map[ImageData, rgbimages]; dim = Dimensions[alldata]; allrgb = alldata[[All, All, All, 1 ;; 3]]; allalpha = If[dim[[4]] == 4, alldata[[All, All, All, 4]], ConstantArray[1, dim[[1 ;; 3]]]]; allweightedrgb = allrgb*allalpha; totweightedrgb = Total[allweightedrgb, {1}]; effalpha = Map[{#} &, 1 - Apply[Times, 1 - allalpha], {2}]; totalpha = Total[allalpha, {1}];(*The sum of the opacity of the layers serves as a normalisation factor*) meanweightedrgb = MapThread[If[#2 > 0, #1/#2, {0, 0, 0}] &, {totweightedrgb, totalpha}, 2];(*Avoid division by zero*) tot = Join[meanweightedrgb, effalpha, 3]; Image[tot, ColorSpace -> "RGB"] ] 

Testing with simple images

rgbmean[images] 

RGB mean

(The perceived differences in brightness are due to the specific nature of the RGB space.)

Other mixing functions

It is relatively easy to build a subtractive mixing function. Mixing in HSB space is slightly more difficult. And this is because the variable H has a topology of a circle and the usual averaging does not apply there. The correct approach is to use the mean on the surface of a disk of radius S and the angular coordinate H.

Step 2 - Plot generation function

The function that generates the plots takes as arguments: a list of functions to be plotted, colours, plot range, raster size and a mixing function.

rgbPlot[funs_, cols_, range_, rastersize_, mixing_] := Module[{additive, meannormalized, size, plots, chart, rasters, comb, inset}, size = range[[All, 2]] - range[[All, 1]]; plots = MapThread[Plot[#1, {x, range[[1, 1]], range[[1, 2]]}, PlotRange -> range, PlotStyle -> #2, Axes -> False, PlotPoints -> 200] &, {funs, cols}]; chart = Plot[{}, {x, range[[1, 1]], range[[1, 2]]}, PlotRange -> range]; rasters = Map[Rasterize[#, Background -> None, RasterSize -> rastersize] &, plots]; comb = mixing[rasters]; inset = Graphics[{Inset[comb, range[[All, 1]], {0, 1}, size]}, PlotRange -> range]; Show[chart, inset, PlotRange -> range] ]; 

Let's choose the parameters.

cols = {Red, Green, Blue}; range = {{-Pi, Pi}/2, {-1, 1}*2}; rastersize = 2048; 

Finally, we can generate the charts

rgbPlot[funs, cols, range, rastersize, rgbadd] 

RGB add plot

rgbPlot[funs, cols, range, rastersize, rgbmean] 

RGB mean plot

$\endgroup$
1
  • $\begingroup$ The resulting images lost their antialiasing (same as with Domen's answer). I do not think that final result is worth of loosing quality of graphics. $\endgroup$ Commented Mar 12 at 13:53

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.