侯光敏 ([email protected])
2002 年 1 月
搞过java语言对vrml场景控制的人都知道,目前有两种方式对vrml场景进行控制,那就是EAI方式和SAI方式.EAI的全称是External Authoring Interface ,使用这种控制方式可以在applet中一目了然地输入各种参数来改变场景的内容,控制是非常灵活的.SAI方式,它不需要有applet的存在,只要为场景中的物体写相应的脚本代码即可,使用它可以实现许多VRML规范不能提供的复杂逻辑显示.两者可以说是各有所长,但是我在写一个控制VRML场景的程序时遇到了问题.
我在场景中放入了一组芯片模型,我想要达到的目的是,点击场景中的某芯片或某芯片的某管脚时,能在applet中显示芯片的内部结构图或者管脚的名称和电平状态,我必须要使用这些信息来改变相应的模型参数.但是单纯的SAI方式不能和applet通信,点击的结果不能被applet感知.而单纯的EAI方式也解决不了这个问题,因为要控制场景中的某个模型,必须先得到那个模型的名字.但在这种情况下模型的名称不可能被事先指定,它必须随着鼠标选择的变化而变化.要解决这个问题,必须在EAI方式控制和SAI控制之间建立起沟通信息的途径.
在查阅了VRML规范之后,我发现VRML规范中提供了一种供用户扩展的节点类型PROTO,由此我想出了解决这个问题的办法.其实这个办法很简单,就是用PROTO的各个自定义域来缓存需要通信的数据. 具体的做法是:在场景文件中定义一个名为Bridge的PROTO节点,为它增加一系列的域.在我这个具体问题中,我定义了SFString name,SFBool voltage两个域,还有SFSting型的pointA,pointB.然后为每个需要和applet通信的模型加入SAI方式的代码,单击事件(也可以是别的事件)触发一个过程,该过程把模型的名字写入Bridge的name域或pointA,pointB,把状态写入voltage域.
以下是VRML文件示例代码:
#the node that exchange data between SAI and EAI PROTO Bridge [ exposedField SFString pointA "### ^-- ^" exposedField SFString pointB "### ^ --^" exposedField SFString name "null" exposedField SFBool voltage FALSE ]{} // DEF se Bridge{} ....... DEF leg17 Transform { translation 35.5 16.7 -203 rotation 0.577 0.577 -0.577 -4.19 children [ DEF leg17_touch TouchSensor {} Shape { appearance USE legcolor geometry USE leg17-FACES } ] } ...... DEF leg17_S Script { //芯片第十七号管脚的节点 url "NodeScript.class" eventIn SFBool leg17 field SFNode node USE leg17 field SFNode select USE se } ..... ROUTE leg17_touch.isActive TO leg17_S.leg17 .....
以下是节点的脚本代码片断:
public class NodeScript extends Script { private SFNode theSelected; private SFString selectedA,selectedB; Browser br; public void initialize() { theSelected=(SFNode)getField("select"); br=getBrowser(); } public void processEvent(Event e) { ConstSFBool v=(ConstSFBool)e.getValue(); Node selected=(Node)theSelected.getValue(); //**获得交换节点的域 selectedA=(SFString)selected.getExposedField("pointA"); selectedB=(SFString)selected.getExposedField("pointB"); //*/ br.setDescription(e.getName()); //在浏览器底部显示节点名 //在此有改动,不使用开关值,改用队列先进先出写值 2001/11/30 if(v.getValue()) { selectedB.setValue(selectedA.getValue());//b-->a selectedA.setValue(e.getName()); // newValue-->b } } }
然后在EAI方式的代码中为applet加入一个线程,它无限循环的读Bridge节点的各个域值然后显示出来,完成了数据从SAI向EAI的传递,就这么简单.以下是示例代码:
public class displayPanel extends Panel implements Runnable { public static Label label1 = new Label(); public static Label label2 = new Label(); Browser br; ....... public void run() { while(true) //循环监听点击情况 { swich=VrmlObject.getSwichName(br); voltage=VrmlObject.getSwichVoltage(br); displayPanel.label1.setText(VrmlObject.getNodeA(br));//VrmlObject类封装了对节点的访问 displayPanel.label2.setText(VrmlObject.getNodeB(br)); } } }
使用这种方式,你可以扩充Bridge的域来缓存各种VRML规范所规定的数据类型.
关于作者本文地址:http://com.8s8s.com/it/it18336.htm