二维几何光学模拟
项目材料
目录结构
本仿真模拟程序的目录结构如下图所示
├── README.md
├── index.html
└── static
├── css
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── font-awesome.css
│ ├── font-awesome.min.css
│ └── style.css
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.*
│ ├── glyphicons-halflings-regular.*
└── js
├── bootstrap.js
├── jquery-3.6.0.js
├── lens3.js
├── light2D3.js
└── light3.js
index.html
整个仿真模拟的入口文件,用户交互界面README.md
仿真程序代码的说明文档,即本文件static/
静态资源目录css/
样式表目录,存放所有的css
样式style.css
所有自定义的css样式表
fonts/
字体目录,存放所有的网络字体js/
脚本目录,存放所有的js
控制脚本light2D3.js
整个程序的控制脚本,最核心的代码逻辑lens3.js
透镜、平面镜等元件类的定义文件light3.js
光线类的定义文件
外部依赖
Bootstrap
- v3.4.1
- 简洁、直观、强悍的前端开发框架
- 包含
css
样式与js
插件 - 官方中文文档
jQuery
- v3.6.0
- A fast, small, and feature-rich JavaScript library
- 包含
js
脚本 - 官方API文档
Font Awesome
- v4.7.9
- An Internet's icon library and toolkit
- 包含
css
样式与特殊字体 - 官方文档
代码说明
HTML
整个index.html
主要分为三个部分,即#header
、#body
、#footer
三个<div>
标签,分别容纳页面的页头、主体与页脚。
页头
页头即为页面导航栏部分,由导航头、两个左导航条组成与一个右导航条,导航头显示标题,第一个左导航条是物理系Phylab的跳转网址,第二个左导航条即为程序工具栏页面,右导航条为制作者信息。
控制栏由按钮组组成,其中按钮组的类型包括普通按钮与下拉菜单启动按钮两种,下拉菜单启动按钮后面需要跟上下拉菜单的具体内容。普通按钮用于切换程序状态或触发事件,本程序中,添加光线、添加平面镜、清空画布
等按钮为触发事件,绘制路径、绘制透镜、平移、旋转等操作为切换程序状态。下拉菜单启动按钮用于输入初始化参数,其具体内容包括各类输入参数的表单组。
下拉菜单启动按钮等部分的具体实现请参照Bootstrap官网文档
主体
主体即为程序的呈现部分,包括左侧边栏(控制栏)、主显示框两部分。控制栏部分使用nav-pills
实现,可以修改已创建的光学对象。主显示框包含显示主程序的<canvas>
画布#canvas
。
页脚
页脚也是一个导航栏,它是程序的状态栏,会显示当前鼠标下的元件信息。
JavaScript
整个light2D3.js
主要分为3个部分,包括全局变量定义区、功能函数与事件函数定义区、主程序函数定义区
全局变量定义区
根据 ECMAScript 6 标准,我们不再推荐使用var
定义变量,因此,在本文件中,我们一律使用const
或let
定义变量。关于这三者的具体区别,可以参考MDN Web Docs。
常量部分
drawing
主程序画布#canvas
的HTMLElement
context
主程序画布的CanvasRenderingContext2D
- 其余常量分别表示对应id的
HTMLElement
,请在index.html
中查找对应的id
变量部分
add_pole
光线的偏振状态,0为S波,1为P波,默认为S波light_set
所有光线的集合mirro_set
所有面镜的集合lens_set
所有透镜的集合refract_only
光线的参数,是否只显示折射rotate_ref
旋转操作的参考点,一个具有x
与y
属性的对象paintset
在绘制路径模式下的点集,每个元素是一个点对象,对象有x
与y
属性Cpoint
在绘制弧形模式下的圆心,是一个点对象angle_info
在绘制弧形模式下的起始与结束角度,是一个对象,有begin
与end
属性is_concave
在绘制弧形模式下的凹凸性,true
指凸透镜,false
指凹面镜locp
各透镜状态下鼠标的点,有paint
、circle
、rect
三种属性,这个属性应该可以舍弃,之前的学长应该没有设计好,这个变量之后可以删掉rect_p
在绘制矩形模式下的第一个点ele_operate
操作对象,平移与旋转等动作下被操作的元件对象ele
当前鼠标所指代的元件对象is_**
各种状态,默认只有is_transfer
是true
,其他为false
,即默认是平移模式
功能函数与事件函数定义区
string2RGB(color)
color
颜色字符串,如"#aa759f"
return
返回一个RGB数值的数组
dis(p1, p2)
p1
一个点对象p2
另一个点对象return
返回两个点之间的距离
rgb2rgba(rgb, a)
rgb
一个储存RGB三个数值的数组a
透明度return
一个rgba格式的字符串,如"rgba(122,53,200,0.6)"
modtheta(x, y, r)
x
一个角度数值(弧度)y
一个角度数值(弧度)r
一个角度数值(弧度)return
(x + r) % y - r
Rreflect(t1, t2, pole, n1, n2)
t1
入射角t2
折射角pole
偏振方向,同全局变量add_pole
n1
入射空间折射率n2
折射空间折射率return
透射系数R
Rrefract(t1, t2, pole, n1, n2)
t1
入射角t2
反射角pole
偏振方向,同全局变量add_pole
n1
入射空间折射率n2
反射空间折射率return
反射系数R
*.onclick
各个按钮点击后程序对应的改变changeStates(obj)
obj
一个HTMLElement
,见index.html
中该函数的使用- 将
obj
所代表的状态改为true
,其他状态改为false
createInput(set, index=set.length-1)
set
一个集合,如mirro_set
index
集合的下标,默认为最后一个元素return
给set
中下标为index
的元素创建一个属性输入框组
主程序函数定义区
canvas.onmousemove
- 鼠标移动事件函数,该函数是本文件中最复杂最庞大的函数,这也表明了该函数定义的不合理性,但是重新构建事件函数的成本过高,由之后的开发者自己决定是否需要重构。
- 本函数首先确定鼠标的位置,然后判断当前的状态,如果状态为平移、旋转,则进入
if
语句的第一个分支。 - 绘制路径、绘制弧形、绘制矩形、删除四个模式分别对应
if
语句的剩下四个分支。 - 每个分支各自定义了该分支下的
onmousedown
或onmouseup
事件 - 对于绘制路径、绘制弧形、绘制矩形以及旋转等操作,均有两次或多次点击操作,这些操作分别在
onmousedown
事件中有不同的分支语句。
mouse_on_ele(loc)
loc
一个点对象,其实就是当前鼠标的位置return
返回当前鼠标下的元件对象
light_on()
return
返回是否有光线在本函数中被操作,如果有则返回true
,否则返回false
- 本函数也非常复杂,是追踪光线的折射与反射行为的函数,每次所有光线遍历一次,检查子光线是否会有折射或反射操作
draw_all(context)
context
主画布的CanvasRenderingContext2D
对象- 在主画布上绘制所有的元件与光线
draw_tem(context)
context
主画布的CanvasRenderingContext2D
对象- 在主画布上绘制所有临时元件,例如正在绘制中的透镜
init()
- 初始化每一条光线
main()
- 主函数,重复运行。每次运行都会清空画布,初始化光线,进行光线追踪,重新绘制元件与光线
light3.js
与lens3.js
定义了光线与各类原件的类
Light(begin, rgba, width, length, theta, pole, index)
- 光线类
- 成员变量
type
对象类型,light
length
这个属性也没有用到,应该是之前学长定义的冗余的变量,可以删除rgba
含有四个元素的数组,每个元素为RGBA中的一个数值color
字符串表示的颜色,如"#34383c"
begin
一个点对象,作为光线的起始点begintheta
起始点出的出射角度,弧度制index
当前对象在对应的set
中的下标width
光线的宽度chlidlight
包含本条光线所有子光线(ChildLight)对象的数组anchor
一个点对象,是在移动操作中,鼠标在对象上点击的位置transferable
当前对象是否可平移rotatable
当前对象是否可旋转info
控制栏中当前对象的信息的HTMLElement
- 成员方法
addchild(begin, theta, n, rgba, width)
begin
- 一个对象点,子光线的起始点
theta
- 子光线的出射方向
n
- 子光线所在空间的折射率
rgba
一个长度为4的数组,数组中每个数值对应于RGBA值width
子光线的宽度- 为当前光线添加一条新的子光线
addchilds(beginset, thetaset, nset, alphaset, widthset)
- 与
addchild()
函数对应,每个参数都是addchild()
函数参数的列表形式。只有alphaset
不同,alphaset
是颜色透明度alpha
的列表,alpha
为 0~1
之间的实数。
- 与
deletechild(i)
- 删除第
i
个子光线
- 删除第
draw(context)
context
主画布的CanvasRenderingContext2D
对象- 在画布上绘制当前光线
init()
- 初始化当前光线使得子光线只有一条
transfer(x, y)
x
,y
沿x
与y
方向平移的距离- 平移操作,平移当前光线
rotate(theta)
theta
旋转的角度,弧度制- 旋转操作,沿起始点旋转
theta
角度
pole_info()
return
当前光线的偏振信息,如"S波"
show_info()
return
字符串格式的当前元件的信息
attributes_input()
return
返回控制栏中一个元件及其相关属性的输入框组,比panel_body()
函数要多一个下拉菜单的包装
panel_body()
return
只返回属性的输入框组
ChildLight(begin, theta, rgba, n, width, length, pole)
- 子光线类
- 成员变量
path
一个包含光线的路径的所有的点对象的数组,end
该子光线路径上的最后一个端点theta
初设角度n
所在空间的折射率length
没用用到过,看起来也是冗余的变量rgb
数组表示的颜色值Intensityset
路径中每条光线所对应的透明度alpha
endintersity
最后一个端点出射的光线透明度alpha
widthset
路径中每条光线所对应的宽度width
endwidth
最后一个端点出射的光线宽度width
pole
光线的偏振性
- 成员方法
draw(context)
context
主画布的CanvasRenderingContext2D
对象- 在画布上绘制当前子光线
addpath(loc)
- 在
this.path
中添加一个点
- 在
reflect(normal, loc, R)
normal
反射面的法向量角度loc
反射面上的入射点坐标R
反射率- 根据反射行为添加新的光线路径点、透明度与宽度
refract(normal, n2, loc)
normal
折射面的法向量角度n2
折射面另一侧的折射率loc
折射面上的入射点坐标- 根据折射行为添加新的光线路径点、透明度与宽度
Lens(n, index, type, xlist, ylist)
- 透镜类
- 成员变量
types
对象类型,lens
n
折射率index
当前对象在对应的set
中的下标color
字符串表示的颜色,如"rgba(0,255,255,0.5)"
xlist
透镜边界点的x
值组成的数组ylist
透镜边界点的y
值组成的数组typeOfLen
透镜的类型,0,1,2分别表示路径、弧形、矩形绘制的透镜anchor
一个点对象,是在移动操作中,鼠标在对象上点击的位置transferable
当前对象是否可平移rotatable
当前对象是否可旋转info
控制栏中当前对象的信息的HTMLElement
- 成员方法
buildfacet(xlist, ylist)
- 利用
xlist
与ylist
构建透镜
- 利用
buildfacet2(path)
- 利用
path
构建透镜
- 利用
buildcircle(xc, yc, r, begin, end, concave, thick, percise)
xc
,yc
圆心的坐标r
圆弧的半径begin
,end
起始角度与结束角度concave
凹凸性thick
厚度,只对凹面镜有效percise
精度,本质上是圆弧的线段个数(圆弧还是由有限数量的线段绘制而成)
draw(context)
context
主画布的CanvasRenderingContext2D
对象- 在画布上绘制当前元件
transfer(x, y)
x
,y
沿x
与y
方向平移的距离- 平移操作,平移当前元件
rotate(theta)
theta
旋转的角度,弧度制- 旋转操作,沿起始点旋转
theta
角度
checkcross(light)
light
一个ChildLight
对象return
- 一个对象,包括三个属性
cross
:light
与当前透镜的交点,normol
:入射面的法向量角度,n
:出射空间的折射率
- 一个对象,包括三个属性
show_info()
return
字符串格式的当前元件的信息
attributes_input()
return
返回控制栏中一个元件及其相关属性的输入框组,比panel_body()
函数要多一个下拉菜单的包装
panel_body()
return
只返回属性的输入框组- TODO: 矩形元件的输入框组还没有制作完毕
Mirros(loc1, loc2, R, index)
- 平面镜类
- 成员变量
types
对象类型,mirros
loc1
,loc2
平面镜的两个端点R
反射率index
当前对象在对应的set
中的下标color
字符串表示的颜色,如"blue"
anchor
一个点对象,是在移动操作中,鼠标在对象上点击的位置transferable
当前对象是否可平移rotatable
当前对象是否可旋转info
控制栏中当前对象的信息的HTMLElement
- 成员方法
draw(context)
context
主画布的CanvasRenderingContext2D
对象- 在画布上绘制当前元件
transfer(x, y)
x
,y
沿x
与y
方向平移的距离- 平移操作,平移当前元件
rotate(theta)
theta
旋转的角度,弧度制- 旋转操作,沿起始点旋转
theta
角度
checkcross(light)
light
一个ChildLight
对象return
- 一个对象,包括三个属性
cross
:light
与当前透镜的交点,normol
:入射面的法向量角度,n
:出射空间的折射率
- 一个对象,包括三个属性
show_info()
return
字符串格式的当前元件的信息
attributes_input()
return
返回控制栏中一个元件及其相关属性的输入框组,比panel_body()
函数要多一个下拉菜单的包装
panel_body()
return
只返回属性的输入框组