Приложение: условная непрозрачность стрелки

Приложение: условная непрозрачность стрелки

Проблемный

Я хотел бы отобразить простой непрерывный график и набор вертикальных стрелок, высота которых следует форме функции.

Следующая диаграмма потоков в TikZ имеет такое оформление\tikzset{flecheTV/.style={->,ultra thick,densely dotted, decorate,decoration={snake, amplitude=1mm,segment length=3mm, pre length=3mm, post length=3mm}, color=orange}

введите описание изображения здесь

и я хотел бы адаптировать его к PGF, чтобы иметь автоматические стрелки, пропорциональные синему графику (не), как показано ниже

введите описание изображения здесь

К сожалению, пока что

  1. Стрелки не пропорциональны синему графику.
  2. Я хочу показать 10 стрелок, расположенных равномерно, и это не смотря наsamples at = {0,...,10}
  3. украшение не применено так, как мне бы хотелось
  4. как стрелка ( ycombфактически) может начинаться с y=100(стрелок, которые будут направлены вниз, когда синяя кривая опустится ниже этого уровня 100)?

Я думал, что смогу использовать decorative, ycombчтобы имитировать диаграмму, но не могу передать аргумент decoration в plot. Вот MWE (основанный напостроение-стрелок-программно-в-pgfplots)

\documentclass{standalone}

\usepackage{pgfplots}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{decorations.markings}

\tikzset{
declare function={f(\x) =  rand*30*cos(\x) ;},
flecheTV/.style={->,ultra thick,densely dotted, decorate,decoration={snake, amplitude=1mm,segment length=3mm,  pre length=3mm, post length=3mm}, color=orange}
}

\pgfplotsset{
  mycomb/.style={flecheTV,mark=none,ycomb,}
}

\begin{document}

\pgfmathsetseed{2}
\def\Scale{0.8}

\begin{tikzpicture}

    \begin{axis}[domain= 0:10,
        samples at = {0,...,10},    
        ytick=100,
        separate axis lines,
        y axis line style= { draw opacity=0.0 },
    ]

    \addplot[very thin,opacity=0.8] {100};
    \addplot+[mark=none,blue, smooth,very thick,opacity=0.2] {f(x) + 100};
    \addplot+[mycomb,each nth point=1] {\Scale * f(x) +100}; 
    \end{axis}
\end{tikzpicture}
\end{document}

Приложение

После многочисленных вопросов и ответов я пришел к отличному решению, более гибкому, чем даже изначально ожидалось (теперь с пороговыми условиями на кривой, определяющими существование стрелок после того, как этот порог достигнут или нет).

Обновленный код основан (99%) на великолепном решении @Frougon!!

введите описание изображения здесь

введите описание изображения здесь

\documentclass[tikz, border=1mm]{standalone}
\usepackage{pgfplotstable}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}     



\pgfmathsetseed{2}
\newcommand*{\myArrowScale}{1.0}
\def\Couleur{blue}
\def\KK{100}    %I keep that for constency of code of my side
%\newcommand*{\myBase}{\KK}
\def\BarE{120}
\def\BarEE{20}
\def\BarC{90}
%\newcommand*{\BarC}{90}
\def\BarP{60}
\def\CTF{5}
\tikzset{
  declare function={f(\x) = rand*70*sin(40*\x) ;},
  flecheTV/.style={
    ->, color=orange, ultra thick, densely dotted, decorate,decoration={snake, amplitude=1mm, segment length=3mm, pre length=3mm,
post length=3mm},
    opacity={#1},
  },
        fleche/.style={>=latex,very thick},
        flecheTF/.style={fleche, color=\Couleur!50!white},    
}

\newif\ifmyThresholdExceeded      % starts as false

\pgfplotstableset{
  create on use/x/.style={create col/expr={\pgfplotstablerow}},
  create on use/y/.style={create col/expr={f(\pgfplotstablerow)}},
  create on use/meta/.style={
    create col/assign/.code={%
      % Set the cell value depending on the \ifmyThresholdExceeded conditional
      \pgfplotstableset{create col/next content/.expanded={%
          \ifmyThresholdExceeded 0.1\else 1.0\fi}%
      }%
      \ifmyThresholdExceeded
      \else
        % \BarEE = threshold 
        \pgfmathparse{int(\pgfplotstablerow >= 0 &&
                          \pgfplotstablerow <= 9 &&
                          \thisrow{y} > \BarEE)}%
        \ifnum\pgfmathresult=1
          \global\myThresholdExceededtrue
        \fi
      \fi
    },
  },
}

% Create a table with 11 rows (\pgfplotstablerow varies from 0 to 10).
\pgfplotstablenew[columns={x, y, meta}]{11}{\myTable}

\begin{document}

\begin{tikzpicture}
\begin{axis}[
      domain=0:10,
         ytick={\BarE,\KK,\BarC,\BarP},  
      separate axis lines,
      y axis line style={draw opacity=0.0},
      ]
    \addplot[very thin, opacity=0.8] {\KK};
    \addplot[very thick,green, opacity=0.8] {\BarE};
    \addplot[very thick,blue, opacity=0.8]  {\BarC};    
    \addplot[very thick,red, opacity=0.8]   {\BarP};  

    \addplot+[mark=none, blue, smooth, very thick, opacity=0.2]
      table[x=x, y expr={\thisrow{y} + \KK}] {\myTable};
    \addplot+[mark=none,
              quiver={u=0, v={
              %\CTF             %Fixed height arrow
              \thisrow{y}       %Proportionnal arrow
              + \KK - \BarC},
                      scale arrows=\myArrowScale,
                      every arrow/.append style={
                        /utils/exec={%
                          \pgfmathfloattofixed{\pgfplotspointmeta}%
                          \let\myOpacity\pgfmathresult
                        },
                      flecheTV/.expand once=\myOpacity,
                      }}]
      table[x=x, y expr=\BarC, point meta=\thisrow{meta}] {\myTable};
  \end{axis}
\end{tikzpicture}

\end{document}

решение1

Ваши стрелки нарисованы с новыми вычислениямиф(Икс) для каждогоИксценность, и посколькуфиспользует rand, вы получаете значения, отличные от тех, которые использовались для кривой. Чтобы решить эту проблему, я предлагаю динамически создать таблицу с помощью pgfplotstable, сохранить необходимыеИксиф(Икс) значения там, затем рисуем и кривую и стрелки из этих значений. Таким образом, randиспользуется ровно один раз для каждой точки данных.

Стиль /pgfplots/quiverпозволяет легко рисовать стрелки, начиная с любого места, где вам нужно — отсюда, отсюда (Икс, 100) для каждого значенияИкс. Масштабирование стрелок выполняется просто scale arrows=0.8(это quiverопция). Конечно, можно было бы динамически создать еще один столбец, содержащий определенное знаковое значение для каждой стрелки, и использовать его в третьей \addplotкоманде, но это не кажется необходимым, учитывая эту scale arrowsопцию.

Если вы хотите использовать свой \Scaleмакрос, вы, конечно, можете написать scale arrows/.expand once=\Scale, или даже scale arrows=\Scale, так как scale arrowsклавиша PGF, по-видимому, расширяет его аргумент.

\documentclass[tikz, border=1mm]{standalone}
\usepackage{pgfplotstable}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}       % 1.16 works as well

\pgfmathsetseed{2}

\tikzset{
  declare function={f(\x) = rand*30*cos(50*\x) ;},
  flecheTV/.style={
    ->, color=orange, ultra thick, densely dotted, decorate,
    decoration={snake, amplitude=1mm, segment length=3mm, pre length=3mm,
                post length=3mm},
  },
}

\pgfplotstableset{
  create on use/x/.style={create col/expr={\pgfplotstablerow}},
  create on use/y/.style={create col/expr={f(\pgfplotstablerow)}},
}

% Create a table with 11 rows (\pgfplotstablerow varies from 0 to 10).
\pgfplotstablenew[columns={x, y}]{11}{\myTable}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[
      domain=0:10,
      ytick=100,
      separate axis lines,
      y axis line style={draw opacity=0.0},
      ]
    \addplot[very thin, opacity=0.8] {100};
    \addplot+[mark=none, blue, smooth, very thick, opacity=0.2]
      table[x=x, y expr={\thisrow{y} + 100}] {\myTable};
    \addplot+[mark=none, quiver={u=0, v=\thisrow{y}, scale arrows=0.8,
                                 every arrow/.append style={flecheTV}}]
      table[x=x, y expr=100] {\myTable};
  \end{axis}
\end{tikzpicture}

\end{document}

введите описание изображения здесь

Приложение: условная непрозрачность стрелки

Это отвечает на ваш вопросэтот комментарий. Учитывая случайное семя из вопроса, чтобы что-то увидеть, я задам непрозрачность 0,1 всем стрелкам, как только мы увидим хотя бы одно значение функциифчто больше 14 (т.е. 114, если учесть смещение 100)среди значений для точек 1, 2, ..., 9, где первая точка - номер 0 (по вашему запросу). Для этого мы:

  • измените flecheTVстиль так, чтобы он принимал непрозрачность стрелки в качестве единственного аргумента;

  • добавляем новый столбец в сгенерированную таблицу, где мы сохраняем желаемую непрозрачность в зависимости от текущегоф(Икс) значение и те, которые были видны до сих пор;

  • использовать этот столбец как point meta;

  • преобразовать каждое метазначение точки в фиксированный формат (числовое метазначение точки находится в формате библиотеки PGF fpu, например, 1Y1.0e0]);

  • передать результат в измененный flecheTVстиль.

Если заменить \ifnum\pgfplotstablerow<1на \ifnum\pgfplotstablerow<4, то вы увидите, что четвертая точка больше не вызывает состояние превышения порога, поскольку ее номер равен 3 (начиная с 0).

\documentclass[tikz, border=1mm]{standalone}
\usepackage{pgfplotstable}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}       % 1.16 works as well

\pgfmathsetseed{2}

\tikzset{
  declare function={f(\x) = rand*30*cos(50*\x) ;},
  flecheTV/.style={
    ->, color=orange, ultra thick, densely dotted, decorate,
    decoration={snake, amplitude=1mm, segment length=3mm, pre length=3mm,
                post length=3mm},
    opacity={#1},
  },
}

\newif\ifmyThresholdExceeded      % starts as false

\pgfplotstableset{
  create on use/x/.style={create col/expr={\pgfplotstablerow}},
  create on use/y/.style={create col/expr={f(\pgfplotstablerow)}},
  create on use/meta/.style={
    create col/assign/.code={%
      \ifmyThresholdExceeded
      \else
        \ifnum\pgfplotstablerow<1
        \else
          \ifnum\pgfplotstablerow>9
          \else
            % 14 = threshold (this corresponds to 114)
            \pgfmathparse{int(\thisrow{y} > 14)}%
            \ifnum\pgfmathresult=1
              \global\myThresholdExceededtrue
            \fi
          \fi
        \fi
      \fi
      % Set the cell value depending on the \ifmyThresholdExceeded conditional
      \pgfplotstableset{create col/next content/.expanded={%
          \ifmyThresholdExceeded 0.1\else 1.0\fi}%
      }%
    },
  },
}

% Create a table with 11 rows (\pgfplotstablerow varies from 0 to 10).
\pgfplotstablenew[columns={x, y, meta}]{11}{\myTable}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[
      domain=0:10,
      ytick=100,
      separate axis lines,
      y axis line style={draw opacity=0.0},
      ]
    \addplot[very thin, opacity=0.8] {100};
    \addplot+[mark=none, blue, smooth, very thick, opacity=0.2]
      table[x=x, y expr={\thisrow{y} + 100}] {\myTable};
    \addplot+[mark=none,
              quiver={u=0, v=\thisrow{y}, scale arrows=0.8,
                      every arrow/.append style={
                        /utils/exec={%
                          \pgfmathfloattofixed{\pgfplotspointmeta}%
                          \let\myOpacity\pgfmathresult
                        },
                        flecheTV/.expand once=\myOpacity,
                      }}]
      table[x=x, y expr=100, point meta=\thisrow{meta}] {\myTable};
  \end{axis}
\end{tikzpicture}

\end{document}

введите описание изображения здесь

Примечание: следующий фрагмент кода используется для инициализации значений в metaстолбце:

\ifmyThresholdExceeded
\else
  \ifnum\pgfplotstablerow<1
  \else
    \ifnum\pgfplotstablerow>9
    \else
      % 14 = threshold (this corresponds to 114)
      \pgfmathparse{int(\thisrow{y} > 14)}%
      \ifnum\pgfmathresult=1
        \global\myThresholdExceededtrue
      \fi
    \fi
  \fi
\fi

можно заменить на:

\ifmyThresholdExceeded
\else
  \pgfmathparse{int(\pgfplotstablerow >= 1 &&
                    \pgfplotstablerow <= 9 &&
                    \thisrow{y} > 14)}%
  \ifnum\pgfmathresult=1
    \global\myThresholdExceededtrue
  \fi
\fi

Последний вариант, вероятно, немного медленнее первого, но этот метод может быть более удобен в случае, если вам нужно написать сложные условия (в аргументе \pgfmathparseможно использовать логические операторы, скобки и все остальные вещи, поддерживаемые pgfmath).

Приложение 2: незначительные изменения

Это решает вопросы вэтот комментарий:

  • \newcommand*{\myBase}{100}, \newcommand*{\myArrowBase}{90}и \newcommand*{\myArrowScale}{1.0}и v={\thisrow{y} + \myBase - \myArrowBase}в quiverпараметрах для изменения того, откуда начинаются стрелки. Будьте осторожны, это может сбивать с толку, потому что теперь стрелка нулевой длины не означает, что была cosравна нулю. Установите обе базы на 100, чтобы вернуться к предыдущей ситуации. Не стесняйтесь устанавливать \myArrowScaleна 0.8или что-то еще, когда вы поймете, как все отображается (это касается вашего 1).

  • \pgfplotstablerow >= 0вместо этого \pgfplotstablerow >= 1в тесте, чтобы первая точка могла запустить вычисление условия (это касается вашего 3);

  • \pgfplotstableset{create col/next content/...}перемещено перед выполнением теста (это касается вашего пункта 2, но для того, чтобы первая стрелка затемнилась, вам, конечно, нужно отменить предыдущий пункт, так как он задерживает затемнение стрелки);

  • Пороговое значение изменено с 14 до 11,77, чтобы быть чуть ниже значения для первой точки (ее значение составляет 11,772903; измените пороговое значение на 11,78, и первая точка больше не сможет вызывать условие).

\documentclass[tikz, border=1mm]{standalone}
\usepackage{pgfplotstable}
\usepackage{pgfplots}
\pgfplotsset{compat=1.17}       % 1.16 works as well

\pgfmathsetseed{2}

\newcommand*{\myBase}{100}
\newcommand*{\myArrowBase}{90}
\newcommand*{\myArrowScale}{1.0}

\tikzset{
  declare function={f(\x) = rand*30*cos(50*\x) ;},
  flecheTV/.style={
    ->, color=orange, ultra thick, densely dotted, decorate,
    decoration={snake, amplitude=1mm, segment length=3mm, pre length=3mm,
                post length=3mm},
    opacity={#1},
  },
}

\newif\ifmyThresholdExceeded      % starts as false

\pgfplotstableset{
  create on use/x/.style={create col/expr={\pgfplotstablerow}},
  create on use/y/.style={create col/expr={f(\pgfplotstablerow)}},
  create on use/meta/.style={
    create col/assign/.code={%
      % Set the cell value depending on the \ifmyThresholdExceeded conditional
      \pgfplotstableset{create col/next content/.expanded={%
          \ifmyThresholdExceeded 0.1\else 1.0\fi}%
      }%
      \ifmyThresholdExceeded
      \else
        % 11.77 = threshold (this corresponds to function value \myBase + 11.77)
        \pgfmathparse{int(\pgfplotstablerow >= 0 &&
                          \pgfplotstablerow <= 9 &&
                          \thisrow{y} > 11.77)}%
        \ifnum\pgfmathresult=1
          \global\myThresholdExceededtrue
        \fi
      \fi
    },
  },
}

% Create a table with 11 rows (\pgfplotstablerow varies from 0 to 10).
\pgfplotstablenew[columns={x, y, meta}]{11}{\myTable}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[
      domain=0:10,
      ytick=\myBase,
      separate axis lines,
      y axis line style={draw opacity=0.0},
      ]
    \addplot[very thin, opacity=0.8] {\myBase};
    \addplot+[mark=none, blue, smooth, very thick, opacity=0.2]
      table[x=x, y expr={\thisrow{y} + \myBase}] {\myTable};
    \addplot+[mark=none,
              quiver={u=0, v={\thisrow{y} + \myBase - \myArrowBase},
                      scale arrows=\myArrowScale,
                      every arrow/.append style={
                        /utils/exec={%
                          \pgfmathfloattofixed{\pgfplotspointmeta}%
                          \let\myOpacity\pgfmathresult
                        },
                      flecheTV/.expand once=\myOpacity,
                      }}]
      table[x=x, y expr=\myArrowBase, point meta=\thisrow{meta}] {\myTable};
  \end{axis}
\end{tikzpicture}

\end{document}

введите описание изображения здесь

Если взять код выше и просто заменить \thisrow{y} > 11.77на \thisrow{y} > 11.78, то первая точка (номер 0, значение 11.772903) больше не будет вызывать условие, хотя оно и проверяется из-за \pgfplotstablerow >= 0частичного условия, используемого здесь. Четвертая точка (номер 3, значение 14.5334485) его вызовет. Поскольку в этом приложении 2 мы задерживаем затемнение на одну точку данных, вывод будет следующим:

введите описание изображения здесь

решение2

Вот еще один способ сделать это, используя tikz intersectionбиблиотеку s.

\documentclass{standalone}

\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{intersections,calc}

\tikzset{
declare function={f(\x) =  rand*30*cos(\x) ;},
flecheTV/.style={->,ultra thick,densely dotted, decorate,decoration={snake, amplitude=1mm,segment length=3mm,  pre length=3mm, post length=3mm}, color=orange}
}

\begin{document}

\pgfmathsetseed{2}
\pgfmathsetmacro{\Scale}{0.8}
\begin{tikzpicture}

    \begin{axis}[domain= 0:10,
        samples at = {0,...,10},    
        ytick=100,
        separate axis lines,
        y axis line style= { draw opacity=0.0 },
    ]

    \addplot[very thin,opacity=0.8] {100};
    \addplot+[mark=none,blue, smooth,very thick,opacity=0.2, name path=f] {f(x) + 100};
    \pgfplotsinvokeforeach{0,...,10}{
        \path[name path=tempxplot] (axis cs:#1,\pgfkeysvalueof{/pgfplots/ymin}) -- (axis cs:#1,\pgfkeysvalueof{/pgfplots/ymax});
        \draw[name intersections={of=tempxplot and f},flecheTV] (axis cs:#1,100) -- ($(axis cs:#1,100)!\Scale!(intersection-1)$);
    }
    \end{axis}
\end{tikzpicture}
\end{document}

введите описание изображения здесь

ПРИЛОЖЕНИЕ: Мне потребовалось некоторое время, чтобы разобраться, но вот версия, в которой порог для длины стрелы может быть учтен. Это действие основано на операции let.

Обратите внимание, что пороговое значение выражается в единицах pt, а не в axisединицах.

\documentclass{standalone}

\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{intersections,calc}

\tikzset{
declare function={f(\x) =  rand*30*cos(\x) ;},
flecheTV/.style={->,ultra thick,densely dotted, decorate,decoration={snake, amplitude=1mm,segment length=3mm,  pre length=3mm, post length=3mm}, color=orange}
}

\begin{document}

\pgfmathsetseed{2}
\pgfmathsetmacro{\Scale}{0.8}
\pgfmathsetmacro{\ArrowThreshold}{1cm}
\begin{tikzpicture}

    \begin{axis}[domain= 0:10,
        samples at = {0,...,10},    
        ytick=100,
        separate axis lines,
        y axis line style= { draw opacity=0.0 },
    ]

    \addplot[very thin,opacity=0.8] {100};
    \addplot+[mark=none,blue, smooth,very thick,opacity=0.2, name path=f] {f(x) + 100};
    \pgfplotsinvokeforeach{0,...,10}{
        \path[name path=tempxplot] (axis cs:#1,\pgfkeysvalueof{/pgfplots/ymin}) -- (axis cs:#1,\pgfkeysvalueof{/pgfplots/ymax});
        % Create a path operation starting with computing the required intersection
        \path[name intersections={of=tempxplot and f}] (axis cs:#1,100) -- (intersection-1)
        % Place a coordinate at the origin of the path (just for convenience)
        coordinate[pos=0] (arrowstart) 
        % Place a coordinate at the 80% of the path (just for convenience)
        coordinate[pos=\Scale]  (arrowend)
        % Based on the predefined coordinates, compute the length of the arrow in pt then attribute opacity based on the threshold
        let \p1 = ($(arrowend)-(arrowstart)$),
            \n1 = {ifthenelse(abs(\y1)>\ArrowThreshold,1,0)} 
        in (arrowstart) edge[flecheTV,opacity=\n1] (arrowend);
    }
    \end{axis}
\end{tikzpicture}
\end{document}

введите описание изображения здесь

Связанный контент