5

I am currently trying to draw a node shaped like a "baseless" triangle or angle bracket shape for a Wigmore chart (a chart representing argumentation at trial). In the example below, I'd ideally like to remove the right-hand edge of the triangle beside node 7 (to show an attack, rather than support).

Example Wigmore chart

I've looked at the pgf manual, section 75.5, that describes defining new node shapes, but I'm finding it extremely complicated. Is there a simpler way, such as a package or working example I've missed?

  • Make sure to include where you want to connect lines to the node (relative to to text). – John Kormylo Nov 25 '13 at 17:07
  • @Jake Yeah, sorry, that was a lapse on my part. I'll update the question so it's clearer for future viewers – Landric Nov 26 '13 at 10:15
6

There is no doubt that creating new shapes is quite involved, but then they do have to be quite versatile.

The following is incomplete and probably not commented enough to be truly helpful. Nevertheless I took the symbols from Chalamish etal (2011):

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}
\makeatletter
\newif\ifpgfshapebaselesstrianglehasinline
\newif\ifpgfshapebaselesstriangleclose
\pgfkeys{/pgf/.cd,
  baseless triangle apex angle/.style={/pgf/isosceles triangle apex angle=#1},
  baseless triangle inline/.is if=pgfshapebaselesstrianglehasinline,
  baseless triangle has base/.is if=pgfshapebaselesstriangleclose
}

\pgfdeclareshape{baseless triangle}{
  % Copy some stuff from the isosecles triangle
  \inheritsavedanchors[from={isosceles triangle}]
  \inheritanchor[from={isosceles triangle}]{center}
  \inheritanchor[from={isosceles triangle}]{north}
  \inheritanchor[from={isosceles triangle}]{south}
  \inheritanchor[from={isosceles triangle}]{east}
  \inheritanchor[from={isosceles triangle}]{west}
  \inheritanchorborder[from={isosceles triangle}]
  \backgroundpath{%
    % The isoceles triangle defines lots of parameters
    % in the \trianglepoints macro.
        \trianglepoints%
        {%
            \pgftransformshift{\centerpoint}%
            \pgftransformrotate{\rotate}%
            % This bit is a bit of a kludge to ensure the inline
            % is at the top of the figure.
            \pgftransformyscale{cos(\rotate)}%
            \pgfpathmoveto{\lowerleft}%
            \pgfpathlineto{\apex}%
            \pgfpathlineto{\lowerleft\pgf@y=-\pgf@y}%
            % Close the base?
            \ifpgfshapebaselesstriangleclose%
              \pgfpathclose%
            \fi%
            % Draw the inline?
            \ifpgfshapebaselesstrianglehasinline
              \pgfpointdiff{\lowerleft}%
                 {\pgfpointlineattime{0.125}{\lowerleft}{\lowerleft\pgf@y=-\pgf@y}}%
                \pgfgetlastxy{\x}{\y}%
                \pgfmathveclen{\x}{\y}%
                \let\inlineshift=\pgfmathresult%
            % Calculate where the inline hits the sloped line of the triangle.
            \pgfmathparse{\inlineshift/2/sin(\pgfkeysvalueof{/pgf/isosceles triangle apex angle}/2)}%
            \let\inlineendshift=\pgfmathresult
            \pgfpathmoveto{\pgfpointadd{\pgfpoint{0pt}{-\inlineshift}}{\lowerleft}}%
            \pgfpathlineto{\pgfpointlineatdistance{\inlineendshift}{\apex}{\lowerleft}\pgf@y=-\pgf@y}%
        \fi%
    }
    }
}


\pgfdeclareshape{inline circle}{
  % Copy some stuff from the circle
  \inheritsavedanchors[from={circle}]
  \inheritanchor[from={circle}]{center}
  \inheritanchor[from={circle}]{north}
  \inheritanchor[from={circle}]{south}
  \inheritanchor[from={circle}]{east}
  \inheritanchor[from={circle}]{west}
  \inheritanchorborder[from={circle}]
  \backgroundpath{%
     \pgftransformshift{\centerpoint}%
     \pgfmathsetlengthmacro\radius{\radius-max(\pgfkeysvalueof{/pgf/outer xsep},\pgfkeysvalueof{/pgf/outer ysep})}%  
     \pgfpathcircle{\centerpoint}{\radius}%
     \pgfpathmoveto{\pgfpointpolar{45}{\radius}}%
     \pgfpathlineto{\pgfpointpolar{135}{\radius}}%
    }
}


\pgfdeclareshape{inline rectangle}{
  % Copy some stuff from the rectangle
  \inheritsavedanchors[from={rectangle}]
  \inheritanchor[from={rectangle}]{center}
  \inheritanchor[from={rectangle}]{north}
  \inheritanchor[from={rectangle}]{south}
  \inheritanchor[from={rectangle}]{east}
  \inheritanchor[from={rectangle}]{west}
  \inheritanchorborder[from={rectangle}]
  \backgroundpath{%
     \pgfmathsetlengthmacro\outerxsep{\pgfkeysvalueof{/pgf/outer xsep}}%
     \pgfmathsetlengthmacro\outerysep{\pgfkeysvalueof{/pgf/outer ysep}}%
     \pgfpointadd{\southwest}{\pgfpoint{\outerxsep}{\outerysep}}%
     \pgfgetlastxy\a\b
     \pgfpointadd{\northeast}{\pgfpointscale{-1}{\pgfpoint{\outerxsep}{\outerysep}}}%
     \pgfgetlastxy\c\d
     \pgfpathrectanglecorners{\pgfpoint{\a}{\b}}{\pgfpoint{\c}{\d}}%
     \pgfpathmoveto{\pgfpoint{\a}{\b+(\d-\b)*0.875}}%
     \pgfpathlineto{\pgfpoint{\c}{\b+(\d-\b)*0.875}}%
    }
}





\tikzset{
  defense explanatory/.style={
    draw,
    baseless triangle,
    baseless triangle apex angle=60,
    baseless triangle inline=true,
    baseless triangle has base=false
  },
  prosecution explanatory/.style={
    draw,
    baseless triangle,
    baseless triangle apex angle=60,
    baseless triangle inline=false,
    baseless triangle has base=false
  },
    defense corroborative/.style={
      draw,
      shape border rotate=180,
      baseless triangle,
      baseless triangle apex angle=60,
      baseless triangle inline=true,
      baseless triangle has base=true
    },
    prosecution corroborative/.style={
      draw,
      shape border rotate=180,
      baseless triangle,
      baseless triangle apex angle=60,
      baseless triangle inline=false,
      baseless triangle has base=true
    },
    defense circumstantial/.style={
      draw,
      inline circle,
      minimum size=0.5cm
    },
    prosecution circumstantial/.style={
      draw,
      circle,
      minimum size=0.5cm
    },
    defense testimonial/.style={
        draw,
        inline rectangle,
        minimum width=.375cm,
        minimum height=0.5cm
    },
    prosecution testimonial/.style={
        draw,
        rectangle,
        minimum width=.375cm,
        minimum height=0.5cm
    }
}
\begin{document}


\begin{tabular}{llcc}
&& Defense & Prosecution \\\\
1. & Testimonial & \tikz\node[defense testimonial] {}; & \tikz\node[prosecution testimonial] {}; \\
2. & Circumstantial & \tikz\node[defense circumstantial] {}; & \tikz\node[prosecution circumstantial] {}; \\
3. & Explanatory & \tikz\node [defense explanatory]  {}; &  \tikz\node[prosecution explanatory] {}; \\
4. & Corroborative & \tikz\node [defense corroborative]  {}; &  \tikz\node[prosecution corroborative] {}; \\
\end{tabular}


\end{document}

enter image description here

Alternatively, if we are careful about the outer sep (i.e., it is small or 0pt) it is possible to add "annotations" to nodes after they are drawn, so no new shapes are required:

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,shapes.geometric}
\tikzset{node annotation/.style={%
 alias=last node,% Ensure node has name
 append after command={%
      \pgfextra{\pgfinterruptpath\pgfscope#1\endpgfscope\endpgfinterruptpath}
}}}


\tikzset{%
  defense explanatory/.style={
     isosceles triangle, 
     isosceles triangle apex angle=60,
     draw=none,
     node annotation={%
       \draw (last node.left corner) -- (last node.apex)
         -- (last node.right corner);
       \draw let \p1=(last node.apex), \p2=(last node.right corner),
         \n1={veclen(\x2-\x1,\y2-\y1)} in ($(\p1)!0.125!(\p2)$) 
         -- ++ (150:\n1*sin 60);
     }
    },
    prosecution explanatory/.style={
        isosceles triangle, 
        isosceles triangle apex angle=60,
        draw=none,
        node annotation={% Don't really need this
          \draw (last node.left corner) -- (last node.apex)
            -- (last node.right corner) -- cycle;
        }
    },
    defense corroborative/.style={
        isosceles triangle, 
        isosceles triangle apex angle=60,
        draw=none,
        node annotation={%
         \draw (last node.left corner) -- (last node.apex)
           -- (last node.right corner) -- cycle;
         \draw let \p1=(last node.apex), \p2=(last node.right corner),
           \n1={veclen(\x2-\x1,\y2-\y1)} in ($(\p1)!0.125!(\p2)$) 
           -- ++ (150:\n1*cos 30);
        }
    },
    prosecution corroborative/.style={
        isosceles triangle, 
        isosceles triangle apex angle=60,
        draw=none,
        node annotation={% Don't really need this
        \draw (last node.left corner) -- (last node.apex)
            -- (last node.right corner) -- cycle;
        }
    },
    defense circumstantial/.style={
        circle,
        minimum size=.5cm,
        draw=none,
        node annotation={%
         \clip [preaction=draw]let \p1=(last node.east), \p2=(last node.west), \n1={\x2-\x1} in
          (last node) circle [radius=\n1/2];
       \draw (last node.45) -- (last node.135);
        }
    },
    prosecution circumstantial/.style={
      minimum size=.5cm,
        circle,
        draw,
    },
    defense testimonial/.style={
        rectangle,
        minimum height=0.5cm,
      minimum width=0.375cm,
        draw,
        node annotation={%
            \draw let \p1=($(last node.north west)!0.125!(last node.south west)$),
              \p2=($(last node.north east)!0.125!(last node.south east)$) in
               (\p1) -- (\p2);
        }
    },
    prosecution testimonial/.style={
      minimum height=0.5cm,
      minimum width=0.375cm,
        rectangle,
        draw,
    }
}

\begin{document}



\begin{tabular}{llcc}
&& Defense & Prosecution \\\\
1. & Testimonial & \tikz\node[defense testimonial] {}; & \tikz\node[prosecution testimonial] {}; \\
2. & Circumstantial & \tikz\node[defense circumstantial] {}; & \tikz\node[prosecution circumstantial] {}; \\
3. & Explanatory & \tikz\node [defense explanatory]  {}; &  \tikz\node[prosecution explanatory] {}; \\
4. & Corroborative & \tikz\node [defense corroborative]  {}; &  \tikz\node[prosecution corroborative] {}; \\
\end{tabular}


\end{document}

The result will be (roughly) the same as before.

  • Works like a charm - I had to tweak it a little to get it working in my simple example, but this is perfect. I particularly appreciate your work on the other symbols too! – Landric Nov 26 '13 at 11:10

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.