# Chromaticity plot of RGB color matching functions

I'm trying to make a 3D chromaticity plot for the RGB coordinates. I do not want to use the predefined ChromaticityPlot function for two resons: to learn how to do it and mainly because it is based on the xy projection of the XYZ color space, which has different color matching functions than the RGB color space.

I'll explain my work so far. First I import my RGB color matching functions RGB CMFs cie1931rgb = Import["cie1931rgb.csv"]

I then get each coordinate{\[Lambda], r, g, b} = Transpose[cie1931rgb]. Here they are:

I plot them in a 3D space and project them in the r+g+b=1 plane:

ListPlot3D[Transpose[{r/(r + b + g), g/(r + b + g), b/(r + b + g)}]]


There's the shape :) It can be better identified with the Above orthographic viewpoint (notice it's different from the ChromaticityPlot shape for the reason I mentioned).

Now I want to put a mesh on it and fill it with color based on the RGB coordinates, on the valid region of course. Since RGBColor is defined as 3 coordinates from 0 to 1, I want to find a middle point on each division of the mesh and if their coordinates are all positive, assign an RGB color to that division based on the actual coordinates on the plot.

I tried

ListPlot3D[Transpose[{r/(r + b + g), g/(r + b + g), b/(r + b + g)}], Mesh -> 50, MeshStyle -> Opacity[0.9], MeshShading -> If[r/(r + b + g) > 0 && g/(r + b + g) > 0 && b/(r + b + g) > 0, Dynamic@RGBColor[r/(r + b + g), g/(r + b + g), b/(r + b + g)], {{White}}], Lighting -> "Neutral", ViewPoint -> {0, 0, \[Infinity]}]


with the idea on incrementing the number of Mesh and at the same time decrease its Opacity but the MeshShading coloring part doesn't work and I'm stuck. Any help is welcome.

Is this what you seek?

The 3D boundary curve (removing the last point because it has coordinates {0.,0.,0.})

curve = Most@Transpose[{r, g, b}];
curve /= Total[curve, {2}];


Closing the boundary curve:

AppendTo[curve, curve[[1]]];


Transforming curve to lie in the plane...

A = RotationMatrix[{{1., 1., 1.}, {0., 0., 1.}}];
planarcurve = ((curve - ConstantArray[{0., 0., 1.}, Length[curve]]).A\[Transpose][[All, 1 ;; 2]]);


... in order to use TriangulateMesh (produces only for full-dimensional meshes). MaxCellMeasure -> {1 -> 0.05} specifies the maximal edge length in the resulting mesh.

mesh = TriangulateMesh[
BoundaryDiscretizeGraphics[Graphics[Line[planarcurve]]],
MaxCellMeasure -> {1 -> 0.05}];


Transforming the coordinates back into 3D and using the triangles from mesh to specify a GraphicsComplex. Using VertexColors to specify the colors at the vertices of the GraphicsComplex. The colors in the interior of the triangles will be interpolated linearly from the corners' colors.

pts = MeshCoordinates[mesh].A[[1 ;; 2]] + ConstantArray[{0., 0., 1.}, MeshCellCount[mesh, 0]];
Graphics3D[
GraphicsComplex[
pts, {EdgeForm[], MeshCells[mesh, 2, "Multicells" -> True]},
VertexColors -> RGBColor @@@ pts]]


You can show the edges of the 3D mesh with EdgeForm[Thin] instead of EdgeForm[].

## Edit

After the last comment, I realized that you are not interested in the curved shape at all. Then all this is easier achieved by

ParametricPlot3D[{u, v, 1 - u - v}, {u, 0, 1}, {v, 0, 1 - u},
ColorFunction -> ({x, y, z, u, v} \[Function] RGBColor[x, y, z]),
Mesh -> None
]


• That looks good, thank you, I have 2 problems though. The first one is a programming begginer one, I executed your code as-is and I got multiple errors imgur.com/a/mFC00qk (I have Mathematica 11.2) The second one is a conceptual one, if RGB is defined for values between 0 and 1, one could only color in theory the area inside the triangle imgur.com/a/NBc09DQ ... so can you explain a little bit your code? May 5, 2018 at 23:05
• Sorry for the inconvenience. Fixed several bugs. Please try again. ^^ May 5, 2018 at 23:20
• It worked and it is well explained. Thank you sir. May 5, 2018 at 23:30
• You're welcome. May 5, 2018 at 23:30

I actually got a more faithful reconstruction of a chromaticity plot. I noticed the answer of @Henrik Schumacher had embedded the RGB encoding into VertexColors -> RGBColor @@@ pts so it colors the area into red, green and blue... it is not how a chromaticity plot should be. For starters, it should begin with violet, not blue, since the contour represent spectrally monochromatic colors in order of wavelenght.

I took @Mr.Wizard's solution of getting a more faithful spectral colors than the default VisibleSpectrum color scheme

ChromaticityPlot;
newVisibleSpectrum = With[{colors = {ImageColorOperationsDump\$wavelengths, XYZColor @@@ ImageColorOperationsDumptris}\[Transpose]}, Blend[colors, #] &];


I extracted the colors from 380nm to 780nm in steps of five

nmcolors = Table[newVisibleSpectrum@x, {x, 380, 780, 5}];


Here's the normalized data

list = Most@Transpose[{r/(r + g + b), g/(r + g + b), b/(r + g + b)}];


And if I plot the points with their true colors

ListPointPlot3D[{#} & /@ list, PlotStyle -> nmcolors]


Now comes the hard part: color the area between the points. I tried many things and none seemed to work, the best plot I got was through joining opposing points with lines that were colored blending the colors of the extremes

Graphics3D[Table[{Thick, Line[{Part[list, i], Part[list, -i]},VertexColors -> {Part[nmcolors, i], Part[nmcolors, -i]}], If[i > 14, {Text[380 + (5*i), 1.1*Part[list, i]],Text[780 - (5*i), 1.1*Part[list, -i]]}]}, {i, 1, 40}],Boxed -> False]


(the if cycle is to avoid cramping the wavelenghts labels of the colors at the ends of the contour).

And here it is, my best plot so far

Notice it has all the right colors in the contour, starting with violet. The problem, though, is that it is not fully colored, it could be solved by making an interpolation of the original data which is separated by 5 nm, and then reapply this method. I don't know if that's the properly way to do a chromaticity plot, so that's why I leave this question open.