1

I'm looking to make a rectangular prism that has color shading, with red at the bottom of the entire prism and blue at the top. So I adapted the following code from here

\documentclass{report}
\usepackage{tikz}
\usetikzlibrary{3d}
\begin{tikzpicture}[x  = {(2cm,1.5cm)},
y  = {(5.75cm,0cm)},
z  = {(0cm,3cm)}]
\begin{scope}[canvas is yz plane at x=-1]
\shade[bottom color = red!50, top color = blue!50] (-1,-1) rectangle (1,1);
\end{scope}
\begin{scope}[canvas is xz plane at y = 1]
\shade[bottom color = red!50, top color = blue!50] (-1,-1) rectangle (1,1);
\end{scope}
\begin{scope}[canvas is yx plane at z = 1]
\fill[blue!50] (-1,-1) rectangle (1,1);
\end{scope}
\end{tikzpicture}

which yields

enter image description here

Now, there are some issues with the above picture that I was hoping someone could help me with.

  1. The first is that the left and right edges of the top (lid) of the prism aren't parallel, and changing the dimensions in the tikz picture never fixes this. Why is this and how can it be fixed?
  2. I actually want the shading to be red along the entire bottom of the prism and blue along the entire top. The colouring on front face and the top are both fine. However, the side face has red in the top left corner of the face, when it should be blue to match the colour on the top. I played around with the shadings but to no avail, so I was wondering if this was fixable too?
  3. I was hoping to bold the edges of the prism to outline where each face begins and ends, though I am having trouble finding code that works with what I have already done.
  4. Finally, is it possible to get the 'hidden' edges put in the back of the cuboid? By hidden, I mean those three edges from the back face that meet the bottom and top of the prism.

Sorry for the many questions, I'm not very competent with tikz yet. Thanks in advance.

EDIT:

Thanks to the responses below. I managed to get the hidden sides on the picture, as well as the colouring on the bottom of the side face correct. However, the colouring at the top of the side face isn't correct, it is too red and doesn't match the colour of the top face (lid) of the cuboid. Here is the code

\begin{tikzpicture}[join = round, xscale = 12, yscale = 6]

\colorlet{bcolor}{red!60}   
\colorlet{tcolor}{blue!100}
\colorlet{ttcolor}{blue!100}

\shadedraw[bottom color = bcolor, top color = tcolor]
(-1,-1) coordinate (A) rectangle (0,0);     
\shadedraw[xscale = 0.5, yslant = 0.5, bottom color = bcolor, top color = tcolor, shading angle = 25]
(0,-1) rectangle (1,0) 
(1,-1) coordinate (B)
(-1,0) coordinate (O);      
\draw[yscale = 0.5, xslant = 0.5, fill = ttcolor] (-1,0) rectangle (0,1) (-1,1) coordinate (C);

\draw[dashed, opacity = 0.7] (O)--(A) (O)--(B) (O)--(C);
\end{tikzpicture}   

which gives

enter image description here

The shade at the top of the side face (touching the lid of the cuboid) should be a darker blue, to match the colour of the lid.

3
  • I am not a TikZ expert (TikZpert?) myself, far from it – but I think the top has parallel edges: It seems to be an optical illusion, perhaps caused by the undesirable colour gradient at the right face, that makes it appear otherwise. Feb 2 at 12:52
  • @HaraldHanche-Olsen Thanks for the reply. I agree, after more careful consideration I think they are parallel and it was just a bit difficult to discern.
    – mattos
    Feb 2 at 23:07
  • I think the visual illusion is much less now, though not completely gone. This supports my hypothesis that it is (at least partly) caused by the shading on the right face confusing the brain. That is much better now that the shading is more “logical”. Feb 3 at 10:11
4

You can transform shadings. However, this requires you to resort to transform canvas, which is a bit "violent". The lower level command corresponding to transform canvas is \pgflowlevelsynccm. This is an untuned version with an orthographic projections.

enter image description here

\documentclass[tikz,border=3mm]{standalone}
\usepackage{tikz-3dplot}
\begin{document}
\tdplotsetmaincoords{70}{110}
\begin{tikzpicture}[tdplot_main_coords,line cap=round,line join=round,
    fill opacity=0.8,scale=2]
 \path foreach \X in {-1,1} {foreach \Y in {-1,1} 
 {foreach \Z in {-1,1} {(\X,\Y,\Z) coordinate (p\X\Y\Z)}}};
 \draw[dashed] (p-11-1) -- (p-1-1-1) edge (p-1-11) -- (p1-1-1);
 \begin{scope}[canvas is xz plane at y=1]
  \pgflowlevelsynccm
  \draw[bottom color = red, top color = blue]  (-1,-1) rectangle (1,1);
 \end{scope}
 \begin{scope}[canvas is yz plane at x=1]
  \pgflowlevelsynccm
  \draw[bottom color = red, top color = blue]  (-1,-1) rectangle (1,1);
 \end{scope}
 \begin{scope}[canvas is xy plane at z=1]
  \draw[fill=blue]  (-1,-1) rectangle (1,1);
 \end{scope}
\end{tikzpicture}
\end{document}

OLDER VERSIONS:

enter image description here

\documentclass[tikz,border=3mm]{standalone} 
\usetikzlibrary{3d}
\begin{document}
\begin{tikzpicture}[x  = {(2cm,1.5cm)},
    y  = {(5.75cm,0cm)},
    z  = {(0cm,3cm)}]
  \begin{scope}[canvas is yz plane at x=-1]
   \shade[bottom color = red!50, top color = blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
  \begin{scope}
   \clip[canvas is xz plane at y = 1] (-1,-1) rectangle (1,1);
   \shade[transform canvas={canvas is xz plane at y = 1},
     bottom color = red!50, top color = blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
  \begin{scope}[canvas is yx plane at z = 1]
   \fill[blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
\end{tikzpicture}
\end{document}

In this version the color spectrum is not quite correct, as you noted, since parts are clipped away.

Here is a second version which is free of that problem, but very tuned. There are two reasons for the tuning: transform canvas is violent and the 3d projection is non-orthographic, so I did not find a good way to invert it. However, this (admittedly tuned and hence ugly coded) version substantiates once more that one can use canvas transformations to transform shadings.

enter image description here

\documentclass[tikz,border=3mm]{standalone} 
\usetikzlibrary{3d,calc}
\begin{document}
\begin{tikzpicture}[x  = {(2cm,1.5cm)},
    y  = {(5.75cm,0cm)},
    z  = {(0cm,3cm)}]
  \begin{scope}[canvas is yz plane at x=-1]
   \shade[bottom color = red!50, top color = blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
  \begin{scope}
   \path [canvas is xz plane at y = 1] (0,0) coordinate (BL) (0,1) coordinate (TL)
    (1,0) coordinate (BR) (1,1) coordinate (TR);
   \path  let \p1=($(BR)-(BL)$) in  
   [canvas is xz plane at y = 1,yslant={-2*\y1/\x1/3},yshift=6.5pt]
   (-1,-1) coordinate (BL') (-1,1) coordinate (TL')
    (1,-1) coordinate (BR') (1,1) coordinate (TR');
   \shade let \p1=($(BR)-(BL)$) in
   [transform canvas={yslant={\y1/\x1},yshift=-5cm},
     bottom color = red!50, top color = blue!50] 
     (BL') -- (BR') -- (TR') -- (TL') -- cycle;
  \end{scope}
  \begin{scope}[canvas is yx plane at z = 1]
   \fill[blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
\end{tikzpicture}
\end{document}

Or with some lines.

\documentclass[tikz,border=3mm]{standalone} 
\usetikzlibrary{3d,calc}
\begin{document}
\begin{tikzpicture}[x  = {(2cm,1.5cm)},
    y  = {(5.75cm,0cm)},
    z  = {(0cm,3cm)},fill opacity=0.7,line cap=round,line join=round]
  \begin{scope}[canvas is yz plane at x=1]
   \draw[dashed]  (1,-1) -- (-1,-1) coordinate (B) -- (-1,1);
  \end{scope}
   \draw [dashed] (-1,-1,-1) -- (B);
  \begin{scope}
   \path [canvas is xz plane at y = 1] (0,0) coordinate (BL) (0,1) coordinate (TL)
    (1,0) coordinate (BR) (1,1) coordinate (TR);
   \path  let \p1=($(BR)-(BL)$) in  
   [canvas is xz plane at y = 1,yslant={-2*\y1/\x1/3},yshift=6.5pt]
   (-1,-1) coordinate (BL') (-1,1) coordinate (TL')
    (1,-1) coordinate (BR') (1,1) coordinate (TR');
   \shade let \p1=($(BR)-(BL)$) in
   [transform canvas={yslant={\y1/\x1},yshift=-5cm},
     bottom color = red!50, top color = blue!50] 
     (BL') -- (BR') -- (TR') -- (TL') -- cycle;
  \end{scope}
  \begin{scope}[canvas is yz plane at x=-1]
   \draw[bottom color = red!50, top color = blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
  \begin{scope}[canvas is yx plane at z = 1]
   \draw[fill=blue!50] (-1,-1) rectangle (1,1);
  \end{scope}
  \draw (TR) -- (1,1,-1) -- (-1,1,-1);
\end{tikzpicture}
\end{document}

enter image description here

7
  • (+1) Thank you very much for your response. Yours definitely looks better than what I had, though it still not quite what I am after. In your picture, on the side face (corresponding to 'canvas is xz plane at y = 1'), the 'bottom right' corner of the face is where it is most red and the 'top left' corner of the face is where it is most blue. However, I was hoping to have a red hue all the way along the face from the 'bottom left' to the 'bottom right' and all the way along the face from the 'top left' to the 'top right' in the blue hue. Is there anyway to do this?
    – mattos
    Feb 2 at 22:39
  • @mattos Yes, you are right. The reason is that the clip removes part of the color spectrum. I added a second version in which this problem is solved, at the expense of tuning. One could avoid the tuning. The cleanest way of accomplishing this would be to improve the transform canvas function. The next-cleanest version would be to install orthographic projections, which can be inverted analytically, so that one could find a precise analytic canvas transformation that projects on the planes.
    – user234180
    Feb 3 at 1:06
  • Thanks for the update, I appreciate your help. I think 'shading angle' might be a cleaner way to code in what I was after (see the edit to my post above), though I was unable to get it to work with the transform canvas function.
    – mattos
    Feb 3 at 1:18
  • @mattos The shading angle rotates the shading, and does not tilt it. But with a clip you can make it presumably work, too, at least in this case.
    – user234180
    Feb 3 at 1:35
  • Thanks for your help, the shading you created is exactly what I was after. One last question, is it possible to thicken the edges and also get the 'hidden' edges in your diagram? Like the ones found in the second diagram in the other answer below? I tried modifying the \draw[dashed, opacity=0.7] code used in that question but because the vertices in your answer don't start in the same place I had difficulty drawing them in.
    – mattos
    Feb 3 at 5:19
3

How about this? I use plain TikZ to play around with geometric transformations, and add another color for the top of the cuboid.

enter image description here

\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}
\colorlet{bcolor}{red!50}   
\colorlet{tcolor}{blue!50}
\colorlet{ttcolor}{blue!55}

\shade[bottom color=bcolor,top color =tcolor] (-1,-1) rectangle (0,0);      
\shade[xscale=.5,yslant=.5,bottom color=bcolor,top color=tcolor] (0,-1) rectangle (1,0);        
\fill[yscale=.5,xslant=.5,ttcolor] (-1,0) rectangle (0,1);
\end{tikzpicture}   
\end{document}  

For hidden lines, you can attach necessary points under action of the above geometric transformation, and use opacity. I add middle color as OP's request.

enter image description here

\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}[join=round,scale=2]
\colorlet{tcolor}{blue}   
\colorlet{bcolor}{red}
\colorlet{mcolor}{bcolor!50!tcolor}

\shadedraw[bottom color=bcolor,middle color=mcolor,top color =tcolor]
(-1,-1) coordinate (A) rectangle (0,0);     
\shadedraw[xscale=.5,yslant=.5,bottom color=bcolor,middle color=mcolor,top color=tcolor]
(0,-1) rectangle (1,0) 
(1,-1) coordinate (B)
(-1,0) coordinate (O);      
\draw[yscale=.5,xslant=.5,fill=tcolor] (-1,0) rectangle (0,1) (-1,1) coordinate (C);

\draw[dashed,opacity=.5] (O)--(A) (O)--(B) (O)--(C);
\end{tikzpicture}   
\end{document}

Note that these are still 3D-like effect. You may use Asymptote to get more real-3D-feeling figure.

5
  • (+1) Thanks for the response. The hidden lines look great. I was wondering if it is possible to get the shading correct though, as it still doesn't match what I am after. In both of the pictures in your post, on the side facing to the right of the screen, the bottom left corner of that side is red (yellow) and the top right cornet is blue (teal). What I'm after is the bottom left and the bottom right corners being red (yellow) and the top left and top right corners being blue (teal) so that the colour matches the top/lid of the cuboid. Is this possible?
    – mattos
    Feb 2 at 23:05
  • I'm essentially looking to have the entire bottom half of the cuboid in one colour (red) and the entire top half of the cuboid in another (blue).
    – mattos
    Feb 2 at 23:06
  • @mattos I added middle color. Is that what you want?
    – Black Mild
    Feb 3 at 0:20
  • Not quite. I actually got the color angle correct on the side face by using 'shading angle = 25' i.e I used \shadedraw[xscale=.5,yslant=.5,bottom color=bcolor,top color=tcolor, shading angle=25] (0,-1) rectangle (1,0) (1,-1) coordinate (B) (-1,0) coordinate (O);, however the coloring on the top of the side face no longer matches the color on the top face of the cuboid. I've updated my original post, I apologise if it wasn't clear originally.
    – mattos
    Feb 3 at 0:31
  • I should have stated that using middle color in conjunction with shading angle doesn't actually make the top of the side face the correct colour, it is still too red and looks exactly the same as the edit in my original post.
    – mattos
    Feb 3 at 0:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.