20

How do I draw a triangle in tikz with "point colours" so that every point in the triangle is coloured according to the weighted (by distance) average colour of the corners.

5
  • This sounds like a very difficult problem. What you want is to basically recreate the RGB triangle for aribtrary vertex colours. This sounds very tricky. Could you explain why you want to do this?
    – Seamus
    Mar 30, 2011 at 22:07
  • I am nothing close to a pro, so please bear with me. But actually it might not be such a difficult problem if any pro guys here can address the color mixing problem. There is some hint in Tikz manual about \pgfdeclarefunctionalshading . What I have in mind is to apply clipping a triangle and and a gradient shading simultaneously. Then this can be rendered pretty fast compared to pointwise coloring. There is this section about fading in the manual but I am not sure about the availability.
    – percusse
    Mar 30, 2011 at 23:58
  • @Seamus: I just want the RGB triangle (with some other stuff overlayed.) I thought there might be some way of defining point colours in tikz, but I guess not....
    – Neil G
    Mar 31, 2011 at 0:00
  • @percusse: sounds really cool.
    – Neil G
    Mar 31, 2011 at 0:00
  • 1
    If tikz had a way to generate PDF type 4 shadings (free-form Gouraud-shaded triangle mesh), described in section 8.7.4.5.5 of the PDF spec, then this would be extremely simple. Getting the same result using type 1 (function-based) shading, as you get with \pgfdeclarefunctionalshading is possible, but more tricky (and harder for the PDF viewer to render).
    – Lev Bishop
    Mar 31, 2011 at 14:56

5 Answers 5

22

I feel that the other answers given are perhaps a little overcomplicated! If you want the triangle exact then maybe they are the best way to go. But if you want something that just looks about right, then there is a much simpler way to do this using ordinary fadings.

(Added in edit: I've updated this a little to try to correct the colour bias. The red colour is now correct and the green/blue are relatively correct. That is, the green and blue are correct at the bottom of the triangle, but as you move up the sides then some blue gets mixed in with the green and vice-versa. However, before it gets too noticeable, the red swamps the picture so it's actually quite close to the Real Thing.)

Here's the result:

RGB triangle in TikZ

Here's the code:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{fadings}
\begin{document}
\begin{tikzpicture}
\fill[green] (90:4) -- (210:4) -- (-30:4) -- cycle;
\fill[blue,path fading=west] (90:4) -- (210:4) -- (-30:4) -- cycle;
\fill[red,path fading=south] (90:4) -- (210:4) -- (-30:4) -- cycle;
\end{tikzpicture}
\end{document}
4
  • Good idea! Note however, that this also renders incorrectly with Evince (and probably anything else based on Poppler).
    – Caramdir
    Mar 31, 2011 at 16:28
  • This is really nice. But that's exactly what I meant by color mixing problem in my original comment. Above all, looks good enough.
    – percusse
    Mar 31, 2011 at 19:19
  • @percusse: I think that Caramdir's answer is more what you had in mind. Note that my answer was posted after the other ones and is very definitely intended as a "if you want something that just looks about right" answer, not a perfect one. Mar 31, 2011 at 19:25
  • 1
    Very nice example! I added it to the TikZ example gallery, I hope that's ok for you: texample.net/tikz/examples/rgb-triangle
    – Stefan Kottwitz
    Dec 22, 2011 at 0:14
15

PGF provides functional shadings. The following works by calculating the barycentric coordinates of a equilateral triangle from the Cartesian coordinates (see Wikipedia) and using those as the RGB color.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shadings}
\begin{document}
\begin{tikzpicture}
\pgfdeclarefunctionalshading{rgbtriangle}
{\pgfpointorigin}{\pgfpoint{100bp}{86.60bp}}{}{
    % y coordinate is on top of the stack, x below it
    % divide both by 100 to get numbers in [0,1]
    100 div exch 100 div exch
    % save a copy of the coordinates
    2 copy
    % calculate red amount
    0.5774 mul add neg 1 add
    % bring copy of the coordinates to the top
    3 1 roll
    % calculate green amount
    0.5774 mul neg add 
    % calculate blue as (1-red-green)
    2 copy
    add 1 sub neg
}
\clip[shift={(-50bp,{-25bp*sqrt(3)})}] (0,0) -- (50bp,{50bp*sqrt(3)}) -- (100bp,0) -- cycle;
\pgfuseshading{rgbtriangle}
\end{tikzpicture}
\end{document}

result in Adobe

The PGF manual has a warning:

These shadings are the least portable of all and they put the heaviest burden of the renderer. They are slow and, possibly, will not print correctly!

In fact, Evince (and probably most Linux pdf viewers) renders the above document as

result in Evince


I keep playing around with this. The most portable solution seems to be the one by Altermundus. Here it is encapsulated into a macro and with some optimizations

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document} 

% arguments:
%   number of subdivision (optional)
%   side length
\newcommand\colortriangle[2][50]{
    \begin{scope}[shift={({-#2/2},{-sqrt(3)/6*#2})}]
        \coordinate(A) at (0, 0);
        \coordinate(B) at (#2, 0);
        \coordinate(C) at (60:#2);
        \clip (A) -- (B) -- (C) -- cycle;
        \pgfmathsetmacro\delta{1/#1}
        \pgfmathsetmacro\r{\delta*1.2*#2}
        \edef\r{\r pt}
        \foreach \x in {0,\delta,...,1} {
            \pgfmathsetmacro\t{1-\x}
            \foreach \y in {0,\delta,...,\t} {
                \pgfmathsetmacro\z{1-\x-\y}
                \definecolor{mycolor}{rgb}{\x, \y, \z}      
                \coordinate (mypoint) at (barycentric cs:A=\x,B=\y,C=\z); 
                \path[fill=mycolor] (mypoint) rectangle ($(mypoint)+(\r,\r)$);
            }
        }
    \end{scope}
}

\begin{tikzpicture}
    \colortriangle[40]{4cm}
\end{tikzpicture}
\end{document}

For sufficiently small many subdivision, the triangle looks passably smooth and should render correctly in most PDF viewers. It does however draw O(subdivisions²) rectangles and compilation time scales accordingly. So you might want to use the externalization library of TikZ. The above example compiles in about 2.9s on my computer and produces

result

1
  • @Neil: Be aware of the warning: This will possibly not render and print correctly, in particular when not using Adobe Acrobat.
    – Caramdir
    Mar 31, 2011 at 3:10
11

I am fully aware that this post is quite old.

However, due to the remark of Lev Bishop that this would be very simple if TikZ would support "PDF type 4 shadings (free-form Gouraud-shaded triangle mesh)", I wanted to add a matching example.

pgfplots can generate type 4 shadings, and since version 1.8, it can do so for both colormaps and explicit colors (the latter has been added after pgfplots: color a (3D) surf using arbitrary RGB colors).

Here is a type 4 shading with explicitly colored vertices:

\documentclass{standalone}

\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture}
\begin{axis}[title=RGB shading]
    \addplot[
        patch,
        shader=interp,
        mesh/color input=explicit,
        data cs=polar,
    ]
    coordinates {
        (90,4) [color=red]
        (210,4) [color=green]
        (-30,4) [color=blue]
    };
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

The example above is a patch plot with interpolated shading. The coordinates are given in polar coordinates due to data cs=polar.

This example can be generalized easily to more than one triangle (or other primitive forms like rectangles or bezier shapes). Here is an example with different color specifiers which results in three triangular patches:

\documentclass{standalone}

\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}[minor x tick num=1]
    \addplot[
        patch,
        shader=interp,
        mesh/color input=explicit,
    ]
    table[meta=c] {
        x y c
        0 0 color=green
    % default color model is rgb:
        1 1 1,0,0
        2 0 1,1,0
        1.5 1 cmyk=1,0,0,0
        2.5 0 gray=0.5
        3.5 1 color=red!80!black
        3 0 1,0,1
        4 1 0,0,1
        5 0 rgb255=0,128,128
    };
\end{axis}
\end{tikzpicture}

\end{document}

enter image description here

Finally, this kind of shading is sometimes quite useful if you have scalar color values which are mapped into a colormap. In this scenario, the smallest scalar values gets the first color of the colormap, the largest the last color. Everything else is interpolated accordingly. A colormap typically has more than one color which participates. Here is also an example:

\documentclass{standalone}

\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture}
\begin{axis}[title=RGB shading with colormap,colorbar]
    \addplot[
        patch,
        shader=interp,
        point meta=explicit,
        data cs=polar,
    ]
    coordinates {
        (90,4) [0]
        (210,4) [1]
        (-30,4) [2]
    };
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

This last example makes it very clear that triangle interpolation is linear.

1
  • This is a solution which is very useful for data visualization. If someone wants to use my solution in order to insert such shadings neatlessly in a pure tikz picture without the rescaling applied by pgfplots (i.e. not data visualization), one would need extra steps which are discussed in the pgfplots manual, Section "TikZ Interoperability" Aug 11, 2013 at 6:41
6

Only for the fun but perhaps it's possible with postcript macros 84.2.3 General (Functional) Shadings of pgfmanual cvs. The code is very slow to compile a better will be to draw triangles (node) instead of circles. The Caramdir's answer proves that my idea was fine. I use the comment to change my answer, always for the fun ...This is perhaps always wrong but it's a more correct answer than my first one.

\documentclass{scrartcl}
\usepackage[usenames,dvipsnames]{xcolor}  
\usepackage{tikz}

\begin{document} 

\begin{tikzpicture}
\coordinate(A) at (0, 0);
\coordinate(B) at (1, 0);
\coordinate(C) at (60:1);

\foreach \x in {0.05,0.1,...,.95} 
\foreach \y in {0.05,0.1,...,.95}
\foreach \z in {0.05,0.1,...,.95}
{\definecolor{mycolor}{rgb}{\x, \y, \z}      
\coordinate (mypoint) at (barycentric cs:A=\x,B=\y,C=\z);
\path[fill=mycolor] (mypoint) circle (.04); }

\end{tikzpicture}
\end{document}

enter image description here

3
  • According to the problem description, the center should be RGB(0.33,0.33,0.33). You should have y ∈ [0,1-x] and z=1-x-y.
    – Caramdir
    Mar 31, 2011 at 2:58
  • I would like to make something like your answer but I don't know the postscript functions. Bravo for your answer!! Mar 31, 2011 at 5:12
  • @Caramdir I changed the code to give a solution better than the first one, perhaps it's possible to find something correct with this idea ? Mar 31, 2011 at 7:22
6

enter image description here

Just one more way to get a Gouraud-shaded triangle: tri.asy:

size(400);
pen[] p={red,green,blue};
pair[] z={(0,1),rotate(120)*(0,1),rotate(-120)*(0,1)};
int[] edges={0,1,2};
gouraudshade(z[0]--z[1]--z[2]--cycle,p,z,edges);

To get a standalone tri.pdf, run asy -f pdf tri.asy.

Edit (suggested by Andrew Stacey): Here asy is the command that invokes Asymptote.

1
  • 1
    For those, like myself, who've never used it, I'd like to add that asy is the command that invokes Asymptote. Aug 10, 2013 at 19:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .