Manipulate 简介

Manipulate (操作)这个单个的命令允许你仅用少数的几行输入就能创造出范围惊人之广的人机互动的应用. Manipulate 是为所有会使用基本命令例如 Table(表格)和 Plot (画图)的用户设计的: 它不要求学习任何复杂的新概念,也不要求有任何对于用户界面编程思想的了解.

计算 Manipulate 之后你所得到的输出是一个人机互动的对象,它包含一个或多个控件(滑块等等),使用这些控件你能改变一个或多个参数的数值. 这个输出很像一个小型应用程序或小器具:它不是一个静止的结果,它是一个能和你互相作用的运行程序.

这个教程是为熟悉 Wolfram 语言基本语言的用户设计的,包括怎样使用函数,不同种类的括号和大括号,以及怎样画简单的图形. 一些例子需要用到更高级的函数,但是不需要具体了解这些函数怎样工作也能够明白这些例子的主要意图.

这个教程虽长,它也只包含了一部分的内容. "高级操作(Manipulate)功能" 提供了关于这个丰富命令的更复杂的一些特性的进一步信息.

Manipulate 和 Table 一样简单

从最基本的角度,Manipulate 的语法是和简单的表格函数 Table 相同的. 考虑以下的 Table 命令,它能产生从1到20的一个数据列表.

In[1]:=
Click for copyable input
Out[1]=

仅仅用 Manipulate 这个词替换 Table 这个词,你能实现一个人机互动的应用,这个应用允许你使用滑块来考察 n 的数值.

In[2]:=
Click for copyable input
Out[2]=PlayAnimation

如果你在 Wolfram 语言内读这个文件,你可以通过点击和拖拉滑块来观看显示数值实时变化(这意味着在你拖拉滑块的同时数值在变化,而不是只在当你释放滑块时才变化). 如果你在读一个静态的文件,你会看到滑块被移到了一个任意的位置. (按照系统默认值,滑块从左边开始,但在以下的例子中滑块一般都离开了他原来的位置.)

在使用 TableManipulate 这两个命令时, {variable,min,max} ({变量,最小值,最大值})这个格式是用来指定一个迭代器,给出变量的名字以及它变化的范围.

当然 Manipulate (从这一点上 Table 也一样) 整个的意图是使你能在第一个变量里放任和表达式,不仅仅是一个简单变量的名字. 在以下的简单输出中移动滑块就能开始体会到 Manipulate 的功能.

In[3]:=
Click for copyable input
Out[3]=PlayAnimation

再重复一下,如果你在读一个静态的文件你应该相信当滑块移动时图形实时变化.

请注意,滑块旁边有一个额外的图标,单击该图标会打开另一控件的小面板. 这里,打开上一个示例中的面板.

PlayAnimation

这个面板能使你看到变量的数值,而且能用动画控件使得图形运动起来.

如果你想不打开面板就能看到变量的数值,你可以在变量指定中加上 Appearance->"Labeled" 的选项. (注意在加号右边显示的数值,当滑块移动时会随时更新.)

In[4]:=
Click for copyable input
Out[4]=PlayAnimation

这也第一次向我们暗示 Manipulate 远远超过了Table 的 相对简单性,这体现在它的输出以及指定变量列单时的灵活性和范围.

Table 一样, Manipulate 允许你对多个变量进行范围指定.

In[5]:=
Click for copyable input
Out[5]=PlayAnimation

你可以任意设置很多的变量,以至于功能相似的 Table 命令将试图列举过于大量的数据输入.

In[6]:=
Click for copyable input
Out[6]=PlayAnimation

你可以打开任何或者所有的子面板来看参数的数值,并且你可以自由地同时将任何不同的变量制作成动画.

一种理解 Manipulate 的方式是把它看成是一个人机互动的考察大型参数空间的方法. 你可以随意地在这个空间里移动,同时考察显示的方向的有趣特性. 在以下的章节中你将会看到. Manipulate 有许多的特性是为使得这种考察更简易和更有益而设制的.

符号输出和步长

以上的例子都是图形式的,确实, Manipulate 最常用的应用是创造人机互动的图形. 但是,Manipulate 能使得任何 Wolfram 语言函数具有人机互动的功能,不仅仅是图形.

通常在涉及符号输出而不是图形输出的例子中市的第一个问题是你希望处理整数而不是连续变幻的实数. 在 Table 这个命令里默认的步长是1,你自然就得到整数,但是在 Manipulate 这个命令里默认的步长是允许连续变化的(你可以把它看成是步长为0). 比较这两个例子,并且注意 Manipulate 允许数值在 Table 返回的数值之间.

In[7]:=
Click for copyable input
Out[7]=
In[8]:=
Click for copyable input
Out[8]=PlayAnimation

比如,涉及代数控制的函数当给予非整数参数值时通常给不出有意义的结果. 以下的 Expand(展开)函数并不展开任何式子.

In[9]:=
Click for copyable input
Out[9]=PlayAnimation

幸运的是在 Manipulate 命令中明确地加入步长为1是很简单的,这样得到的一组可能值和 Table 命令返回的数值是完全一样的.

In[10]:=
Click for copyable input
Out[10]=PlayAnimation

当有了一个明确的步长值时,以上 Expand 的例子就会更有意义了.

In[11]:=
Click for copyable input
Out[11]=PlayAnimation

一次只显示一个数值这一事实允许你创造出远远超出 Table 命令可行范围的很多例子. Manipulate 输出的一个重要特性是面板没有固定的尺寸也没有对输出面板大小的随意限制.

In[12]:=
Click for copyable input
Out[12]=PlayAnimation

(在印刷本的文件中,为了避免浪费纸张滑块设置地比较低,但当被移动到最右边时,输出结果平稳地增长到覆盖好几页纸的空间).

Table 一样,如果你设置的最小值和步长是有理数,你所得到的变量将会是有理数,而不是近似的实数. 这里是一个用格式函数 Row (行)来建立一个简单的分数相加的例子.

In[13]:=
Click for copyable input
Out[13]=PlayAnimation

你甚至可以用符号表达式而不只是简单的数字来作为终点值和步长.

In[14]:=
Click for copyable input
Out[14]=PlayAnimation

控件的类型

Manipulate 支持广泛的可供选折的变量指定方法,为变量产生各种不同的控件. 这包括复选框,弹出菜单,以及其它除了滑块之外的控件.

主要的原则是对于每一个变量,当你要求一组特定的可能值之后,Manipulate 就会自动地选择一种合适的控件使得很方便地就能取得那些数值. 对于一个数字的 Table 类式的迭代器,滑块是最方便的界面.

另一方面,你也许希望指定一列离散的数值(数字的或符号的) 而不是一个数值范围. 这可以利用这个形式 {variable,{val1,val2,}}({变量,{数值1,数值2,......}})的迭代器来完成.

(注意和指定范围相比有了额外一级别的列单.)如果你要求的是少量单独的数值,你将得到一行按钮.

In[15]:=
Click for copyable input
Out[15]=PlayAnimation

如果你要求的是大量的离散数值,Manipulate 会换用一个弹出菜单.

In[16]:=
Click for copyable input
Out[16]=PlayAnimation

如果你使用具体的数值 True(真)和 False(假),你将得到一个复选框.

In[17]:=
Click for copyable input
Out[17]=PlayAnimation

当然这些选择有一些随意性,但是它们是为使用方便而设计的,并且你总是可以在指定变量时插入一个 ControlType 的选项来撤销控件种类的自动选择. (关于控件类别的一套完整的列单可以在 Manipulate 的文件中找到.)

例如,你可以用 ControlType->SetterBar 的选项来要求一行按钮,尽管自动选择会选用一个弹出菜单.

In[18]:=
Click for copyable input
Out[18]=PlayAnimation

滑块不仅能用来扫描数字范围,也能用来扫描离散的符号数值(这使得你通过它们也能实现动画). ControlType->Manipulator 这个选项要求由 Manipulate 默认的控件,它是一个滑块加上一个选择性的控制面板,带有数值和动画的控件(看前面的例子). ControlType->Slider 要求一个简单的滑块.

In[19]:=
Click for copyable input
Out[19]=PlayAnimation

甚至可以用两种不同的控件来调节同一个变量的值. 这里一个弹出菜单和一个滑块都和 filling 这个变量的数值相连接. 如果滑块选择的数值不出现在弹出菜单里,弹出菜单会呈现空白,但它的功能还是保存着. 当在弹出菜单里选折一个数值时,滑块会移动到相应的位置. 所以这两个控件可以交互使用来调节同一个数值,并且当一个被使用时,另一个随着变化.

In[20]:=
Click for copyable input
Out[20]=PlayAnimation

这里并没有详尽地包括 Manipulate 中所有可能的控件的种类. 更详细的列表,请看 Manipulate 的文件. 其中最重要的控件之一是 Locator(定位器),它能使你把控制点放在 Manipulate 图形输出里面,这在 "定位器" 里介绍; Slider2D (二维滑块)在 "二维滑块" 中介绍.

初始值和标记

这里是一个画 Lissajous 图形的有趣的例子.

In[21]:=
Click for copyable input
Out[21]=PlayAnimation

遗憾的是你最初看不到任何图形:在你把和 a 1a 2(振幅)这两个变量从它们的初始值0移开之前,是不会看到任何图形的. 这时如果把初始值设为和默认值(最左边的值)不同的一个值,那就会更方便一些. 这可以通过使用这样的变量指定形式 {{var,init},min,max}({{变量,初始值},最小值,最大值})来完成.

这里是同一个例子,但把两个振幅最初都设为1,并且设置了频率的默认值以便得到一个宁人满意的最初图形.

In[22]:=
Click for copyable input
Out[22]=PlayAnimation

看一种形状变换成另一种形状是很有意是的,从这方面讲了解 Mathematica 中关于滑块的一个独特的性能是很有益的. 如果你按下 Option 键 (Macintosh) 或者 Alt 键 (Windows),鼠标的运动和滑块的行动相比减慢了20倍. 这也就是说,当你向左或向右拖拉鼠标时,拇指只会移动平常距离的1/20. 如果你移出滑块的区域,只要鼠标时一直按下的,数值会开始向那个方向缓慢移动.

如果你除了按下 Option/Alt 键之外还按下 ShiftCtrl 键,或者两个同时按下,你能把运动减慢额外的20多倍(每一个额外的修改键减慢20倍). 如果三个键都按下,拇指的动作有可能小与它整个范围的一百万之一,这对于像这个例子一样的情况是有帮助的,在这样的例子中美丽的图形隐藏在参数空间非常小的值域范围里.

(在这个例子中用了 PerformanceGoal->"Quality" 这个选项,这是为了保证甚至在滑块被移动时 ParametricPlot 也能画出光滑的曲线:对这个选项的需求在 "高级操作(Manipulate)功能" 中进行了详细讲解.)

在默认下, Manipulate 用变量名来标记每一个控件. 但是你也许希望给出更长,更有描述性的标记,这可以通过这种形式的变量指定 {{var,init,label},min,max}({{变量,初始值,标记},最小值,最大值})来完成.

这里是使用了标记的同一个例子.

In[23]:=
Click for copyable input
Out[23]=PlayAnimation

美化控制区域

Manipulate 支持一些允许你重组,注解,以及美化控制区域概貌的特性,使得它能够符合特殊例子的要求. (但是,高级用者应该记住, Manipulate 决不是 Mathematica 中创造人机互动的界面的唯一方法,并且如果你用 Manipulate 不能实现你所需要的事项,你可以轻易地开始直接使用例如 Dynamic (动态)和 DynamicModule (动态模块)的函数, 用它们来创造不受 Manipulate 常规局限,形势自由,开放式的用户界面,(这些特性在 "动态简介" "高级动态功能" 之中进行了详细地解释.)

当你有少量控件时,通常最方便的是把它们放在 Manipulate 面板内容区的上方. 但是因为通常荧屏的官度比长度要高,如果你有大量的控件,你会发现用 ControlPlacement (控件放置)这个选项可以把它们放在左边,这样会更方便一些.

In[24]:=
Click for copyable input
Out[24]=PlayAnimation

ControlPlacement 用在 Manipulate 整体这一级时,它对所用控件的默认位置进行设置. 但是这个选项也可以用在单个的变量指定中,使得你能够把控件分配在输出区的多个位置上.

在以下的例子中,所有的控件自然地分配成两组,每组三个,或者三组,每组两个. 你可以在变量指定的系列中插入 Delimiter(分隔符)这个关键词来表明你愿意在什么地方设置分界线. 这里两个没有进行标记的分隔符把所有的控件分成三组.

In[25]:=
Click for copyable input
Out[25]=PlayAnimation

另外可行的方法是,字符串,或分隔符和字符串,能用来标记各组控件.

In[26]:=
Click for copyable input
Out[26]=PlayAnimation

在控件中可以插入的东西是相当多的,包括样式文本,任意表达式,甚至以及动态对象,它们能独立地在主输出窗口之外进行更新. 这里是一个简单的例子,用 Style 来使得组群标题更显著.

In[27]:=
Click for copyable input
Out[27]=PlayAnimation

更复杂的安排和动态的标记在 "高级控制函数功能" 里显示.

二维滑块

Mathematica 里一个很聪明的特性是它支持二维的滑块,这使你能把鼠标运动的两个方向都用上,同时控制两个数值. (从一定的意义上来说,普通的一维滑块浪费了鼠标能有的两个自由度中的一个.)

要得到一个二维的滑块,对 min (最小值)和 max (最大值)都取用一对数值,就像这样{var,{xmin,ymin},{xmax,ymax}} ({变量,{x 最小值,y 最小值},{x 最大值,y 最大值}}).

变量的数值也将是一对 {x,y} 变量. 在这个简单的例子中,只看一下变量的数值就能体会到控件是怎样工作的.

In[28]:=
Click for copyable input
Out[28]=PlayAnimation

以下的例子更以图形的形式显示了一个二维滑块的数值是怎样和一个坐标点相对应的.

In[29]:=
Click for copyable input
Out[29]=PlayAnimation

把这做得更有意思,你可以用三个二维的滑块,而不是六个一维的滑块来重做上一节中的 Lissajous 图. 你还是在控制同样的六个参数,但你现在可以一次控制两个.

In[30]:=
Click for copyable input
Out[30]=PlayAnimation

这创造了一个简洁有趣的例子. 注意,使用 OptionShiftCtrl 键能进行细微控制,使滑块的动作减慢(如在 "初始值和标记" 里解释的),这不仅对一维滑块生效,对二维滑块也一样工作.

除画图之外的图形

到目前为止,高级画图函数大部分都用过了,但是在 Manipulate 中使用 Mathematica 的低级图形语言是同样有趣的. 以下从 上一节 重复下来的例子,是一个使用低级图形语言的简单例子.

In[31]:=
Click for copyable input
Out[31]=PlayAnimation

这个例子也指出很重要的一点,那就是任何时候你在 Manipulate 中使用 Graphics ,你很可能希望设置一个明确的 PlotRange (画图范围)选项. (PlotRange->1 表明所有方向上的范围都是从原点到1,这和PlotRange->{{-1,1},{-1,1}} 是相等的.)如果你省略 PlotRange 这个选项,Wolfram 语言会自动确定画图范围,导致那个点看上去好像完全没有移动,这是因为点的范围总是精确地围绕着它.

简单(或复杂)的 Wolfram 语言程序能给输出添加任意的图形元素. 例如,这里我们有指向中心点的连线,而不是一个点,连线的数目用第二个线性滑块来确定.

In[32]:=
Click for copyable input
Out[32]=PlayAnimation

这里是一个关于线行图的小例子,也是基于创造一个直线表格来做的.

In[33]:=
Click for copyable input
Out[33]=PlayAnimation

因为 Wolfram 语言是一个高度发展的程序语言,可以人机互动地用 Manipulate 来探索参数化的程序或算法. Wolfram 语言的图形语言在 "图形结构" 中进行了讲解,并且像这样的更多的例子能在Wolfram 演示项目中找到.

定位器

用来创造人机互动的图形例子中, Manipulate 的最重要的特新之一是能在输出区的图形里放置一个控制点,称为一个 Locator(定位器).

考虑以前连接中心点直线的例子. 尽管使用一个二维的滑块是一个控制中心点的良好方法,你也许更希望能直接点击和拖拉中心点本省. 这可以通过在 pt (点)这个变量的控制指定中加入 Locator 来完成. 在这种情况下,不必要指定一个 min (最小值)和 max(最大值)的范围,因为这个范围能自动从图形中得到. (但是,必须指定一个初始值.)

In[34]:=
Click for copyable input
Out[34]=PlayAnimation

现在你能点击图形中的任何地方,只要你是一直按下鼠标的按钮,连线的中心点会跟随鼠标移动. (不需要恰好点击在图形的中心;无论你点击图形的什么地方,中心点都会跳到那里.)

通过单独列举,你可以得到多个 Locator 控件. 而且,你也可以在使用 Manipulate 时不在内容区用任何控件,这是完全可以的,这样你可以创造出完全图形化的例子.

In[35]:=
Click for copyable input
Out[35]=PlayAnimation

当有多个定位器时,你仍旧可以点击图形的任何地方. 最近的一个 Locator 会跳到你点击的地方,并且开始跟踪鼠标.

你能用数值是一列点的一个单一变量来代替使用多个单独的变量,每一个对应一个单一的 {x,y} 点.

In[36]:=
Click for copyable input
Out[36]=PlayAnimation

再一次,如果你点击图形的任何地方,而不是某一个特别的 Locator,最近的那一个会跳到鼠标上然后开始跟踪它.

由于内部限制,不可能把单独的 Locator 变量和一个含有一列多个 Locator 变量的单一变量合并在一起:在一个 Manipulate 命令中你只能有一个多点的 Locator 变量. 但是,作为一个交换,你能在那一个 Locator 的多变量指定中加入 LocatorAutoCreate->True (定位器自动创造 -> 真值)这个选项,这样你能人机互动地创造和消毁 Locator 点(改变存储在那个变量中的点的列单的长度).

在以下的例子中,按下 Cmd 键 (Macintosh) 或 Alt 键 (Windows) ,并在不是已经存在的 Locator 的任何地方点击一下,以至在那个位置创造出一个新的定位器. 按下 Cmd/Alt 键并点击一个已经存在的 Locator 来销毁它. 当你添加或去除一个 Locator 时,你在改变存储在 pts 这个变量里的点的列单的长度,因而改变显示的多边形的顶点的数目.

In[37]:=
Click for copyable input
Out[37]=PlayAnimation

当然,你可以将 Locator 控件和普通的 Manipulate 变量合并起来. 例如,你可以用一些滑块和颜色选择器来控制多边型的外貌.

In[38]:=
Click for copyable input
Out[38]=PlayAnimation

尽管以上的例子可以看成是较琐碎的,但它们的意图是展现这个系统的普遍性:它提供了一个在其中任何事情都可能实现的构架. 并且,以下的例子显示出甚至仅一两行程序也能做出相当非凡的事情:创造一个人机互动的曲线拟合的环境.

定位器的拇指代表通过最小二乘法用一个多项式来进行拟合的数据点. 多项式的阶次由阶次滑块来决定. 例子中最初提供了五个点,但你能用 Cmd/Alt 键同时点击图形中的任何空白区来添加新的点子,或者按下 Cmd/Alt 键同时点击一个点子来去除它.

In[39]:=
Click for copyable input
Out[39]=PlayAnimation

这么复杂的例子能用那么少量的程序构成,这一事实确实是很非凡的. 如果你真想使某个人对 Mathematica 的程序的简洁性产生深刻的印象,以下的例子示范怎样仅用两行语句就能完成这个工作,但其中失去了一些普遍性. 实习一下,你能用30秒或更少的时间从头打出之个语句.

In[40]:=
Click for copyable input
Out[40]=PlayAnimation

三维图形

Manipulate 来探索三维图形和对二位图形一样简单,但性能问题成为更需关心的事情. 考虑下面这个简单的例子.

In[41]:=
Click for copyable input
Out[41]=PlayAnimation

对大数值的 n ,函数振荡迅速. 为了产生一个光滑的图形, Plot3D 中默认的适应性抽样算法产生大量的多边形,这需要相应的很长的计算和描绘时间.

幸好的是,为了在控件被拖拉时提高速度,当在 Manipulate 里使用时,Plot3D 和其它的固有画图函数自动调节它们内部的算法和设置,有时会以描绘质量为代价. 一旦鼠标的按钮被释放了,一个高质量的图形版本就异步地产生了(这意味着在图形被产生的同时,前端的其它操作能继续执行). 异步评价在 "高级动态功能" 中的 "同步相对异步动态计算" 中有更详细的介绍.

净结果就是当你在拖拉滑块的同时,一个快速的,但有些粗陋的图形表现实时产生了,当你释放控件,一个光滑的表现在很短时间之后就显现了. (这发生的原因是 Plot3D,以及大多数其它的画图函数,都参考 ControlActive 这个函数来设置默认的控制描绘质量和速度的各种选项. Manipulate 中使用 ControlActive 的更多信息,看 "高级操作(Manipulate)功能" 中的"处理缓慢计算".)

和二维的情况一样,你可以使用低级的画图语言,这和使用更高级的画图命令是一样简单的. 在这个例子中,你能看到 Mathematica 是如何处理互相交叉并和包围盒相交的球体.

In[42]:=
Click for copyable input
Out[42]=PlayAnimation

这个例子显示了阻光度(那就是说,透明度)是怎样能用来看到嵌套的三维结构的内部.

In[43]:=
Click for copyable input
Out[43]=PlayAnimation

(注意,给一个三维图形加添透明度能显著地减慢表现时间.)

你可以在 Manipulate 的输出里通过通常的点击和拖拉来旋转一个三维图形. 在绝大多数情况下,如果你随后移动 Manipulate 的某一个控件,图形会停留在你手工把它旋转到的位置,除非 Manipulate 之中的图形表达式含有一个明确的 ViewPoint 选项,或者它把图形输出以额外的格式建构包绕起来.

所有类型的输出都是被支持的

Manipulate 在设计上是能和 Wolfram 语言所提供的全范围的各种不同输出类型一起工作的,它并不仅仅停留在图形和代数输出上. 各种 Wolfram 语言所支持的输出都能在 Manipulate 中应用. 这里是一些例子,也许不是那么明显.

例如 Grid(格子)、 Column(列)、 Panel(面板)等等的格式建构能用于产生格式优良的输出. (关于格式建构的更多信息,参看"格子、行、和列".)

In[44]:=
Click for copyable input
Out[44]=PlayAnimation

你甚至可以用 Manipulate 来包绕产生用户界面元素,例如滑块和标签查看的函数. (关于用户界面元素的更多信息,看 "控件对象""浏览视图与注释" .) 在这个例子中,我们用两个滑块来控制第三个滑块的外貌.

In[45]:=
Click for copyable input
Out[45]=PlayAnimation

在这个更复杂的例子中, TabView (标签查看)这个结构由一个 Manipulate 命令来控制. Dynamic[pane] (动态嵌板)能使你通过使用 Manipulate 创造的滑块,或通过在输出区里点击 TabView 来选折 TabView 当前的嵌板. 输出是完全活性的.

In[46]:=
Click for copyable input
Out[46]=PlayAnimation

这个例子也许有些惊人,但是它的意图只是阐明 Manipulate 是一个完全普遍的函数,并不局限于探索任何固定的图形或代数的范例领域. 简直可以说,在你能看见的 Wolfram 语言笔记本单元里的所有事物中没有一个是不能通过使用 Manipulate 来进行人机互动的探索的(唯一的限制当然是你的计算机的速度).

保存使用在 Manipulate 里面的函数的定义

设想你定义了一个函数,然后把它用在 Manipulate 里的第一个自变量中.

In[47]:=
Click for copyable input
In[48]:=
Click for copyable input
Out[48]=PlayAnimation

这个例子会实行得很好,直到当你试图把它保存到一个文件中然后在一个新的 Wolfram 语言课程中重新把它打开时. 函数 f 直到你手工地计算包含它的定义的那个单元时才会有定义. (事实上,如果你是在 Wolfram 语言里读这个文件,你会看见 f 最初是在 Manipulate 的输出区里出现的,正是因为这个原因.)

Manipulate 支持 SaveDefinitions->True (保存定义 -> 真)这个选项,这导致它自动地在 Manipulate 输出中建立一个所有在 Manipulate 输入里提到过的函数定义的复本(以及循环式的任何它们提到过的函数定义). 然后当 Manipulate 的输出在任何新的 Wolfram 语言课程中被打开时,这些定义会在第一次计算 Manipulate 的内容之前重新建立起来.

In[49]:=
Click for copyable input
In[50]:=
Click for copyable input
Out[50]=PlayAnimation

所以,如果你是在 Wolfram 语言里读这个文件,那么甚至当第一次被打开时,第二个例子也应该正确地显示出一个数字.

你能用 SaveDefinitions 来存储函数定义或数据集,但是你应该小心如果你涉及到很大的数据量,这些数据当然会在含有保存的 Manipulate 输出的文件中存在,这有可能创造一个很大的文件.

这种情况下另外一个可行的方法是使用 Initialization (初始化)这个选项来从一个文件或其它数据源里装载一个数据包,而不是在 Manipulate 的输出里建立这个数据包. 当 Manipulate 的内容在任何新的 Wolfram 语言课程中被第一次计算之前,能给于 Initialization 这个选项任意的 Wolfram 语言程序块来进行计算. Initialization 这个选项的右边在每一个课程中只计算一次.

例如,你能用 Initialization 这个选项代替 SaveDefinitions 来实现和前面一样的结果.

In[51]:=
Click for copyable input
Out[51]=PlayAnimation

你可以把 SaveDefinitions 看成是一个方便自动的方法来设置 Initialization 这个选项,使其中设有运行这个例子你所需要的所有定义. (SaveDefinitions 实际上不妨碍使用 Initialization 这个选项:如果你愿意,你能两个都用.)

游戏垫和控制杆

当用鼠标来和 Manipulate 的输出进行互动时,你受到限制只能一次移动一个控件. 然而,有很多可用的 USB 控制元件装置,它们通过在每一个手指下放置一个按钮或控制杆能克服这个局限,从而大大增加了你能同时移动的控件的数目.

为了在 Manipulate 中利用一个 USB 控制元件,你所需要做的就是把它插上,然后用鼠标来选折(使突出)那个含有你想控制的 Manipulate 输出的单元括号. Wolfram 语言自动地查出那个控制元件,并且 Manipulate 自动地把可用的控制杆及按钮与尽可能多的参数相连接起来.

尽管 Wolfram 语言会或试图会和任何的 USB 控制元件装置(游戏垫,控制杆,模拟飞机油门控制-甚至使用 USB 控制元件界面标准的数据采集装置)一起工作,某些肯定能比其它一些更好地控制 Manipulate 的输出. 一般来说,双重控制杆的游戏垫,例如那些在电子游戏中普通使用的,提供一套很好的控件,通常是四个模拟轴和多数的按钮.

在这一节剩下的部分中,我们会假设你在使用一个 Logitech Dual Action (Logitech 双行动)的游戏垫. (这个价格低廉的控件使用广泛,并且比其它许多的装置有更好的机械上和电机上的性能,甚至包括价格昂贵得很多的装置.)如果你在用一个单一的控制杆或另外一种商标的游戏垫,那么也许在哪些控件部件映射到哪些 Manipulate 参数这方面会有一些差别.

当一个游戏垫被插入时,选折那个含有以下输出的单元的单元括号. 最初什么也不会发生,这是因为游戏垫的控制杆处于中性的未偏斜的位置. 但是如果你移动这些控制杆,你就会看到一个或多个参数开始变化. 参数变化的速率是和控制杆偏斜的角度成正比的.

In[52]:=
Click for copyable input
Out[52]=PlayAnimation

在默认时, Manipulate 把左边游戏垫的 x 轴与第一个参数相连接,左边控制杆的 y 轴与第二个参数相连接,右边控制杆的 x 轴与第三个参数相联接,右边游戏垫的 y 轴与第四个参数相连接. 你可以向一定方向移动每一个控制杆并同时观察哪一个参数在变化,通过这个来证实上面所讲的. (如果你在使用和 Logitech Dual Action(Logitech 双行动)不同的游戏垫,你也许会看到一种不同的映射:每一个制造商的做法都有一些不同,并且尽管 Wolfram 语言有表格来试图把许多普遍使用的控制元件规格化,新的器件总是同时在被引进.)

在默认时,映射是基于速度的,这就是说参数变化的速率是由控制杆的位置来控制的. 控制杆的位置所以不是直接和变量的数值相联系的.

你也许希望变量的数值是由控制杆的绝对位置来决定的,这有两种方法来实现. 在很多的游戏垫上,包括所推荐的 Logitech 型号,控制杆也是按钮:如果你按下一个控制杆,它会像一个按钮一样点击,当控制杆在控制一个 Manipulate 命令时,这导致对应的参数变成是直接与控制杆的位置相连接的. 你能用这个直接的模式来迅速地跳到任何位置,然后释放控制杆使参数值停止在那个地方.

如果你的游戏垫的控制杆上没有按钮,或者你就是希望连接的模式总是直接的,那么你可以使用 ControllerMethod->"Absolute" (控制元件方法 -> 绝对)这个选项.

In[53]:=
Click for copyable input
Out[53]=PlayAnimation

注意直接连接方式有不足之处,最显著的是一旦你使单元的括号变得突出(在一个游戏垫被插入时),所有的参数值立即跳到它们的中间位置,当然它们必须这样做如果控制杆是处在中性位置上. 尽管你还是可以用鼠标来设置数值,但一旦游戏垫被碰,这些数值就会被撤销.

ControllerMethod->"Cyclic" (控制元件方法 -> 循环的)这个选项能使速度控制有一个小的变化. 用这个设置连接是基于速度的,但是当你到达参数范围的一端时,不是停止数值而是循环到相反的另一端.

是使用直接的还是使用基于速度的连接, 这最好取决于例子本身. 上面的例子总体上使用速度连接更令人满意,但下面的例子明显地用直接连接会更好一些.

In[54]:=
Click for copyable input
Out[54]=PlayAnimation

如果这些例子中的任何一项好想不工作,有可能是因为你忘记了使得包含这些例子的单元括号变成突出. 这是一个普遍的错误. 为了方便,并且为了避免一旦一个输出产生就立刻选折它的这种需要,如果你在产生了一个提到控制元件的输出后立即摇动任何一个游戏垫控制元件,Wolfram 语言就会自动地为你选折输出单元. 但是这只会在输出被产生后立即发生,这之后由你来选折想把控制元件与哪一个 Manipulate 的输出相联系.

如果你希望一个给定的 Manipulate 总是对控制元件有反应,不管它是否被选中,你可以添加 ControllerLinking->All (控制元件连接 -> 全部)这个选项,但是这个特性应该谨慎使用. 如果你在荧屏上有多个那样的输出,它们都会试图同时移动,这是极少有益的. 这个选项最好是在这样的情况下使用,那就是在你创造一个固定格式的输出窗口时,而不是在创造像在这样的卷轴文件中使用的例子时.

在和像上面一样的例子中,除非你有一个游戏垫可用,这些例子都是不会很有意义的,通常没有任何必要显示与参数有关的滑块,或 Manipulate 其余的架构. ControllerManipulate (控制元件操作)这个函数在所有的性能和语法上都基本上和 Manipulate 相同,除了这一点之外,那就是它不显示任何框架或滑块.

In[74]:=
Click for copyable input
Out[74]=

Manipulate 将会在接下来的例子中继续应用,这是因为能看见控件对于理解它们是怎样受游戏垫影响是有帮助的,但是许多这些例子用 ControllerManipulate 也能在外观和工作上实现地一样好.

如果 Manipulate 这个命令含有数值是 {x,y} 数字对的 Slider2D 变量,那么这些变量会自动和可用控制杆的两个方向都连结起来. 这个例子对一个游戏垫左手控制杆的两个方向都有反应.

In[56]:=
Click for copyable input
Out[56]=PlayAnimation

控制元件上的按钮在默认时和在 Manipulate 中指定的任意多的 Boolean (布尔) (True/False) (真/假)参数相连接. 在一个给定的控制元件上哪一个按钮是哪一个能有一点难以猜测(这个概念在别处进行了讲解),但是在 Logitech 这个模型上那一群四个在右边的按钮被标为1到4, Manipulate 就以这个顺序使用它们. 在这个例子中点击那个1的按钮切换 b1 这个设置,改变那个点的颜色.

In[57]:=
Click for copyable input
Out[57]=PlayAnimation

这种切换的行为(每一次按下按钮参数的数值就翻转一次)是和基于速度的连接相等的. 如果你用 ControllerMethod->"Absolute" 这个选项(参照以前的例子),参数会被直接连接,这也就是说它的数值将总是 False (假),除了在按钮实际上被按下时.

In[58]:=
Click for copyable input
Out[58]=PlayAnimation

Manipulate 连接控制元件轴和参数的确切规则是有些复杂的,但基本上是这样,这些规则试图把可用的模拟的和 Boolean (布尔)的控制元件分配给 Manipulate 中的参数,使得最大量地使用可用的控制杆、按钮、旋钮以及其它控制元件上的小机械. (通常发现什么和什么相连接的最快和最简单的方法就是扭动不同的旋钮来看一看什么会发生.)

如果你发现默认的连接不是你所喜欢的,你可以明确地指出哪一个控制轴应该和哪一个参数相联接,这样可以推翻默认的设置. 控制轴是按照一个逻辑的系统来命名的,但是在大多数的用途中,记住一些基本的名字就足够了: "X""Y""XY""X1""X2""B1""B2",等等.

"X",或它的同义字 "X1",指的是主要的,或游戏垫左手边的控制杆的 x 轴. 如果要指定一个参数应该和这个轴相连接,用下面的表格.

In[59]:=
Click for copyable input
Out[59]=PlayAnimation

相似的,"X2" 指的是次要的,或右手边的控制杆的 x 轴.

In[60]:=
Click for copyable input
Out[60]=PlayAnimation

多个轴可以被合并成多维的参数. 例如, "XY" 指的是左边或主要的控制杆的两个方向,合并成一个单一的 {x,y} 变量. 如在这个例子中,这样一个合并的轴必须和 Slider2D 这种样式的参数相联系.

In[61]:=
Click for copyable input
Out[61]=PlayAnimation

三轴的变量也是被支持的,如 "XYZ". 在使用一个三轴控制杆的控制元件时,三个轴将于控制杆的三个自由度相对应. 当使用一个双控制杆的游戏垫时,每一个控制杆只有两个自由度. 在这种情况下, "XYZ" 这个轴和左边控制杆的 xy 方向加上右边控制杆的 x 方向连接. 这是否有意义取决于例子本身:对于明确的为了利用三自由度的控制杆而写的例子也许就不能和其它任何控制元件很好地工作.

In[62]:=
Click for copyable input
Out[62]=PlayAnimation

有些控件实际上提供6个模拟的自由度,它们可以被称为 "XYZ""XYZ2". 例如,如果你有一个 3Dconnexion SpaceNavigator 控件,下面的例子会让你探索它的三个空间和三个角度的自由度. 如果你没有一个这样的控件,那么这个例子就不会令人满意.

In[63]:=
Click for copyable input
Out[63]=PlayAnimation

(注意,无论你是否在用一个控制元件,Manipulate 里的三维变量都是可用的,但一般用处不大,除非和一个控制杆或游戏垫联系着.

一个典型的游戏垫有两个 x-y 控件,称为 "XY""XY2". 但是不太明显的是还有一些伪模拟的轴也是可用的,这是通过这样来产生的,那就是把四个按钮组当成四个方向:上,下,左,和右. 例如,帽子,一个在 Logitech Dual Action 游戏垫左边的指向性的垫子能被称为 "XY3".

In[64]:=
Click for copyable input
Out[64]=PlayAnimation

两个额外的轴 ("XY4") 由游戏垫右边的四个按钮定义,("XY5") 轴由正面的四个按钮定义. 不用说,这是高度地对 Logitech 这个商标的控制元件具体而言的,但是其它品牌典型地也有相似的按钮组.

这些伪模拟的轴工作起来就和真的模拟轴一样,但除了以下这一点之外,那就是在速度连接的模式下它们总是以同样的速度前进,并且在绝对模式下它们总是国定在全左,中间,或全右的位置上.

当试图连接具体的轴时,通常不是很清楚在一个给定的控制元件上哪一个轴是哪一个. ControllerInformation[] (控制元件信息[])这个函数能用来人机互动地解决这个问题. 在你的游戏垫或控制杆插入时,计算这个输入(为了得到当前的信息,你必须在你的控制元件已被插入时在你的 Mathematica 教程中计算它).

In[75]:=
Click for copyable input
Out[75]=

取决于你在用哪一种类型的计算机你也许能得到几个国有的控件. 例如,Macintosh 的手提电脑典型地含有一个位置传感器,它能读出计算机在任何时候的方位. 这个信息是可用的并能和 Manipulate 使用,但不是在默认时使用的(否则,所有的在那种手提电脑上运行的 Manipulate 函数在你倾斜计算机时都会不停地到处移动,有些人会认为这是很厌烦的事).

找到你想检查的控制元件并点击在它名字旁边的透露三角形来打开一个信息面板,然后打开 Mathematica Controls 这个分节来看一列所有可用轴的名字.

In[66]:=
Click for copyable input
Out[66]=

如果显示动态数值被打钩,那么在你摇摆控制元件或按下它的按钮时,面板里显示的数值会随实时更新,使得你能轻易地决定哪一个按钮对应哪一个命名的轴. (在 Manipulate 里使用轴的名字时不要忘记用引号把它们包围起来.)

ControllerMethod 这个选项只能在整个 Manipulate 这个级别上使用时才能把连接从基于速度的改变成绝对的. 如果你想使某些轴成为绝对的而某些成为基于速度的,那么在任何你想进行绝对连接的轴的名字上加上 "Absolute" ,如同这个例子,里面有一个基于速度的 x 方向和一个绝对的 y 方向.

In[67]:=
Click for copyable input
Out[67]=PlayAnimation

在这个注释里和 "Absolute" 相反的是 "Relative",就像在 "XRelative" 等等中一样.

自动运行

在很多方面,Manipulate 和简单的线性动画相比是一个很大的改善. Manipulate 使你自如地来回移动,而不是浏览一个固定的顺序. 但是如果你不想必须用手来移动一个滑块,那么该怎么办了?一种可选的办法是用每一个滑块旁边的 这个画像来打开一个有动画控件的面板. 只有一个变量被实行动画的 Manipulate 实际上是和 Animate 差不多相等的.

但是如果你有多个变量并且希望看到改变所有变量的效果,使用单独动画控件是不方便的. Manipulate自动运行这个特性能解决这个问题,它通过提供一个单一的运行所有变量范围的动画控件来完成.

点击在 Manipulate 输出右上角的 菜单并从菜单底下选择自动运行. 你会看到一个自动运行的面板出现在 Manipulate 的上部,它含有动画控件和一个 (关闭)按钮. 在默认时,这个动画会运行每一个单独变量的取值范围,其它的保留它们的默认值. 就如任何动画控件一样,你能改变速度和方向,或者点击滑块来手工地运行动画. Autorun 这个动画滑块有一点像一个主控件,以一个明确的顺序驱动其它所有的控件.

In[1]:=
Click for copyable input
Out[1]=PlayAnimation

自动运行默认的行为是模拟你能自己用鼠标做的事情,那就是一次移动一个控件. 如果你把AutorunSequencing->All (自动运行排序 -> 全部)这个选项加入到 Manipulate 的输入中,在造成的输出里的自动运行这个命令会反而同时移动所有的控件,就像你能在这个例子中看到一样. 这个特性对某些例子工作地更好一些,对另外一些会差一点.

In[69]:=
Click for copyable input
Out[69]=PlayAnimation

你还可以用 AutorunSequencing 来从自动运行的动画中排除一定的控件,或改变控件在动画中运行的顺序. 在以下的例子中,第三个控件在动画中最先运行,其次是第一个控件,再其次是第四个,第二个控件保留在它的默认值上.

In[70]:=
Click for copyable input
Out[70]=PlayAnimation

AutorunSequencing 允许你对某一个特定的控件指定它所预定的动画持续时间. 这个设置中对第一个控件预订了两秒钟,对第三个控件预订了两秒钟,对第四个控件预订了十秒钟. 第二个控件和以前一样被略过了.

In[71]:=
Click for copyable input
Out[71]=PlayAnimation

关心 AutorunSequencing 的细节的一个原因是能够用 Export (输出)这个命令来自动地产生一个动画录像(例如,在 QuickTime 或 Flash 格式中). 在默认下,Export 将通过运行 Manipulate 这个命令经过一个自动运行周期来产生一个动画.

如果 AutorunSequencing 不能使你对动画序列有足够的控制,你可以用书签这个将在 下一节 中介绍的特性来定义一列途径点 - 参数值的组合 - 然后创造出一个平滑的通过定义点插值的动画. 这使得能够对动画的确切的途径有完全的控制.

对参数值的组合加书签

Manipulate 函数,尤其是当它们有很多控件时,能用来在大海里捞针:一种特定的多参数值的组合产生一个特定的有意义的结果. 当你找到如此的一组数值时,你也许希望把它们存下来作为未来的参考. 通过在 Manipulate 输出右上角的 菜单, Manipulate 提供了几个能实现这个功能的特性,

想使一个单一的数值进入一个你能作为静态输入使用的形式,在 菜单中使用粘贴快照这个命令. 结果会作为一个新的单元被插入在 Manipulate 输出之下.

In[72]:=
Click for copyable input
Out[72]=PlayAnimation

这里是一个在这个例子中使用粘贴快照的结果.

In[73]:=
Click for copyable input
Out[73]=

当前的三个数值都被复制到 DynamicModule(动态模块)的变量定义块里,第一个自变量被复制到正文中. (DynamicModule 被使用的原因是在正文中含有明确的对 Dynamic 的使用的情况下,这会造成更正确的功能. 取决于你想用这个结果做什么,你当然可以自由地用 ModuleWith ,或 Block 来代替 DynamicModule ,并且不需要在表达式中作任何另外的改变. 或者,你可以把整个的赋值块复制/粘贴到你正在编写的其它程序里等等. ModuleDynamicModule 之间的区别在 "高级动态功能" 中有更详细的介绍.)

如果你不想立即就提取那个地点,你只是想记住它以便在未来访问,那么在 菜单中选择加入到书签中. 这会显示出一个面板,让你给书签命名,并通过点击 这个按钮来把它加入到这个 Manipulate 所知道的书签列单中, 或通过点击 这个按钮来取消加入.

在添加了一个书签之后,你为它所指定的名字会在 菜单中显示. 从那个菜单中选择它的名字会引起所有的参数都反弹到当书签被加入时它们所具有的数值. 并且也注意,每一个 Manipulate 都记住了它的所有控件的设置,你能通过在这个菜单中选取初始设置来反弹到那些数值上.

一旦你开始放置书签,在 菜单中有另外两个项目变得重要相关:粘贴书签动画书签.

书签是一个给定的参数空间中的地点列单,你能通过选取粘贴书签项来提取那个列单中的原始数据. 造成的列单中的每一个元素都是 bookmarkName:>parameterValues (书签名 :> 参数值)的形式. 这个列单在语句构架上是适合于作为 Bookmarks 选项的设置来重新插入到 Manipulate 输入中的. (这能使你,例如,在把它们恢复成一个新的 Manipulate 输出中的有效书签之前,通过人工编辑来改变这些书签,或者在它们之上运行一个程序.)

动画书签菜单命令工作起来和在上一节中描述的自动运行命令很一样,除了这一点不同之外,那就是它并不对每一个参数的数值范围进行动画,而是创造一个对这些书签所指定的点插值的动画.

在对书签进行动画时产生的插值是通过 Interpolation (插值)命令内部地完成的. Manipulate 甚至接受 InterpolationOrder (插值阶次)这个选项来调节这个动画是怎样从一个点进行到下一个. 如果有足够的书签, Automatic 的默认值执行二次插值,否则,执行线性插值.

当一个含有明确书签的 Manipulate 输出通过使用 Export 被输出到一个录像动画格式时,所造成的录像将是动画书签 产生的序列的一个周期.(如果没有书签存在,结果是自动运行的一个周期.)