# How to make a vertical/horizontal line cap independent of how the paths lies?

Is it possible to make the line cap in TikZ always be vertical (or horizontal or a 90 degree edge)? Maybe as an option to line cap or even better as an arrow tip.

I imagine a type vertical resulting in some thing like this

horizontal could look like this

and a 90 degree corner, which is rotate so that one of it’s edges is parallel to the horizon. (Other that the triangle 90 degree cap.)

I created the images with a clipping, but clipping the path is no option …

-
The third looks possible, but the first two could be quite tricky because the shape you'd have to draw is somewhat difficult to compute. – Loop Space May 19 '12 at 18:05
@AndrewStacey: Thanks, I fear that. I guess it’s impossible to lengthen the path an cut it to get a vertcial/horizontal end, isn’t it? – Tobi May 21 '12 at 7:36
The difficulty would be knowing how to lengthen it. In your horizontal example, imagine that the actual path ended as the boundary of the curve first touched the horizontal line. Then you'd need to extend the line until the other side also touched it, but in your example this is quite clearly curved. How do we figure out that curving? One could do a straight extension, I guess. – Loop Space May 21 '12 at 7:52
@AndrewStacey: Ahh … I see. But could you provide an example for the 90 degree edge? – Tobi May 21 '12 at 8:16
I've been looking at the code and I don't think this is possible (or at least, easily possible) as an arrow tip. The point is that you need to know about the absolute coordinates when drawing the arrow but the arrow is rotated by a canvas transformation and these are not remembered so there's no way to say "invert the current canvas transformation". It might be possible via a decoration, though. – Loop Space May 22 '12 at 17:00

Okay, here's a method for the third using a decoration. In a decoration, the transformation is set as a coordinate transformation which means that we can figure out what it does. That means we can go between the transformed coordinate system, in which the x-axis points along the line and the y-axis is normal to it, and the page system, in which the x-axis is horizontal and the y-axis vertical. We need to use both, which is why I don't think that this can be done with arrow tips as those are set using a canvas transformation which cannot be accessed to the same degree as a coordinate transformation.

Update 2012-05-23 Revamped version, now does horizontal and vertical ends. There are slight artefacts as it goes from the main path to the filled ends - don't know how to deal with those. There's also an "auto" version which selects the "best" of "box", "horizontal", or "vertical" depending on the angle of the path.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/55671/86}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\makeatletter

\def\box@end@box{
\pgfmathsetmacro\boxed@xd{\boxed@xa - \boxed@xo}
\pgfmathsetmacro\boxed@yd{\boxed@yb - \boxed@yo}
\pgfmathsetmacro\boxed@sf{\boxed@xd * \boxed@xc + \boxed@yd * \boxed@yc > 0 ? 1 : -1}
\pgfmathsetmacro\boxed@xd{\boxed@sf * \boxed@xd + \boxed@xo + \boxed@xc}
\pgfmathsetmacro\boxed@yd{\boxed@sf * \boxed@yd + \boxed@yo + \boxed@yc}
}

\def\box@end@horizontal{
\pgfmathsetmacro\boxed@yd{(\boxed@ya - \boxed@yb) *\boxed@yc > 0 ? \boxed@ya + \boxed@yc: \boxed@yb + \boxed@yc}
\pgfmathsetmacro\boxed@xd{(\boxed@ya - \boxed@yb) *\boxed@yc > 0 ? \boxed@xb + (\boxed@ya - \boxed@yb) * \boxed@xc/\boxed@yc: \boxed@xa +  (\boxed@yb - \boxed@ya) * \boxed@xc/\boxed@yc}
}

\def\box@end@vertical{
\pgfmathsetmacro\boxed@xd{(\boxed@xa - \boxed@xb) *\boxed@xc > 0 ? \boxed@xa + \boxed@xc: \boxed@xb + \boxed@xc}
\pgfmathsetmacro\boxed@yd{(\boxed@xa - \boxed@xb) *\boxed@xc > 0 ? \boxed@yb + (\boxed@xa - \boxed@xb) * \boxed@yc/\boxed@xc: \boxed@ya +  (\boxed@xb - \boxed@xa) * \boxed@yc/\boxed@xc}
}

\def\box@end@auto{
\pgfmathparse{atan2(abs(\boxed@xc),abs(\boxed@yc)) > 60 ? "horizontal" : (atan2(abs(\boxed@xc),abs(\boxed@yc)) > 30 ? "box" : "vertical")}
\csname box@end@\pgfmathresult\endcsname
}

\tikzset{
line cap start/.style={
},
line cap end/.style={
},
line cap type/.style={
line cap start=#1,
line cap end=#1,
},
line end path style/.style={
%    draw=red,line width=1pt
fill
},
decoration={
markings,
mark=at position #1\pgflinewidth with {
\pgfextra{
\pgfpointtransformed{\pgfpointorigin}
\xdef\boxed@xo{\the\pgf@x}
\xdef\boxed@yo{\the\pgf@y}
\pgfpointtransformed{\pgfpoint{0pt}{.5\pgflinewidth}}
\xdef\boxed@xa{\the\pgf@x}
\xdef\boxed@ya{\the\pgf@y}
\pgfpointtransformed{\pgfpoint{0pt}{-.5\pgflinewidth}}
\xdef\boxed@xb{\the\pgf@x}
\xdef\boxed@yb{\the\pgf@y}
\pgfpointtransformed{\pgfpoint{#1\pgflinewidth}{0pt}}
\pgfmathsetmacro\boxed@xc{\boxed@xo - \the\pgf@x}
\pgfmathsetmacro\boxed@yc{\boxed@yo - \the\pgf@y}
\csname box@end@#2\endcsname
\global\let\boxed@xd\boxed@xd
\global\let\boxed@yd\boxed@yd
\global\let\boxed@xc\boxed@xc
\global\let\boxed@yc\boxed@yc
}
\pgftransformreset
\path[line end path style] (\boxed@xa,\boxed@ya) -- (\boxed@xa + \boxed@xc,\boxed@ya + \boxed@yc) -- (\boxed@xd pt,\boxed@yd pt) -- (\boxed@xb + \boxed@xc,\boxed@yb + \boxed@yc) -- (\boxed@xb,\boxed@yb);
}
},
postaction=decorate,
},
}
\makeatother
\begin{document}
\begin{tikzpicture}[line width=1cm]
\draw[line cap type=box] (1,0) .. controls +(1,-2) and +(-1,-1) .. (6,0);
\draw[line cap type=box] (1,-4) .. controls +(1,2) and +(-1,1) .. ++(6,0);
\end{tikzpicture}

\begin{tikzpicture}[line width=1cm]
\draw[line cap type=horizontal] (1,0) .. controls +(1,-2) and +(-1,-1) .. (6,0);
\draw[line cap type=horizontal] (1,-4) .. controls +(1,2) and +(-1,1) .. ++(6,0);
\end{tikzpicture}

\begin{tikzpicture}[line width=1cm]
\draw[line cap type=vertical] (1,0) .. controls +(1,-2) and +(-1,-1) .. (6,0);
\draw[line cap type=vertical] (1,-4) .. controls +(1,2) and +(-1,1) .. ++(6,0);
\end{tikzpicture}

\begin{tikzpicture}[line width=1cm]
\draw[line cap type=auto] (1,0) .. controls +(1,-2) and +(-1,-1) .. (6,0);
\draw[line cap type=auto] (1,-4) .. controls +(1,2) and +(-2,1) .. ++(6,0);
\end{tikzpicture}

\end{document}

Result:

-