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 Answers
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:
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}
-
Good idea! Note however, that this also renders incorrectly with Evince (and probably anything else based on Poppler).– CaramdirMar 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.– percusseMar 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
-
1Very 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
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}
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
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
-
@Neil: Be aware of the warning: This will possibly not render and print correctly, in particular when not using Adobe Acrobat.– CaramdirMar 31, 2011 at 3:10
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}
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}
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}
This last example makes it very clear that triangle interpolation is linear.
-
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
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}
-
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.– CaramdirMar 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
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
.
-
1For 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
\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 aboutfading
in the manual but I am not sure about the availability.\pgfdeclarefunctionalshading
is possible, but more tricky (and harder for the PDF viewer to render).