【问题标题】:Plotting arrows at the edges of a curve在曲线边缘绘制箭头
【发布时间】:2025-12-21 04:05:11
【问题描述】:

this question at ask.sagemath 的启发,在PlotContourPlot 等产生的曲线末端添加箭头的最佳方法是什么?这些是高中时看到的图类型,表明曲线在页面末尾继续。

经过一番搜索,我找不到执行此操作的内置方式或最新包。 (有ArrowExtended,不过比较老了)。

ask.sagemath 问题中给出的解决方案依赖于函数及其端点的知识,以及(也许)求导的能力。它在 Mathematica 中的翻译是

f[x_] := Cos[12 x^2]; xmin = -1; xmax = 1; small = .01; 
Plot[f[x],{x,xmin,xmax}, PlotLabel -> y==f[x], AxesLabel->{x,y},
  Epilog->{Blue,
    Arrow[{{xmin,f[xmin]},{xmin-small,f[xmin-small]}}],
    Arrow[{{xmax,f[xmax]},{xmax+small,f[xmax+small]}}]
  }]

另一种方法是简单地将Plot[] 生成的Line[] 对象替换为Arrow[]。例如

Plot[{x^2, Sin[10 x], UnitStep[x]}, {x, -1, 1}, 
  PlotStyle -> {Red, Green, {Thick, Blue}},
  (*AxesStyle -> Arrowheads[.03],*) PlotRange -> All] /. 
 Line[x__] :> Sequence[Arrowheads[{-.04, .04}], Arrow[x]]

但这有一个问题,即线条中的任何不连续性都会在您不想要它们的地方生成箭头(这通常可以通过选项Exclusions -> None 修复)。更重要的是,这种方法对CountourPlots 是没有希望的。比如试试

ContourPlot[x^2 + y^3 == 1, {x, -2, 2}, {y, -2, 1}] /. 
  Line[x__] :> Sequence[Arrowheads[{-.04, .04}], Arrow[x]]

(上述情况下的问题可以通过规则来解决,例如{a___, l1_Line, l2_Line, b___} :> {a, Line[Join[l2[[1]], l1[[1]]]], b}或使用适当的单头箭头。)。

如您所见,上述两种方法(快速技巧)都不是特别健壮或灵活的。有谁知道这种方法吗?

【问题讨论】:

  • 我可以建议找到 Plot 上/下产生的点
  • @Alexey:我不太确定你在暗示什么......
  • @Simon 抱歉,句子不完整。在不连续的情况下,我可以建议找到位于PlotRange 边缘的点。例如:xmin = -2 Pi; xmax = Pi; Plot[Tan[x], {x, xmin, xmax}, Exclusions -> {Cos[x] == 0}] /. {x_Line?(Abs[#[[1, 1, 1]] - xmin] < 1*^-6 && Abs[#[[1, -1, 1]] - xmax] < 1*^-6 &) :> Sequence[Arrowheads[{-.04, .04}], Arrow[x]], x_Line?(Abs[#[[1, 1, 1]] - xmin] < 1*^-6 &) :> Sequence[Arrowheads[{-.04, 0}], Arrow[x]], x_Line?(Abs[#[[1, -1, 1]] - xmax] < 1*^-6 &) :> Sequence[Arrowheads[{0, .04}], Arrow[x]] }.
  • @Alexey:我以为这就是你的意思。感谢您的代码-如果您将其放入答案中,我会投票赞成。这也是我想到的,但是如果绘图范围意味着一条线从顶部或底部而不是侧面退出绘图,会发生什么?您必须从生成的图中提取 PlotRange 并在规则中使用它...
  • @Simon 最近发现了这个users.dimi.uniud.it/~gianluca.gorni/Mma/Mma.html(未测试)

标签: wolfram-mathematica plot


【解决方案1】:

以下似乎可行,首先对段进行排序:

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]

【讨论】:

  • @belisarius :几个月前有一个问题/答案如何在 Mathematica 中制作带有箭头的传统 3d 轴,但我再也找不到了,你还记得其中的任何关键字吗?
  • 啊,它在不同的网站上,所以我找不到它...谢谢!
  • @belisarius:我完全错过了您的一系列修订,感谢您的所有努力。看起来不错!不幸的是,对于问题中给出的ContourPlot,您的排序失败,因为它是使用GraphicsComplex 对象构造的。这可以通过使用Normal 来解决。但是你仍然会遇到你的规则不喜欢带有'Tooltip'的情节的问题......
  • @belisarius:我知道出了什么问题。 Sequence[] 使 Tooltip 获得太多参数。将Sequence[] 替换为List[] 就可以了。
【解决方案2】:

受到阿列克谢的评论和贝利撒留的回答的启发,这是我的尝试。

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
  ]

我们可以在某些功能上进行测试

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

还有一些等高线图

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

失败的一个地方是绘图边缘有水平线或垂直线;

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

这可以通过PlotRange-&gt;{-2.1,2.1}Exclusions-&gt;None 等选项来解决。

最后,最好添加一个选项,以便每个“曲线”只能在其边界上箭头。这会给出像贝利撒留的答案那样的情节(它也可以避免上面提到的问题)。但这是一个品味问题。

【讨论】:

    【解决方案3】:

    以下构造的优点是不会弄乱 Graphics 结构的内部结构,并且比 ask.sagemath 中建议的更通用,因为它更好地管理 PlotRange 和无穷大。

    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]]}]};
    

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

    编辑

    作为一个函数:

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

    仍在思考什么对 ListPlot 和 al 更好。

    【讨论】:

    • 酷!这将如何概括,比如说,ListPlot 的实验数据? (假设你很懒,没有将数据放入插值函数,然后像上面一样使用Plot)?