原理:splishsplash中的MVC模式与GenericParameter
splishsplash中采用了MVC设计模式。该设计模式最大的特点就是将用户界面与后端数据计算分开,也就是俗称的“前端”和“后端”分开。实际上,只要是涉及到GUI或者图形学的程序,都几乎或多或少要有MVC设计模式。
MVC中的三个字母代表的是Model View Controller
Model负责数据的计算,View负责用户IO(也就是显示界面和接收用户输入),Controller负责两者的通讯。通过Controller,我们彻底截断了后端和前端。实现了系统的解耦。
splishsplash中的Model不必多说,就是计算的主体,可以认为是FluidModel等实例的计算数据。
View代表的则是GUI,目前默认采用的是IMGUI。
而Controller,则是GenericParamerter 这个第三方库。这个库是splishsplash项目组自己开发的另一个项目。在splishsplash中作为第三方库来使用。它的用处就是管理参数。只要你想要将Model中的参数与用户界面的参数相互通讯,就可以将GenericParameter作为你当前类的父类。GenericParameter就会负责和IMGUI相互沟通。那么,对我们代码开发者来说,只需要会用GenericParameter的API就可以实现与GUI之间的通讯了。
你可能会问为何要中间加一个中介,而不是直接去联通IMGUI呢?原因是1)GUI库有很多种选择,IMGUI,TweakBar(曾经的默认GUI)等等。假如我变更了GUI,那岂不是算法中的所有代码都要变?所以为了统一API,增加一个中介是有好处的。2)假如没有中介的Controller,两者沟通的代码,要么写在GUI的源文件里,要么写在算法的源文件里,两者都会造成很令人头疼的耦合问题。3)简化了使用。IMGUI有很多功能,但我们这里只需要管理参数就够了,多余的对我们来说反而是负担。
因此,下面我们只关注这个中介Controller(也就是GenericParameters)的API。
使用方式讲解
我们看一个例子,代码来自SurfaceTensionBase.cpp
SURFACE_TENSION = createNumericParameter("surfaceTension", "Surface tension coefficient", &m_surfaceTension);
setGroup(SURFACE_TENSION, "Surface tension");
setDescription(SURFACE_TENSION, "Coefficient for the surface tension computation");
RealParameter* rparam = static_cast<RealParameter*>(getParameter(SURFACE_TENSION));
rparam->setMinValue(0.0);
该代码的作用是:绑定参数m_surfaceTension到GUI上去,GUI中显示的参数叫做surfaceTension。
我们一行行分析:
第一行
SURFACE_TENSION = createNumericParameter("surfaceTension", "Surface tension coefficient", &m_surfaceTension);
createNumericParameter这个函数就是创建一个数值型的参数。这个参数的名字叫做surfaceTension(这个名字同样适用于json文件)。描述为Surface tension coefficient,所绑定的参数为m_surfaceTension(注意传递的是指针)。至于返回的那个整数型变量,是GUI的组号。必然是一个static的(因为要求静态生存期)
第二行
setGroup(SURFACE_TENSION, "Surface tension");
为了方便管理,参数通常要分组。这在GUI上面的效果就是分页。每个组就是单独的一页来显示。
第三行
setDescription(SURFACE_TENSION, "Coefficient for the surface tension computation");
鼠标悬浮的时候显示的描述。
第四行
RealParameter* rparam = static_cast<RealParameter*>(getParameter(SURFACE_TENSION));
用来设定最大最小值的。这是为了防止用户错误输入。
第五行
rparam->setMinValue(0.0);
设定最小值
我们来修改一下试验效果
刚才讲完了原理和用法,那么我们就尝试改动一些东西,看看是不是理论符合实践。
测试改名字和描述和初始值
第一行改为
SURFACE_TENSION = createNumericParameter("thisIsName", "This is Discription", &m_surfaceTension);
编译运行
我们发现确实如此
那么是否能通过json改变它的参数呢?目前默认是0.05
我们在一个json中更改
"Materials": [
{
"id": "Fluid",
"thisIsName": 1.11
}
],
运行
发现确实读入了参数
测试分组
第二行更改为
setGroup(SURFACE_TENSION, "A new group");
编译运行
当你选择任何SurfaceTension算法的时候就会出现新分组
并且我们之前的那个参数也被挪到新分页内了。
请注意:决定你的参数是否在新分页的,是你的参数所在的组(也就是组号SURFACE_TENSION所决定的)
例如原有的Boundary surface tension coeff就没有分到新租“A new group”
测试组描述
与之前那个不同,这个是针对组内每个变量的描述
setDescription(SURFACE_TENSION, "This is the group discription");
测试最大最小值
第五行更改为
rparam->setMinValue(1.0);
rparam->setMaxValue(5.0);
假如我们键入0.9,会自动弹回1.0
最大值同理