原标题:智能设计 | 建模&仿真(3):力学仿真
智能设计| 建模& 仿真(3) :力学仿真
刘肖健
浙江工业大学
1. 案例描述
画出“磁场的形态”,大致如下图所示:
原理描述:
想象页面中有一个磁铁,它对页面空间中的任何一个坐标位置将产生引力或斥力(假设正极产生引力,负极产生斥力)。如果有多个磁铁,它们的磁力效应将叠加在一起。
反过来,页面空间中的任意一点会受到所有磁铁(正极和负极)的磁力作用,这些力会叠加在一起,最后形成一个合力,合力的方向即该点的磁场方向。
仿真系统输入:
由用户在页面中放入若干磁铁Shape ,或磁单极。请自行设计用户的输入形式,以及正极和负极的标识方式。
仿真系统输出:
给出页面中任意一点的磁场方向和磁场强度。请自行设计磁场的给出方式,比如用一个均匀间隔的矩阵,在每个节点上画出磁场的方向。
说明:
1 )这个任务只给提出了需求,没给出具体形式,请自己设计。
2 )动手前请规划好做事的顺序,比如先设计力的表达方式(大小、方向、作用点),再编写两个力的合力计算方法,再编写多个力的合力计算方法;先编写页面中只有一个磁单极的情况,再编写有多个磁铁的情况,等等。
3 )每做一步,测试无误后再往下走;出了错,就退回上一步。宁可做不完,也不要搞出一个运行不通的程序。
4 )适当记录开发过程(包括做错的),不要只展示一个结果。
2. 规划
确定准备实施这个任务后,我计划分三步走:
1 )模拟一个磁单极的力学效应;
2 )模拟多个磁单极的力学效应;
3 )画满页面的磁场。
用户界面:
只有最后一个按钮是最终要做的,前面两个都是中间过程。作为一个学习案例,我把三个按钮单独做了出来。
3.事件设计
先来设定一下目标实现的方式,这里采用最简单的一种:按钮按下后,根据页面中的“磁铁”正负极来绘制页面上每一点处的磁场方向和磁场强度。如下图所示:
我们这样设计程序事件行为的输入和输出:
1 )磁铁由用户给定,当前页面中的任何一个Shape 都可以被当做磁铁处理,其正负极通过Shape 的Name 属性来标识,由用户设定;
2 )磁场方向通过一个短线段的方向来表达;
3 )磁场强度通过线段的粗细来表达。
实际上还可以给线段加箭头,表达该点处的受力方向。这一步不用编程,可以很快地手工完成。
上图中间有个位置线段细到看不见,这里的受力是零,这个位置叫“拉格朗日点”……是个天文学概念,我们不严格的借用一下。
磁铁可以是各种形状,但只有正负极两点的位置决定了磁场的特性,所以这里就把正极或负极简化成一个圆点,它的图形名称(即Name 属性)分别改为“+ ”和“- ”,如下图右边栏对象管理器中的显示——这里有两个正极(红色)和两个负极(蓝色)。
上图放了两个磁铁,可以想象成有两个直棒或U 形磁铁,愿意把磁铁画出来也行,只是它们的名称不能是“+ ”和“- ”,以免干扰计算。
当然我们还可以设计更复杂、更精细的形式,把磁力线画完整(而不是短线段)也是可以的。
4.磁场方向和强度的计算原理
根据中学物理讲到的力学原理,页面上任意一点的磁场方向和强度是这样计算的:
页面中的任意点O 处,受到正极的吸引力OA 和负极的排斥力OB ;其中OA 是向着正极方向的,OB 是背向负极方向的。OA 的大小与OC1 的长度成反比——离磁铁越近受力越大,越远受力越小;同理,OB 的大小与OC2 的长度成反比。因为OC1 比OC2 短(近),所以OA 比OB 大一些。
OA 与OB 的合力是OF ,它的大小和方向用OA 和OB 通过矢量合成(矢量“加”)可以得到。
页面中可以有若干个正极或负极。
用一个双循环在页面中均匀布点,把每一处的磁场方向和强度都表达出来。表达方式是画一条短线段,线段方向及该处磁场强度,但是用线段长度来表达磁场强度不太好看,整个页面充满长长短短的线会显得很乱。所以可以改用线段的粗细来表达强度,线段越粗磁场强度越大。
线的粗细用s1.Outline.Width 变量来设置,比如设为页面宽度的千分之一:
s1.Outline.Width =ActivePage.SizeWidth/1000
5.按钮1:只有一个磁单极的情况
一般磁铁的正负极是成对出现的,但是只有一个正极或负极(即磁单极)的情况似乎也有,这是个神秘的物理学问题。
n徒手画一个磁单极
我们徒手画一个正的磁单极,形状色彩随意,别忘了给它命名为“+ ”:
n任务内容(按钮的功能)
正磁单极(只有一个)放入页面后,用鼠标在页面上点击一下,程序在点击处画一条线表达该处的受力方向。
在按钮的事件程序Magnet_Click 中增加两句代码:
Dim shift As Long
ActiveDocument.GetUserClick x, y, shift, 10, False, cdrCursorPickOvertarget
GetUserClick 函数是获取当前鼠标点击处的坐标位置。执行到这一句时,鼠标会变为准星形状(一个圆圈中间一个十字);在页面左键点击后鼠标,点击处的两个坐标值分别存入变量x 和y 。“10 ”的意思是等待10 秒,如果10 秒内没有点击,则放弃,鼠标变回箭头。点击后,鼠标也会变回箭头。
Shift 是判断点击鼠标时有没有按下shift 键。最后一个变量cdrCursorPickOvertarget 是指定鼠标的形状,这个值是表示准星形状,还有其他若干种形状可选,敲代码时,敲入前面的逗号后可选列表就会出现,有多达十几种鼠标形状:
因为页面中只有一个正的磁单极,所以鼠标点击处的受力方向应该指向磁单极。
我们准备实现的结果是下图这样的:
这是10 次鼠标点击的结果。可以看到,每条线都与磁铁(红圆)的圆心共线,且离磁铁越近,线越长,表示受力越大。
如果鼠标点击处放一个磁针,则线段方向即磁针负极所指的方向。
n原理
画一条线来表示该点的受力方向和受力大小。既然是一条线,要把它画出来需要确定两个因素:
1)线的起点
起点就是鼠标点击处的坐标,这个我们上面已经说了如何获取了。
2)线的终点
这里我们已经知道了线的方向是从鼠标点击处向着磁铁的方向,再给出一个线的长度就可以确定终点的位置了。线的长度用来表示受力大小。距离磁铁越远,受力越小,那我们就把受力(线的长度)定义为距离的倒数好了。
n计算受力线终点
要计算线的终点,这里解释一下math 模块中的点偏移函数:math.shiftPt(p, p0, p1,d) 。这个函数的作用是: 把p点沿着p0-p1方向移动长度d,返回移动后的坐标。
先定义好三个点的位置变量:
Dim pStart, pEnd, pMag '受力线起点、终点、磁铁位置
受力线起点直接用鼠标点击得到的x 和y :
pStart = Array(x, y)
要找到磁铁位置,首先得找到磁铁。现在页面中只有一个磁铁,即名称为“+ ”的shape ,我们遍历当前页面所有物体找到它,并用s1 表达。
For i= 1 To ActivePage.Shapes.Count
If ActivePage.Shapes(i).Name ="+" Then
Set s1 = ActivePage.Shapes(i)
Exit For
End If
Next
上面的代码中:ActivePage.Shapes.Count 是当前页面中图形的数量,一个个检测它们的名字(Name 属性),如果遇到有名为“+ ”的,则将其赋予s1 ,然后用Exit For 跳出循环,后面的不用再找了,找到一个即可。
磁铁的位置pMag 为:
pMag = Array(s1.PositionX,s1.PositionY)
s1.PositionX 和s1.PositionY 是图形s1 的位置。
这里有个小问题:s1 不是一个点,它是一个有面积的图形,它的“位置”是指的哪一处的位置呢?
对此,CorelDraw 用一个内部变量ReferencePoint 来设置确定图形位置的参考点:
列表中:cdrCenter 表示图形中央,cdrBottomLeft 表示图形左下角点……其他选项的含义也都体现在名称里了。
我们要的磁铁位置显然是中心点。 这句写按钮的事件程序Magnet_Click在最前面,变量定义后,确保使用图形位置信息之前它已经被设置好:
ActiveDocument.ReferencePoint= cdrCenter
现在来计算受力大小,也就是线的长度,我们为它定义一个dForce 变量:
Dim dForce As Double '受力大小(线长)
dForce= 1/math.distPP(pStart, pMag)
这里是假设受力大小跟距离成反比。可能实际上并非如此,如果有更精确的力学计算公式,这句替换一下即可。
函数math.distPP 用来计算两点的距离,这里的两点给出的是就是刚计算出来的pStart 和pMag 。在math 模块中可以看到它的定义,十分简单,就是一个勾股定理的应用而已:
现在条件已经齐备,可以计算线的终点pEnd 了:
pEnd = math.shiftPt(pStart,pStart, pMag, dForce)
上面这句的意思是:把起点pStart (鼠标点击处),沿着pStart 与pMag (磁铁)连线方向移动dForce 那么长一段距离,得到终点pEnd 的坐标。
n画受力线
有了起始点和终点,可以用模块draw 里的polyline 函数画线了:
Dim points
points=Array(pStart, pEnd)
Set s1 = draw.polyLine(points)
定义了一个points 数组,它用来表示画线要通过的一系列点的位置,这里只有两个点,用函数Array 把这两个点组成一个数组,然后赋予points ,然后用这个points 数组作为画线函数polyLine 的参数。
执行一下看看结果:
上述每一个线段的生成方式:先点一下对话框上的按钮,再点一下页面中的位置。线段上的箭头是手工设置的。
可以看到这些线段的箭头都指向磁铁,而且距离磁铁越近的线段越长,表明算法是正确的。
6. 按钮2 :有多个磁单极的情况
n任务内容
页面中有多个磁铁时,磁单极不止一个。可以是多个,可以正负极数量不一致,可以都是正极或负极。你可能会觉得奇怪,磁场怎么可能全是正极没有负极?这种情况也是有的,比如天体系统。所有星球都有引力,万有引力(好像还没发现有斥力的星球),它们相当于宇宙中一个个 正的磁单极,离得越近引力越大,离得越远引力越小,跟磁铁原理一样——引力与距离的关系可能比较复杂,不是简单的反比关系,不过只要能用数学公式表达,就能写进程序。
引力场、磁场、电场等,可以看做同一类事物,爱因斯坦晚年就在从事“ 统一场论”的研究,现在统一得如何了还不清楚,不过我们可以先在这个案例里把它们统一了。
来看看有超过一个磁单极的情况。先放两个做示范,一正一负。
接着首先要处理的问题是多个磁单极的表达。上一节定义了一个pMag 来存储一个磁单极的坐标,这里定义一个pMags 数组来存储所有磁单极的坐标,再定义一个sMags 数组来存储代表磁单极的所有图形。
Dim pMags
Dim sMags As Shape
n找出磁单极及其位置坐标
下一步的任务是把页面中所有的磁单极都找出来,图形放在数组sMags 里,它们的中心坐标放在数组pMags 里。上述两个数组都是动态数组,定义它们时还不知道有几个磁单极,要一个个检测完才知道。
下述代码是动态数组应用的经典案例,要牢记。
Dim pMags
Dim sMags As Shape
ReDim pMags(0)'数组长度定义为1,即先假设只有一个磁单极
ReDim sMags(0) As Shape
For i= 1 To ActivePage.Shapes.Count'遍历当前页面的所有图形,一个个检测是不是磁单极
Set s1 = ActivePage.Shapes(i)
If s1.Name = "+" Or s1.Name ="-" Then'检测名称,是如果是磁单极,则……
Set sMags(UBound(sMags)) = s1'把检测到的磁单极保存在数组末尾
ReDim Preserve sMags(UBound(sMags) + 1)As Shape'数组长度增加一个,已有内容保留
pMags(UBound(pMags)) =Array(s1.PositionX, s1.PositionY)'磁单极坐标保存在数组末尾
ReDim Preserve pMags(UBound(pMags) +1)'数组长度增加一个,已有内容保留
End If
Next
If UBound(sMags) > 0 Then'数组长度大于1时才做这种操作
'去除数组最后一位(数组最后增加的一个,没用到要去掉)
ReDim Preserve sMags(UBound(sMags) - 1) AsShape
ReDim Preserve pMags(UBound(pMags) - 1)
End If
上述代码运行完后,两个数组sMags 和pMags 的长度应该是一样的,有一个磁单极就有一个磁单极中心位置坐标。如果数组长度大于1 (即UBound(pMags)>0 ),则数组长度就是磁单极的数量。如果数组长度为1 (即UBound(pMags)=0 ),则可能有两种情况:一是有一个磁单极,二是一个也没有。因为数组长度一开始就定义为1 ,即使一个磁单极也没找到也不会重新把数组长度定义为0 ,没有这种定义方法。
判断数组里有没有磁单极的方法很简单,就是检测对应的中心坐标pMags(0) 是不是一个数组。如果有磁单极,则pMags(0) 里保存的是它的坐标位置数组Array(s1.PositionX,s1.PositionY) ,否则就不会是数组。
下述代码检测有没有磁单极,如果没有,则跳出程序,给出错误报告,然后结束。
If UBound(pMags) = 0 And (Not IsArray(pMags(0))) Then
MsgBox "Magnets not found."
Exit Sub
End If
IsArray(pMags(0)) 函数用来检测pMags(0) 是否数组,是则返回True ,不是则返回False ;Not 把这个结果反了过来,把True 变成False ,把False 变成True 。
如果UBound(pMags)= 0 和(Not IsArray(pMags(0))) 两个条件同时满足(都是True ),则表示:1 )pMags 数组长度为1 ;2 )pMags 数组的唯一成员pMags(0) 不是数组,不是一个坐标位置。这就表示没找到磁单极,那就歇工吧,Exit Sub 表示退出程序,后面的代码都不用执行了。
n画出所有受力线
上一节实现了在鼠标单击处用一条线段来显示该点受力,这节要实现在鼠标单击处用一条线段来显示该点的 所有受力,及其 合力。
每一个磁单极都会在鼠标点击处产生一条受力线。受力线的做法跟上节类似,可以直接搬过来放在一个循环里面。要注意的是,如果磁单极是负极,线的方向相反,是背离磁单极方向的。具体到程序语句中,就是math.shiftPt(p, p0, p1,d) 中的p0 是磁单极位置坐标,p1 是鼠标点击位置坐标,则点的偏移方向p0-p1 是背离磁单极的。
先获取鼠标点击处的位置,跟上节相同:
Dim shift As Long
ActiveDocument.GetUserClick x, y, shift, 10, False, cdrCursorPickOvertarget
Dim pStart, pEnd
pStart= Array(x, y) '受力线起始点位置即鼠标点击位置
然后是画受力线:
Dim dForce As Double '受力大小(线长)
Dim nMags As Integer: nMags = UBound(sMags) + 1'磁单极数量
Dim points'数组:受力线首尾坐标点位置
For i= 0 To nMags – 1'遍历各磁单极
Set s1 = sMags(i)
dForce = 1 / math.distPP(pStart,pMags(i))'计算受力:与磁单极距离的倒数
'计算末尾点pEnd位置
If s1.Name = "+" Then'正极
pEnd = math.shiftPt(pStart, pStart,pMags(i), dForce)'中间两参数指示了受力方向
ElseIf s1.Name = "-" Then'负极
pEnd = math.shiftPt(pStart, pMags(i),pStart, dForce)'中间两参数指示了受力方向
End If
points = Array(pStart, pEnd)'构建画受力线的点坐标数组
Set s1 = draw.polyLine(points)
s1.Outline.EndArrow = ArrowHeads(3)'设置受力线尾端箭头
Next
上述代码中,循环里面的内容是在只有一个磁单极那个按钮里面写过的,直接拷贝过来。
执行结果:
可以看到每个点的两条受力线分别指向红色的正磁单极,以及背离蓝色的负磁单极。受力的大小(线的长短)也是正确的,与距离成反比。
增加一个正磁单极再执行一次:
观察发现,也是对的,三条线的延伸方向分别穿过三个磁单极。
n画出合力线
下面要把上图的几个受力线算出一个合力,并且也用一条线段画出来。
合力的计算方法很简单,就是把几条受力线所代表的矢量加起来(当然是用矢量运算法则来加)就行了。具体算法可以分为三步:算出每条受力线的矢量、算出合力矢量,最后根据合力矢量画出合力线。
1)算出每条受力线的矢量
从初中数学可以知道,矢量的表达方式跟点坐标是一样的,也就是一个Array(x,y) 数组而已。从受力线计算矢量只要用受力线终点坐标pEnd 减去起始点坐标pStart 即可。按矢量计算法则,两个点的x 坐标和y 坐标分别进行减法运算。
在上面那段代码的循环前面增加一句数组定义:
Dim vForce: ReDimvForce(nMags - 1)
在循环里也增加一句计算式,放在循环里面末尾:
vForce(i) = Array(pEnd(0) -pStart(0), pEnd(1) - pStart(1)
pEnd 的x 值是数组的第一个成员pEnd(0) ,y 值是数组的第二个成员pEnd(1) ;pStart 同样。
2)算出合力
计算合力即对各受力矢量进行加法运算。这个更简单,把所有受力矢量的x 和y 值加在一起即可:
Dim vForceX: vForceX = Array(0, 0) '定义合力,并初始化
For i= 0 To nMags - 1
vForceX(0) = vForceX(0) + vForce(i)(0)'x坐标累加
vForceX(1) = vForceX(1) + vForce(i)(1)'y坐标累加
Next
3)根据合力矢量画出合力线
算出来的vForceX 是一个矢量,要画出合力线还要根据它算出合力线的首尾点坐标才行。
起始点坐标已经算出来的,就是鼠标点击处,即pStart 。
末尾点坐标是pStart 与vForceX 的加和,即两个数组的x 和y 值分别相加。
pEnd= Array(pStart(0) + vForceX(0), pStart(1) + vForceX(1))
points= Array(pStart, pEnd)
Set s1 = draw.polyLine(points)
s1.Outline.EndArrow= ArrowHeads(3)'合力线加箭头
s1.Outline.Color= CreateRGBColor(255, 0, 0)'合力线改为红色
s1.Outline.Width= 10 * s1.Outline.Width'合力线加粗
合力画出来这样,红色粗线是合力:
下面是两个磁单极的情况,可以很清晰地看到合力是两个分力构成的平行四边形的对角线。
n合力线的另一种表达形式
如果要画出整个页面空间每一处的磁场方向,合力线最好长度一致,否则看起来非常不好看。那么合力强度怎么表达呢?可以用线的粗细,而线长都改为等长。
在上述语句pEnd 的计算式后面加一句:
pEnd = math.shiftPt(pStart,pStart, pEnd, ActivePage.SizeWidth / 50)
这句用偏移函数shiftPt 重新计算了pEnd 的位置,新的pEnd 在pStart-pEnd 的连线上。shiftPt 里的第三个参数pEnd 是上一步算出的末尾点坐标。pStart 和pEnd 之间的距离改为了固定值ActivePage.SizeWidth / 50 。现在所有合力线都一样长了。
最一句“合力线加粗”改为动态可变的粗细:
s1.Outline.Width = math.VectorLength(vForceX)/50
math.VectorLength 函数是求一个矢量的长度。这里用合力矢量vForceX 的长度的50 分之一作为合力线的粗细。这个值可以根据测试结果修改成最合适的大小。
把画分力线的语句注释掉(前面加英文单引号“'”)变成注释语句。合力线加箭头的语句也注释掉,因为箭头大小是根据线的粗细来的,大大小小的很难看。加箭头最好用同等粗细的线。
7. 按钮3 :画出整个页面的磁场
把页面当磁场,画出每一点处的合力线,就可以观察到整个磁场的形态了。
方法很简单:在刚才的程序外面套一层循环,对每一个点都做同样处理就行了。只是点的位置不再由用户点击确定,而是程序给定的。
我们把画一条合力线的操作定义为函数(Function ),然后不停地调用这个函数,以点位置作为参数,这样它就可以在每个给定点的位置画合力线了。
n创建一个画单条合力线的函数
建立一个空Function :
Function ForceLine(pStart, dLength As Double, aThick As Double) As Shape
End Function
这个函数有五个参数:
1) sMags 为磁单极图形,是一个数组;
2) pMags 为磁单极坐标位置,也是一个数组;
3) pStart 为绘制合力线的点位置坐标,也是数组,即原程序里面由鼠标点击产生的点坐标;
4) dLength 为合力线的长度;
5) aThick 为合力线的粗细参数,合力线的粗细为aThick 乘以合理矢量长度。
把按钮MultiMagnets的事件子程序代码全部复制到Function里,然后做如下修改:
1) 删除sMags 和pMags 定义与生成的语句,因为它们已经由参数提供;
2) 由参数pStart 提供合力线起始点坐标,所以删除pStart 的定义语句Dim pStart 和赋值语句pStart=Array(x,y) ;
3) 删除获取鼠标点击的语句ActiveDocument.GetUserClick ,因为不需要鼠标点击了;
4) 计算pEnd 的语句中的合力线长参数ActivePage.SizeWidth / 50 改为dLength ;
5) 设置线宽的语句中的“/50 ”这个数字改为“* aThick ”;
6) 在最后增加返回值语句:SetForceLine = s1 ;
7) 删除设置图形参考点的语句ActiveDocument.ReferencePoint= cdrCenter ,一个文档设置一次就行了,不用放在Function 里面多次执行。
n画多条合力线
把第二个按钮中的变量定义和pMags 、sMags 的定义和获取语句,全部复制进来,如下:
Dim i As Integer, j As Integer
Dim x As Double, y As Double
Dim s1 As Shape
ActiveDocument.ReferencePoint= cdrCenter
'------获取磁单极
Dim pMags
Dim sMags As Shape
ReDim pMags(0)''数组长度定义为1,即先假设只有一个磁单极
ReDim sMags(0) As Shape
For i= 1 To ActivePage.Shapes.Count'遍历当前页面的所有图形,一个个检测是不是磁单极
Set s1 = ActivePage.Shapes(i)
If s1.Name = "+" Or s1.Name ="-" Then'检测名称,是如果是磁单极,则……
Set sMags(UBound(sMags)) = s1'把检测到的磁单极保存在数组末尾
ReDim Preserve sMags(UBound(sMags) + 1) As Shape'数组长度增加一个,同时保留已有内容
pMags(UBound(pMags)) =Array(s1.PositionX, s1.PositionY)'磁单极坐标保存在数组末尾
ReDim Preserve pMags(UBound(pMags) +1)'数组长度增加一个,同时保留已有内容
End If
Next
If UBound(sMags) > 0 Then'去除数组最后一位(数组最后增加的一个,没用到要去掉)
ReDim Preserve sMags(UBound(sMags) - 1) As Shape
ReDim Preserve pMags(UBound(pMags) - 1)
End If
If UBound(pMags) = 0 And (Not IsArray(pMags(0))) Then
MsgBox "Magnets not found."
Exit Sub
End If
下面的任务是生成一系列的pStart ,然后对每一个pStart 调用刚编好的函数ForceLine 画合力线。
先把pStart 的定义语句加上,放哪都可以:
Dim pStart
要把整个页面填满,需要一个双循环,每行画多少条合力线,一共画多少行,这样一行行画出来。
每行画的合力线数量可以由用户在界面上给出(最下面的输入框)。
把每行线数输入框命名为nLines ,默认值是20 。
计算画合力线的行列数:
Dim nRows As Integer, nCols As Integer
nCols= nLines.Value
nRows= Int(nCols * ActivePage.SizeHeight / ActivePage.SizeWidth)
列数直接来自用户输入。行数根据页面宽高比和列数计算出来,由于算出来不是整数,要用Int 抹掉小数取整。
然后计算行列间隔距离,以便后面的pStart 起点位置计算:
Dim dx As Double, dy As Double'x和y方向的间隔
dx =ActivePage.SizeWidth / nCols
dy =ActivePage.SizeHeight / nRows
用双循环来画一条条的合力线:
Dim dLen As Double'合力线长
dLen= 0.8 * dx
For i= 0 To nRows
For j = 0 To nCols
x = j * dx
y = i * dy
pStart = Array(x, y)
Set s1 = ForceLine(sMags, pMags,pStart, dLen, 1 / 40)
Next
Next
每一个pStart 需要首先计算出来,作为调用ForceLine 函数的参数。
sMags 和pMags 已经有了,可以直接用。
定义合力线长变量dLen ,其值取列间隔dx 的0.8 倍,可以确保不长不短。
合力线粗细的比例因子选1/40 ,这是测试了几次的结果。
绘制结果:
8. 进一步拓展
绘制磁场的基本技术内容就这么Over 了。这个程序还有很大的拓展余地,比如:
1) 把更多的参数放在界面上让用户输入,如合力线长度、合力线粗细系数、要不要箭头等;
2) 合力线可以有更好看的形式,如做成两端圆头,或甚至摆上一个小指南针的图形,让它的转角跟磁场方向一致,这些都很简单,不停的 复制、粘帖、计算转角即可:
3) 可以把磁力线的曲线形态画出来,而不仅是一系列短线;下图是磁力线的一种画法(这个磁场里有14 个磁单极,都是正极):
4) 可以画出星系引力场;前面说过,这是一种只有引力没有斥力的场,也就是只有正磁单极;
5) 可以自己设计“场”的力学规则,用来生成一些奇怪的(但是好看的)复杂曲线;
6) ……
9.总结
1) 跟前一个案例一样,这个案例的关键也不是编程,而是如何在虚拟世界里再现现实世界的规律;
2)程序中设置了一些可以修改和拓展的模块,如引力和距离的关系,这种关系可能比我们想象的要复杂,但只要有公式,我们就能编写出来;
3)在磁场的视觉化表达上,需要表达里的方向和强度两个信息,这里是用了最简单直接的方法,就是用一条短线的方向和粗细两个特征来表达;我们还可以有更好看的可视化方式,比如用色彩,下图是用HSB中的H色彩分量表达磁场方向(用math.alfaPP函数计算矢量角度),用方块边框粗细表达磁场强度,也可以用方块大小甚至圆角大小来表达;
4)还有些参数可以设置,比如磁铁的磁力大小,可以通过Shape的尺寸给出;
5)如果想画出3D空间的磁场,原理一样,可以在Rhino中编写这个仿真程序;
6)“想法很多,一下手就乱 ”,这是新手的常见问题。本文这个程序 尽管并不复杂,直接面向结果时还是会有些头大;这需要把问题一步一步分解开,分解成简单而清晰的功能模块(比如合力计算),定义成一个个函数,按顺序逐步完善——清晰性可以让复杂的事情变得不那么难搞,当你感到你正在做的不是一项智力活动,而是一项体力工作,这说明够清晰了。
这是我教儿子学编程用过的案例之一,中学物理……学习快乐!返回搜狐,查看更多
责任编辑: