Adendo: opacidade condicional da seta

Adendo: opacidade condicional da seta

Problemático

Gostaria de exibir um gráfico contínuo simples e um conjunto de setas verticais cuja altura segue o formato de uma função.

O seguinte diagrama de fluxos no TikZ tem esta decoração\tikzset{flecheTV/.style={->,ultra thick,densely dotted, decorate,decoration={snake, amplitude=1mm,segment length=3mm, pre length=3mm, post length=3mm}, color=orange}

insira a descrição da imagem aqui

e gostaria de adaptá-lo para pgf para que eu possa ter setas automaticamente proporcionais ao gráfico azul (não) como abaixo

insira a descrição da imagem aqui

Infelizmente, até agora

  1. As setas não são proporcionais ao gráfico azul
  2. Quero mostrar 10 setas exibidas uniformemente e não é apesar dosamples at = {0,...,10}
  3. a decoração não é aplicada como eu gostaria
  4. como a seta (na ycombverdade) pode começar em y=100(com setas que ficariam para baixo quando a curva azul ficasse abaixo desse nível de 100)?

Achei que poderia usar decorado ycombpara imitar o diagrama, mas não consigo passar o argumento da decoração para o enredo. Aqui está o MWE (baseado emplotando setas programaticamente em 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}

Termo aditivo

Após várias perguntas e respostas, estou chegando a uma ótima solução, mais flexível do que inicialmente esperado (com agora condições de limite superior na curva para definir a existência de setas após esse limite ser atingido ou não).

Código atualizado baseado (99%) na ótima solução do @Frougon !!

insira a descrição da imagem aqui

insira a descrição da imagem aqui

\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}

Responder1

Suas setas são desenhadas com novos cálculos def(x) para cadaxvalor, e desdefusa rand, você obtém valores diferentes daqueles que foram usados ​​para a curva. Para resolver este problema, proponho criar dinamicamente uma tabela usando pgfplotstable, armazenar o necessárioxef(x) valores ali e, em seguida, desenhe a curva e as setas a partir desses valores. Dessa forma, randé usado exatamente uma vez para cada ponto de dados.

O /pgfplots/quiverestilo facilita desenhar as setas começando de onde você quiser - aqui, de (x, 100) para cada valor dex. O dimensionamento das setas é feito simplesmente scale arrows=0.8(é uma quiveropção). Claro, seria possível criar dinamicamente mais uma coluna contendo um valor assinado específico para cada seta e usá-lo no terceiro \addplotcomando, mas isso não parece necessário dada esta scale arrowsopção.

Se você quiser usar sua \Scalemacro, é claro que você pode escrever scale arrows/.expand once=\Scale, ou até mesmo scale arrows=\Scale, já que a scale arrowstecla PGF parece expandir seu argumento.

\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}

insira a descrição da imagem aqui

Adendo: opacidade condicional da seta

Isso responde à sua pergunta emeste comentário. Dada a semente aleatória da pergunta, para ver algo, darei opacidade 0,1 a todas as setas assim que tivermos visto pelo menos um valor para a funçãofque é maior que 14 (ou seja, 114 se você levar em conta o deslocamento 100)entre os valores dos pontos 1, 2, ..., 9, onde o primeiro ponto é o número 0 (conforme sua solicitação). Para fazer isso, nós:

  • modifique o flecheTVestilo para que aceite a opacidade da seta como único argumento;

  • adicione uma nova coluna à tabela gerada onde armazenamos a opacidade desejada dependendo do atualf(x) valor e os vistos até agora;

  • use esta coluna como point meta;

  • converter cada valor meta de ponto em formato fixo (meta de ponto numérico está no formato da fpubiblioteca PGF, por exemplo, 1Y1.0e0]);

  • passe o resultado para o flecheTVestilo modificado.

Se você substituir \ifnum\pgfplotstablerow<1por \ifnum\pgfplotstablerow<4, verá que o quarto ponto não aciona mais a condição acima do limite, pois seu número é 3 (começando em 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}

insira a descrição da imagem aqui

Nota: o seguinte trecho de código usado para inicializar valores na metacoluna:

\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

pode ser substituído por:

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

O último é provavelmente um pouco mais lento que o anterior, mas esta técnica pode ser mais conveniente caso você precise escrever condições complexas (no argumento de \pgfmathparse, você pode usar operadores booleanos, parênteses e todas as outras coisas suportadas por pgfmath).

Adendo 2: pequenas variações

Isso aborda questões emeste comentário:

  • \newcommand*{\myBase}{100}, \newcommand*{\myArrowBase}{90}e \newcommand*{\myArrowScale}{1.0}e v={\thisrow{y} + \myBase - \myArrowBase}nas quiveropções para alterar o início das setas. Cuidado, isso pode ser confuso porque agora, uma seta de comprimento zero não significa que era cosigual a zero. Coloque ambas as bases em 100 para voltar à situação anterior. Sinta-se à vontade para definir \myArrowScaleou 0.8qualquer outra coisa quando entender como as coisas são exibidas (isso aborda o seu 1).

  • \pgfplotstablerow >= 0em vez de \pgfplotstablerow >= 1no teste para que o primeiro ponto possa acionar o cálculo da condição (isso aborda o seu 3);

  • \pgfplotstableset{create col/next content/...}movido antes de fazer o teste (isso aborda o seu 2, mas para que a primeira seta fique esmaecida, é claro que você precisa desfazer o item anterior, pois isso atrasa o escurecimento da seta);

  • O limite foi alterado de 14 para 11,77 para ficar logo abaixo do valor do primeiro ponto (seu valor é 11,772903; altere o limite para 11,78 e o primeiro ponto não poderá mais acionar a condição).

\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}

insira a descrição da imagem aqui

Se você pegar o código acima e apenas substituir \thisrow{y} > 11.77por \thisrow{y} > 11.78, o primeiro ponto (número 0, valor 11.772903) não aciona mais a condição, mesmo que seja testado devido à \pgfplotstablerow >= 0condição parcial usada aqui. O quarto ponto (número 3, valor 14,5334485) irá, entretanto, acioná-lo. Como neste adendo 2 estamos atrasando o escurecimento em um ponto de dados, a saída será a seguinte:

insira a descrição da imagem aqui

Responder2

Aqui está outra maneira de fazer isso, usando tikz intersectiona biblioteca 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}

insira a descrição da imagem aqui

TERMO ADITIVO: Levei um tempo para descobrir, mas aqui está uma versão onde um limite para o comprimento da seta pode ser levado em consideração. Esta ação é baseada na letoperação.

Observe que o limite é expresso em termos pte não em axisunidades.

\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}

insira a descrição da imagem aqui

informação relacionada