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}$};
8
  • 1
    Duplicate ? tex.stackexchange.com/questions/110907/…
    – percusse
    Jul 28, 2014 at 8: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, 2014 at 8: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, 2014 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, 2014 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, 2014 at 17:56

1 Answer 1

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

1
  • 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, 2023 at 10:42

You must log in to answer this question.

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