4

I'm trying to create a slanted shading effect on a tikz picture. I already have this effect working for the top and bottom, but have not found a way to get this working right for the left and right one. enter image description here

Here's the code I used for this:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shadings, patterns}
\begin{document}
    \definecolor{lightblue}{rgb}{0,0.8,1}
    \definecolor{grey}{rgb}{0.9,0.9,0.9}
    \begin{tikzpicture}
        \shade[bottom color=black!0,top color=black!50] (0,0) -- (-0.25,-0.25) -- (3.75,-0.25) -- (4,0);
        \shade[bottom color=black!50,top color=black!0] (2,2) -- (2.25,2.25) -- (6.25,2.25) -- (6,2);
        \draw[draw=black!50!grey,-,dashed] (2,2) -- (1.5,2);
        \draw[draw=black!50!grey,-,dashed] (6,2) -- (6.5,2);
        \draw[draw=black!50!grey,-,dashed] (0,0) -- (-0.25,-0.25);
        \draw[draw=black!50!grey,-,dashed] (2,2) -- (2.25,2.25);
        \draw[draw=black!50!grey,-,dashed] (6,2) -- (6.25,2.25);
        \draw[draw=black!50!grey,-,dashed] (4,0) -- (3.75,-0.25);
        \draw[draw=black!50!grey,-,dashed] (4,0) -- (4.5,0);

        \node at (2,2)[circle,fill,inner sep=1.5pt]{};
        \draw[pattern={north west lines},pattern color=lightblue,draw=none] (0,0) -- (2,2) -- (6,2) -- (4,0);
        \node at (2,2)[circle,fill,inner sep=0.8pt]{};
        \draw (0,0) -- (2,2);
        \node at (0,0)[draw=blue,circle,fill=blue,inner sep=1.5pt]{};
        \draw (2,2) -- (6,2);
        \draw (4,0) -- (0,0);
        \node at (0,0)[draw=blue,circle,fill=blue,inner sep=0.6pt]{};

        \node at (6,2)[circle,fill,inner sep=1.5pt]{};
        \draw[draw=green, thick] (6,2) -- (4,0);
        \node at (6,2)[circle,fill,inner sep=0.8pt]{};
        \node at (4,0)[circle,fill,inner sep=1.5pt]{};

        \draw[draw=red,thick,->] (3,1) -- (3,3);
        \draw[thick,draw=red] ([xshift=2mm]3,1) arc[start angle=0,end angle=90,radius=2mm];
        \node[draw=red,fill=red,inner sep=0.25pt,circle] at (3.0707,1.0707) {};

        \draw[draw=green,thick,->,dashed] (5,1) -- (7,1) node[right] {Edge};
        \draw[draw=red,thick,->,dashed] (3,2.5) -- (7,2.5) node[right] {Normal Vector};
        \draw[draw=blue,thick,->,dashed] (0,0) -- (-1,0) node[left] {Vertex};
        \draw[draw=lightblue,thick,->, dashed] (2,1.5) -- (-1,1.5) node[left] {Face};
    \end{tikzpicture}
\end{document}

I have tried to use something like \shade[right color=black!100,left color=black!0,xslant=1,shading angle=45] (-0.5,0) rectangle (0,2);, but the shading doesn't slant with the parameter, but rather creates a cutout of the shade that is slanted: enter image description here

Does anyone have an idea on how I can get the desired effect?

Thank you!

2
  • 1
    The start for the white is in the upper left corner of the rectangle around your path. Take a look at \shade[right color=black,left color=white,xslant=1,shading angle=45] (-0.5,0) rectangle (0,2); \clip (-0.5,1) rectangle ++ (1.5,1); \shade[right color=black,left color=white,draw=red,shading angle=45] (-0.5,0) rectangle ([xslant=1]0,2);. The shading will be calculated across the rectangle bounding box around your path, irregardless of any transformations. Mar 21 at 18:43
  • 1
    In complement to the excellent comment from Qrrbrbirlbrl, take a look at this answer: tex.stackexchange.com/a/300094/132405. it is regrettable that the official Tikz manual does not mention it.
    – quark67
    Mar 21 at 18:56

1 Answer 1

6

The shading is always applied to the rectangular path bounding box that encapsulates the whole path. This will always be orthogonal to the paper, irregardless of any transformations

This can be seen with the follwing example where I'll be using some annoying colors so that it is much more noticable (though maybe not for colorblind people).

All these three paths have the same rectangular bounding box: from (0, 0) to (2, 2) in the paper coordinate system. Any transformations will not apply to them. (You can however \rotatebox the whole TikZ picture which will also rotate the shading.)

\documentclass[tikz]{standalone}
\usetikzlibrary{shadings}
\begin{document}
\begin{tikzpicture}[bottom color=magenta, top color=green, shading angle=45, very thin]
\shade[draw=blue,      ] (0,0) -- (1,0) -- (2,2) -- (1,2) -- cycle;
\shade[draw=red, dashed] (0,2) rectangle +( 1.5,-1) (2,0);
\shade[draw=red, dashed] (2,0) rectangle +(-1.5, 1) (0,2);
\end{tikzpicture}
\end{document}

enter image description here

Without delving to deep into how PGF/TikZ creates its drawing and trying to \rotatebox only a part of the picture, in this example I'd just create a custom shading that has extra padding on the bottom and the top side (which will get rotated to be the upper left and lower right side).

The shadings already have a padding of 25% on both sides since the shading will be stretched across the path for reasons which are given in the explanation of \pgfshadepath which are basically the counterpoint to your problem. And it also has four examples that try to explain the same problem you have but, in my opinion, some bad ones were chosen.

Either way, by trying a bunch of numbers, I've come up with 46bp and (mirrored) 54bp which seem to cover your use-case best.


I've also rewritten your diagram because it was very confusing since you've only used raw coordinates.

The in plane style sets up the coordinate system so that a squared rectangle (i.e. one from (0, 0) to (2, 2) still looks square in perspective.

I want to also point you to the 3d library and just the keys x, y and z with which you can just set up a coordinate system where you wouldn't have to think of it as slanting but as normal coordinates in 3d.

Since all these coordinates might get confusing, I've added a debug node key (commented out below) that adds a small magenta label with the name above every coordinate/node. Some of them are already covered by other parts of the diagram. Best to work your way from top to bottom and try to retrace the steps I've taken.

Code

\documentclass[tikz]{standalone}
%\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shadings, patterns, angles}
\pgfdeclareverticalshading[%
  tikz@axis@top,tikz@axis@middle,tikz@axis@bottom]{axis padded}{100bp}{%
  color(0bp)=(tikz@axis@bottom);
  color(46bp)=(tikz@axis@bottom); % ← adjust as needed
  color(50bp)=(tikz@axis@middle);
  color(54bp)=(tikz@axis@top);    % ← adjust as needed
  color(100bp)=(tikz@axis@top)}
%\tikzset{debug node/.style={append after command={
%  \pgfextra\begingroup\pgftransformreset
%    \pgftext[at=\pgfpointanchor{\tikzlastnode}{north},bottom]%
%      {\tiny\color{magenta}\tikzlastnode}\endgroup\endpgfextra}}}
\begin{document}
\definecolor{lightblue}{rgb}{0,0.8,1}
\definecolor{grey}{rgb}{0.9,0.9,0.9}
\begin{tikzpicture}[
  in plane/.style={xslant=1, xscale=2},
%  nodes=debug node,
  bottop/.style args={#1:#2}{bottom color={#1},top color={#2}},
  DOT/.style={circle, fill=black, inner sep=+0pt, minimum size=+1.5mm}
]
\begin{scope}[in plane]
\coordinate (BL) at (0,0)
 coordinate (BR) at (2,0)
 coordinate (TR) at (2,2)
 coordinate (TL) at (0,2);
\shade[bottop=white:black!50] (BL) rectangle + (2,-.25) coordinate (BL-BR);
\shade[bottop=black!50:white] (TL) rectangle + (2, .25) coordinate (TL-TR);
\shade[bottop=black!50:white, shading=axis padded, shading angle=45] (BL) rectangle + (-.25, 2);
\shade[bottop=black!50:white, shading=axis padded, shading angle=-135] (BR) rectangle + (.25, 2);
\path[pattern=north west lines, pattern color=lightblue] (BL) rectangle (TR);
\foreach[
  remember=\dirA as \dirB (initially left)
] \DOT/\dirA in {BL/down, BR/right, TR/up, TL/left}
  \path[gray, dashed] (\DOT) edge + (\dirA:.25)
                             edge + (\dirB:.25);
\end{scope}
\begin{scope}[thick]
  \draw[green] (BR) -- coordinate (BR-TR-mid) (TR);
  \draw        (TR) -- (TL) -- (BL) -- (BR);
  \draw[->, dashed, green] (BR-TR-mid) -- ++(right:2)
    node[right, black] (Edge)   {Edge};
  \draw[->, dashed, blue]  (BL)        -- ++(left:1)
    node[left, black]  (Vertex) {Vertex};
  \draw[->, dashed, cyan]  ([in plane]0.5,1.5) coordinate (@)
    -- (@-|Vertex.east) node[left, black] {Face};
  \draw[->, red] ([in plane]1,1) coordinate (center)
    -- coordinate[near end] (noVec) ++ (up:2);
  \pic[draw, angle radius=2mm, red, pic text=.] (@) {angle=Edge--center--noVec};
  \draw[->, dashed, red]   (noVec) -- (noVec-|Edge.west)
    node[right, black]{Normal Vector};
\end{scope}
\foreach[/tikz/DOT BL/.style=blue] \DOT in {BL, BR, TR, TL}
  \node[DOT, DOT \DOT/.try] at (\DOT) (\DOT'){};
\end{tikzpicture}
\end{document}

Output

enter image description here enter image description here

1
  • Thank you very much, this is fantastic! I also really appreciate the detailed explanation. Mar 21 at 20:27

You must log in to answer this question.

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