6

I'm trying to add some explanatory braces in my graphic, and find myself running into some minor difficulties/inconveniences.

[Edit]: After being made aware that the main thrust of the question (about off-center tips of braces) is already covered in How to draw an unbalanced curly brace in TikZ?, I removed that part. [\Edit]

I was wondering if there's a built-in way to determine the position of the tip of the brace from all the given parameters and automatically place a node there. Right now I have to calculate the orthonormal vector to the one which spans the brace, which is tedious, especially for many braces. I tried testing some options from https://tex.stackexchange.com/a/95144/42225 (which weren't listed in the PGF manual), but to no avail.

Also, I think it would be cleaner to have (my implementation of) underext as a key in decoration={...}; is there a way to set it, e.g. like decoration/underext/.style={pre=moveto,pre length=#1,post=moveto,post length=#1}?

MWE:

\documentclass{article}
\usepackage[english]{babel}
\usepackage{tikz}
\usetikzlibrary{calc,intersections,decorations.pathreplacing}

\begin{document}
\begin{tikzpicture}[scale=1.5,%
    underext/.style={decoration={pre=moveto,pre length=#1,post=moveto,post length=#1}}]
\pgfmathsetmacro\ang{asin(1/6)}
\clip (-\ang:6) arc (-\ang:90+\ang:6) -- (-1,-1) -- cycle;

\foreach \r / \i [evaluate=\r as \ang using 90/\r] in {8/4} % radius and number of slice
{
\begin{scope}[rotate={(\i-1)*\ang}]
    \foreach \m in {2}
    {
    % Minkowski sum
    \filldraw [fill=black!5,draw=black] ($(-\ang:\r/2)+(-\ang-90:\m)$) arc (-\ang-90:-\ang-180:\m)
    arc (-\ang:\ang:\r/2-\m) arc (\ang+180:\ang+90:\m)
    -- ($(\ang:2*\r)+(\ang+90:\m)$) arc (\ang+90:\ang:\m)
    arc (\ang:-\ang:2*\r+\m) arc (-\ang:-\ang-90:\m) -- cycle;

    % support
    \filldraw[draw=black,fill=black!20] (-\ang:\r/2) arc (-\ang:\ang:\r/2) -- (\ang:2*\r) arc (\ang:-\ang:2*\r) -- cycle;

    % axis of cone
    \draw[very thin] (0,0) coordinate (A) -- (0:\r/2) coordinate (B);
    \draw[decorate,decoration={brace,mirror,amplitude=10pt,raise=3pt,aspect=0.6},underext=1.5pt,very thin] (A)--(B);
    \coordinate (base) at ($(A)!0.6!(B)$);
    \coordinate (ortho) at ($(0,0)!1!90:($(B)-(A)$)$);
    \node[inner sep=2pt] at ($(base)!-15pt!($(base)+(ortho)$)$) {$2^{j-1}$};
    };
\end{scope}
\begin{scope}[very thin,<->]
    \draw (-1,0) -- (6,0);
    \draw (0,-1) -- (0,6);
\end{scope}
};
\end{tikzpicture}
\end{document}

enter image description here

Whereas what I'd like to have would be something along the lines of:

\draw[very thin] (0,0) node[shape=coordinate](A){} -- (0:\r/2) node[shape=coordinate](B){};
\draw[decorate,decoration={brace,mirror,amplitude=10pt,raise=3pt,aspect=0.6,underext=1.5pt,
    node at tip=node name},very thin] (A)--(B);
\node[below=2pt] at (node name) {$2^{j-1}$};
Axel
  • 791
  • 1
    Duplicate ? http://tex.stackexchange.com/questions/110907/how-to-draw-an-unbalanced-curly-brace-in-tikz – percusse Jul 28 '14 at 08:46
  • @percusse: Ok thanks, I searched before I asked, but didn't find this. I'll edit the question to be about the automatic node creation. – Axel Jul 28 '14 at 08:52
  • 1
    Why is \draw[decorate,decoration={brace,mirror,amplitude=10pt,raise=3pt,aspect=0.6},underext=1.5pt,very thin] (A) -- node[below=2pt] {$2^{j-1}$} (B); not good enough? – Turion Jul 30 '14 at 15:01
  • @Turion: Maybe I misunderstand something, but running your suggestion places the node in such a way that it intersects both the brace and the border of the light grey area. The goal is to have to the tip of the brace point to the node. I could use node[near end,...] or pos=0.6, but below=x pt will not (really) help me move orthogonally to the brace. Of course I can play around with the values until it almost fits, but the question was about an automatic placement... – Axel Jul 30 '14 at 17:52
  • below right with a bigger distance should do it, I can fiddle around with it when I'm home. The only other way I can think of would be looking into the code of the decoration and monkey patching it. – Turion Jul 30 '14 at 17:56
  • @Turion: Thanks for the offer - I know below right as well, but (together with the other 7 axial and diagonal directions) that still only approximates the vector orthogonal to the brace up to an angle of pi/8=22.5deg. I guess you could argue this is enough (together with the freedom of pos=...), but my point is that I don't want to fine-tune so many parameters for many braces (especially if I decide to change the rotation parameter later on).

    What I imagine should calculate the position given all relevant parameters (raise, amplitude, etc.).

    – Axel Jul 30 '14 at 18:06
  • If you're fine with turning the direction of the text, you can use sloped, I think. – Turion Jul 30 '14 at 18:37
  • @Turion: This doesn't change the position, it just rotates the text to match the slope of the line. Also, due to the scope with the rotation, I'd have to use transform shape, which (undesirably) then also takes scale=... into account. – Axel Jul 30 '14 at 19:43

1 Answers1

1

Also, I think it would be cleaner to have (my implementation of) underext as a key in decoration={...}; is there a way to set it, e.g. like decoration/underext/.style={pre=moveto,pre length=#1,post=moveto,post length=#1}?

Almost, since the whole decoration stuff is done by PGF, the keys are actually in the /pgf namespace. Thus, you need to do either

\pgfset{decoration/underext/.style={…}}

or

\tikzset{/pgf/decoration/underext/.style={…}}

This answer provides two new auto function:

  • left with offset and
  • right with offset

There work like left and right but they push the node (or coordinate) orthogonally to the side about the length of /tikz/auto offset.
Remember that these snap to the eight compass anchors.

This value key is initialized with value of the amplitude and the raise value.

There's a lot of similar calculations done but it was the only way for me to differentiate between sloped and not-sloped nodes. Transformations are all over the place and very confusing!

If you would draw the nodes, you'd see that they touch just the tip:
enter image description here


The every brace node style sets up the appropriate auto function and the right position.

For the sloped example, I'm using the same path outside of your rotated scope because otherwise it'll be weird.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calc, decorations.pathreplacing}
\pgfset{decoration/raise/.append code=% Ugh!
  \pgfkeyssetevalue{/pgf/decoration/raise}{#1}}%
\pgfkeyssetvalue{/pgf/decoration/raise}{0pt}
\makeatletter
\tikzset{% https://tex.stackexchange.com/a/665784/16595
  swap/.style={swap auto/.expand once=\tikz@auto@anchor@direction},
  swap auto/.is choice,
  /utils/temp/.style args={#1=#2}{
    /utils/exec=\pgfutil@namedef{tikz@install@auto@anchor@#1}{%
                \let\tikz@do@auto@anchor\tikz@auto@anchor@on
                \def\tikz@auto@anchor@direction{#1}},
    swap auto/#1/.code=\def\tikz@auto@anchor@direction{#2}},
  /utils/temp/.list={left=right, right=left,
    left with offset=right with offset, right with offset=left with offset},
  % which side → mirror/reverse path → auto
  % where      → reverse path/aspect → pos
  every brace node/.style={
    auto=% Ugh?
      \ifx\tikz@dec@mirror\relax
        \ifpgf@decorate@inputsegmentobjects@reverse right\else left\fi
      \else
        \ifpgf@decorate@inputsegmentobjects@reverse left\else right\fi
      \fi\space with offset,
    pos=\ifpgf@decorate@inputsegmentobjects@reverse
          1-\fi(\pgfdecorationsegmentaspect)},
  auto offset/.initial=% doesn't need to be used with decoration,
                       % can just be a fixed distance
    \pgfdecorationsegmentamplitude+(\pgfkeysvalueof{/pgf/decoration/raise})}
\pgfutil@namedef{tikz@auto@anchor@left with offset}{%
  \tikz@auto@pre\tikz@auto@anchor
  \xdef\tikz@marshal{%
      \pgf@process{%
        \noexpand\pgftransformreset
        \noexpand\pgftransformrotate{+90}%
        \noexpand\pgfpointtransformed{%
          \noexpand\pgfpointscale{\noexpand\pgfkeysvalueof{/tikz/auto offset}}
                                 {\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}}}%
      \advance\pgf@pt@x\pgf@x
      \advance\pgf@pt@y\pgf@y}%
  \tikz@auto@post
  \ifpgfslopedattime
    \pgf@xb=\pgf@x
    \pgf@yb=\pgf@y
    \pgf@process{%
      \pgftransformreset
      \pgftransformrotate{90}%
      \pgfpointtransformed{%
        \pgf@process{%
          \tikz@timer%
          \pgf@x=\pgf@pt@aa pt
          \pgf@y=\pgf@pt@ab pt
          \pgfpointnormalised{}%
          \pgfpointscale{\pgfkeysvalueof{/tikz/auto offset}}{}}}}%
    \advance\pgf@pt@x\pgf@x
    \advance\pgf@pt@y\pgf@y
    \pgf@x=\pgf@xb
    \pgf@y=\pgf@yb
  \else
    \tikz@marshal
  \fi}
\pgfutil@namedef{tikz@auto@anchor@right with offset}{%
  \tikz@auto@pre\tikz@auto@anchor@prime
  \xdef\tikz@marshal{%
    \pgf@process{%
      \noexpand\pgftransformreset
      \noexpand\pgftransformrotate{-90}%
      \noexpand\pgfpointtransformed{%
        \noexpand\pgfpointscale{\noexpand\pgfkeysvalueof{/tikz/auto offset}}
                               {\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}}}%
    \advance\pgf@pt@x\pgf@x
    \advance\pgf@pt@y\pgf@y}%
  \tikz@auto@post
  \ifpgfslopedattime
    \pgf@xb=\pgf@x
    \pgf@yb=\pgf@y
    \pgf@process{%
      \pgftransformreset
      \pgftransformrotate{-90}%
      \pgfpointtransformed{%
        \pgf@process{%
          \tikz@timer%
          \pgf@x=\pgf@pt@aa pt
          \pgf@y=\pgf@pt@ab pt
          \pgfpointnormalised{}%
          \pgfpointscale{\pgfkeysvalueof{/tikz/auto offset}}{}}}}%
    \advance\pgf@pt@x\pgf@x
    \advance\pgf@pt@y\pgf@y
    \pgf@x=\pgf@xb
    \pgf@y=\pgf@yb
  \else
    \tikz@marshal
  \fi}
\makeatother
\begin{document}
\begin{tikzpicture}[
  scale=1.5,
  /pgf/decoration/underext/.style={
    pre=moveto, pre length={#1}, post=moveto, post length={#1}}]
\pgfmathsetmacro\ang{asin(1/6)}
\clip (-\ang:6) arc [start angle=-\ang, end angle=90+\ang, radius=6] -- (-1,-1) -- cycle;

\foreach \r / \i [evaluate=\r as \ang using 90/\r] in {8/4}{ % radius and number of slice \begin{scope}[rotate={(\i-1)\ang}] \foreach \m in {2}{ % Minkowski sum \filldraw [fill=black!5, draw=black, delta angle=-90, radius=\m] ($(-\ang:\r/2)+(-\ang-90:\m)$) arc [start angle=-\ang-90] arc [start angle=-\ang, end angle=\ang, radius=\r/2-\m] arc [start angle=\ang+180] -- ($(\ang:2\r)+(\ang+90:\m)$) arc [start angle=\ang+90] arc [start angle=\ang, end angle=-\ang, radius=2*\r+\m] arc [start angle=-\ang] -- cycle;

% support
\filldraw[draw=black,fill=black!20]
  (-\ang:\r/2)
  arc [start angle=-\ang, end angle=\ang, radius=\r/2] coordinate[midway] (B)
  -- (\ang:2*\r)
  arc [start angle=\ang, end angle=-\ang, radius=2*\r] -- cycle;

% axis of cone
\draw[very thin] (0,0) coordinate (A) -- (B);
\draw[
  decorate,
  decoration={
    brace, mirror,
    amplitude=10pt, raise=3pt, aspect=0.6
  },
  very thin] (A)-- node[every brace node]{$2^{j-1}$} (B);
\coordinate (base) at ($(A)!0.6!(B)$);
\coordinate (ortho) at ($(0,0)!1!90:($(B)-(A)$)$);
\node[inner sep=2pt,gray] at ($(base)!-15pt!($(base)+(ortho)$)$) {$2^{j-1}$};

} \end{scope} \draw[decorate, decoration={brace, reverse path, mirror, amplitude=10pt, raise=3pt, aspect=0.6}, very thin] (A)-- node[every brace node,sloped]{$2^{j-1}$} (B); } \path[very thin, <->] (-1,0) edge (6,0) (0,-1) edge (0,6); \end{tikzpicture} \end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
  • This is very cool, thanks a lot! :) The code and how it works is kinda inscrutable for me now, would be cool if this could eventually make its way into pgf. But yeah, my LaTeX days are a while behind me now, haha. – Axel Feb 25 '23 at 10:42