Plotting arrows at the edges of a curve

后端 未结 3 1481
死守一世寂寞
死守一世寂寞 2020-12-28 10:01

Inspired by this question at ask.sagemath, what is the best way of adding arrows to the end of curves produced by Plot, ContourPlot

相关标签:
3条回答
  • 2020-12-28 10:33

    The following seems to work, by sorting the segments first:

    f[x_] := {E^-x^2, Sin[10 x], Sign[x], Tan[x], UnitBox[x], 
                 IntegerPart[x], Gamma[x],
                 Piecewise[{{x^2, x < 0}, {x, x > 0}}], {x, x^2}}; 
    
    arrowPlot[f_] := 
     Plot[{#}, {x, -2, 2}, Axes -> False, Frame -> True, PlotRangePadding -> .2] /.
    
     {Hue[qq__], a___, x___Line} :> {Hue[qq], a, SortBy[{x}, #[[1, 1, 1]] &]} /. 
    
     {a___,{Line[x___], d___, Line[z__]}} :> 
                               List[Arrowheads[{-.06, 0}], a, Arrow[x], {d}, 
                                                 Arrowheads[{0, .06}], Arrow[z]] /. 
    
     {a___,{Line[x__]}}:> List[Arrowheads[{-.06, 0.06}], a, Arrow[x]] & /@ f[x];  
    
    arrowPlot[f]
    

    0 讨论(0)
  • 2020-12-28 10:45

    The following construct has the advantage of not messing with the internal structure of the Graphics structure, and is more general than the one suggested in ask.sagemath, as it manage PlotRange and infinities better.

    f[x_] = Gamma[x]
    
    {plot, evals} = 
      Reap[Plot[f[x], {x, -2, 2}, Axes -> False, Frame -> True, 
        PlotRangePadding -> .2, EvaluationMonitor :> Sow[{x, f[x]}]]];
    
    {{minX, maxX}, {minY, maxY}} = Options[plot, PlotRange] /. {_ -> y_} -> y; 
    ev = Select[evals[[1]], minX <= #[[1]] <= maxX && minY <= #[[2]] <= maxY &];
    seq = SortBy[ev, #[[1]] &];
    arr = {Arrow[{seq[[2]], seq[[1]]}], Arrow[{seq[[-2]], seq[[-1]]}]};
    

    enter image description here

    Show[plot, Graphics[{Red, arr}]]
    

    Edit

    As a function:

    arrowPlot[f_, interval_] := Module[{plot, evals, within, seq, arr},
       within[p_, r_] :=
        r[[1, 1]] <= p[[1]] <= r[[1, 2]] &&
         r[[2, 1]] <= p[[2]] <= r[[2, 2]];
    
       {plot, evals} = Reap[
         Plot[f[x], Evaluate@{x, interval /. List -> Sequence},
          Axes -> False,
          Frame -> True,
          PlotRangePadding -> .2,
          EvaluationMonitor :> Sow[{x, f[x]}]]];
    
       seq = SortBy[Select[evals[[1]],
          within[#, 
            Options[plot, PlotRange] /. {_ -> y_} -> y] &], #[[1]] &];
    
       arr = {Arrow[{seq[[2]], seq[[1]]}], Arrow[{seq[[-2]], seq[[-1]]}]};
       Show[plot, Graphics[{Red, arr}]]
       ];
    
    arrowPlot[Gamma, {-3, 4}]  
    

    Still thinking what is better for ListPlot & al.

    0 讨论(0)
  • 2020-12-28 10:47

    Inspired by both Alexey's comment and belisarius's answers, here's my attempt.

    makeArrowPlot[g_Graphics, ah_: 0.06, dx_: 1*^-6, dy_: 1*^-6] := 
     Module[{pr = PlotRange /. Options[g, PlotRange], gg, lhs, rhs},
      gg = g /. GraphicsComplex -> (Normal[GraphicsComplex[##]] &);
      lhs := Or@@Flatten[{Thread[Abs[#[[1, 1, 1]] - pr[[1]]] < dx], 
                          Thread[Abs[#[[1, 1, 2]] - pr[[2]]] < dy]}]&;
      rhs := Or@@Flatten[{Thread[Abs[#[[1, -1, 1]] - pr[[1]]] < dx], 
                          Thread[Abs[#[[1, -1, 2]] - pr[[2]]] < dy]}]&;
      gg = gg /. x_Line?(lhs[#]&&rhs[#]&) :> {Arrowheads[{-ah, ah}], Arrow@@x};
      gg = gg /. x_Line?lhs :> {Arrowheads[{-ah, 0}], Arrow@@x};
      gg = gg /. x_Line?rhs :> {Arrowheads[{0, ah}], Arrow@@x};
      gg
      ]
    

    We can test this on some functions

    Plot[{x^2, IntegerPart[x], Tan[x]}, {x, -3, 3}, PlotStyle -> Thick]//makeArrowPlot
    

    Normal plots

    And on some contour plots

    ContourPlot[{x^2 + y^2 == 1, x^2 + y^2 == 6, x^3 + y^3 == {1, -1}}, 
       {x, -2, 2}, {y, -2, 2}] // makeArrowPlot
    

    contour plots

    One place where this fails is where you have horizontal or vertical lines on the edge of the plot;

    Plot[IntegerPart[x],{x,-2.5,2.5}]//makeArrowPlot[#,.03]&
    

    fail!

    This can be fixed by options such as PlotRange->{-2.1,2.1} or Exclusions->None.

    Finally, it would be nice to add an option so that each "curve" can arrow heads only on their boundaries. This would give plots like those in Belisarius's answer (it would also avoid the problem mentioned above). But this is a matter of taste.

    0 讨论(0)
提交回复
热议问题