TWaver3D机房不但可以单独作为工具使用,而且还提供了独立的组件,为开发者提供二次开发的能力。
开发环境
使用TWaver机房组件进行二次开发,需要以下开发环境:
- JDK 6+;
- Java IDE(Netbeans或Eclipse);
- TWaver Java 3.5+;
类结构
TWaver机房二次开发涉及到的主要类及其关系:
其中,各个类的主要用途如下:
类 |
用途 |
Network2D |
是TWaver Java中TNetwork的一个扩展。作为3D视图的2D呈现、编辑和数据驱动。 |
Floor2D |
和Network2D配合,用于绘制2D视图中的地板平面。 |
Network3D |
3D视图,以3D的方式呈现2D视图中的数据,并提供缩放、旋转等交互。Network3D必须依赖于Network2D而存在。 |
Floor3D |
和Network3D配合,用于在3D视图中绘制地板平面的3D视图。它依赖于Floor2D存在。 |
SittingPane |
一个封装好的3D机房属性编辑面板。包括3D视图中的各种参数设置,可以用于需要数据编辑的场合中。对于只需要3D数据呈现的情况,可以不使用该类。 |
PropertySheet3D |
一个封装好的3D数据属性表。当一个或多个3D数据被选中后,所有3D相关的属性会自动罗列在这个属性表中,方便查看和修改。该类可以用于需要数据编辑的场合中。对于只需要3D数据呈现的情况,可以不使用该类。 |
简单例子
下面的代码可以创建一个简单的3D机房视图。该程序只显示了一个3D视图,并显示了一个3D立方体物体。3D视图可以通过鼠标的滚轮、拖拽等方式进行缩放、平移等操作。
代码如下:
1import java.awt.Color;
2import javax.swing.JFrame;
3import javax.swing.SwingUtilities;
4import twaver.TDataBox;
5import twaver.TWaverConst;
6import twaver.TWaverUtil;
7
8public class SimpleTest extends JFrame {
9
10 private TDataBox box = new TDataBox();
11 private Network2D network2d = new Network2D(box);
12 private Floor2D roomFloor2D = new Floor2D(network2d);
13 private Network3D view3d = new Network3D(network2d, roomFloor2D, 300, 100);
14
15 public SimpleTest() {
16 this.setTitle("TWaver 3D");
17 this.setSize(900, 600);
18 TWaverUtil.centerWindow(this);
19 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
20 view3d.setScale(1.5);
21
22 this.add(view3d);
23
24 //add a 3d object
25 Element3D element = new Element3D();
26 element.setLocation(100, 100);
27 element.setSize(200, 50);
28 element.setDeep(100);
29 element.setFillColor(Color.orange.darker());
30 network2d.getDataBox().addElement(element);
31 }
32
33 public static void main(String[] args) {
34 SwingUtilities.invokeLater(new Runnable() {
35
36 public void run() {
37 TWaverUtil.setLocale(TWaverConst.EN_US);
38 SimpleTest test = new SimpleTest();
39 test.setVisible(true);
40 }
41 });
42 }
43
运行该代码,效果如下图:
在3D视图中显示Link
和TWaver Java中类似,要在3D视图中显示Link,直接在2D Network的DataBox中添加Link对象即可。以下代码在2D视图中创建了几个节点和连线:
1private void testLink() {
2 Element3D node1 = new Element3D();
3 node1.setLocation(50, 50);
4 node1.getAlarmState().addNewAlarm(AlarmSeverity.CRITICAL);
5 node1.setDeep(50);
6 node1.setFillColor(Color.green.darker());
7 network2d.getDataBox().addElement(node1);
8
9 Element3D node2 = new Element3D();
10 node2.setLocation(200, 100);
11 node2.setDeep(50);
12 node2.setFillColor(Color.green.darker());
13 network2d.getDataBox().addElement(node2);
14
15 Element3D node3 = new Element3D();
16 node3.setLocation(300, 200);
17 node3.setDeep(50);
18 node3.setFillColor(Color.green.darker());
19 network2d.getDataBox().addElement(node3);
20
21 Link link = new Link(node1, node2);
22 link.putLinkColor(Color.cyan.darker());
23 link.putLinkWidth(8);
24 link.setLinkType(TWaverConst.LINK_TYPE_ORTHOGONAL);
25 link.putLinkOutlineColor(Color.red);
26 link.putLinkOutlineWidth(1);
27 link.putLinkOrthogonalDirection(OrthogonalLinkDirectionType.Y_TO_X);
28 network2d.getDataBox().addElement(link);
29
30 link = new Link(node2, node3);
31 link.putLinkColor(Color.green.darker());
32 link.putLinkWidth(8);
33 link.setLinkType(TWaverConst.LINK_TYPE_FLEXIONAL);
34 link.putLinkOutlineColor(Color.red);
35 link.putLinkOutlineWidth(2);
36 network2d.getDataBox().addElement(link);
37 }
在2D和3D视图中会同时显示Link,显示效果如下:
3D视图会将Link绘制在地板上,并保持和2D的颜色、边框、线宽、选择状态等属性一致。以下是将3D视图进行各角度旋转后Link的显示效果:
创建3D视图右键菜单
在TWaver 2D的Network组件中已经提供了右键菜单支持。TWaver通过一个菜单生成器来动态生成Network的右键菜单。下面代码展示了这种用法:
1network2d.setPopupMenuGenerator(new PopupMenuGenerator() {
2 public JPopupMenu generate(TView tview, MouseEvent me) {
3 JPopupMenu menu = new JPopupMenu();
4 …
5 return menu;
6 }
7 }
8 });
同样,在3D视图中,TWaver也提供了类似机制,并使用了相同的方法名称和接口。我们可以new一个PopupMenuGenerator实例然后同时应用在2D和3D视图中,如下面代码:
1PopupMenuGenerator generator= new PopupMenuGenerator() {
2 public JPopupMenu generate(TView tview, MouseEvent me) {
3 JPopupMenu menu = new JPopupMenu();
4 JMenuItem item = new JMenuItem("Create Object Icon");
5 menu.add(item);
6 return menu;
7 }
8 }
9 });
10 network2d. setPopupMenuGenerator(generator);
11 network3d. setPopupMenuGenerator(generator);
运行以上代码可以看到,在2D和3D视图上右键点击同一个节点,可以看到同样的右键菜单:
创建简单的编辑器
上述例子显示了一个非常简单的、只读的3D视图。本节将介绍如何创建一个简单的编辑器程序,可以对数据进行动态的添加、编辑等操作。其实在上述例子中已经创建了Network2D视图,只是没有显示出来而已。下面的例子将Network2D视图显示出来,并增加了SettingPane参数编辑视图、PropertySheet3D属性表等组件,共同构成了简单的编辑器程序。
代码如下:
1import java.awt.Color;
2import java.awt.Insets;
3import java.awt.event.ActionEvent;
4import java.awt.event.ActionListener;
5import javax.swing.JButton;
6import javax.swing.JComponent;
7import javax.swing.JFrame;
8import javax.swing.JSplitPane;
9import javax.swing.SwingUtilities;
10import twaver.TDataBox;
11import twaver.TWaverConst;
12import twaver.TWaverUtil;
13import twaver.table.TPropertySheetPane;
14
15public class Test extends JFrame {
16
17 private TDataBox box = new TDataBox();
18 private Network2D network2d = new Network2D(box);
19 private Floor2D roomFloor2D = new Floor2D(network2d);
20 private Network3D view3d = new Network3D(network2d, roomFloor2D, 300, 50);
21 private SettingPane settingPane = new SettingPane(roomFloor2D, view3d, 2);
22 private PropertySheet3D sheet = new PropertySheet3D(box);
23 private TPropertySheetPane sheetPane = new TPropertySheetPane(sheet);
24
25 public Test() {
26 init();
27 }
28
29 private void init() {
30 this.setTitle("TWaver 3D Room");
31 this.setSize(1000, 700);
32 TWaverUtil.centerWindow(this);
33 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
34
35 JSplitPane split1 = createSplit(sheetPane, network2d, JSplitPane.HORIZONTAL_SPLIT, 300);
36 JSplitPane split2 = createSplit(settingPane, view3d, JSplitPane.HORIZONTAL_SPLIT, 350);
37 JSplitPane split = createSplit(split1, split2, JSplitPane.VERTICAL_SPLIT, 350);
38
39 this.add(split);
40
41 JButton createNodeButton = new JButton("New");
42 createNodeButton.setMargin(new Insets(0, 0, 0, 0));
43 createNodeButton.addActionListener(new ActionListener() {
44
45 public void actionPerformed(ActionEvent e) {
46 Element3D element = new Element3D();
47 element.setLocation(100, 100);
48 element.setDeep(50);
49 element.setFillColor(Color.green.darker());
50 network2d.getDataBox().addElement(element);
51 }
52 });
53 network2d.getToolbar().add(createNodeButton);
54 }
55
56 private JSplitPane createSplit(JComponent one, JComponent two, int direction, int dividerLocation) {
57 JSplitPane split = new JSplitPane(direction);
58
59 split.setLeftComponent(one);
60 split.setTopComponent(one);
61
62 split.setRightComponent(two);
63 split.setBottomComponent(two);
64
65 split.setContinuousLayout(true);
66 split.setDividerLocation(dividerLocation);
67
68 return split;
69 }
70
71 public static void main(String[] args) {
72 SwingUtilities.invokeLater(new Runnable() {
73
74 public void run() {
75 TWaverUtil.setLocale(TWaverConst.EN_US);
76 Test test = new Test();
77 test.setVisible(true);
78 }
79 });
80 }
81}
上述代码在Network2D的工具条上添加了一个按钮,点击按钮后,创建一个Element3D对象,并将其设置在面板上。程序运行效果如下图:
点击“new”按钮创建一个3D物体,并在左侧属性表中设置其上、左、右三面的图片贴图。效果如下:
保存至文件
要将编辑器中的数据保存至文件,可以在工具条上添加一个按钮,点击后将DataBox中的数据输出到XML字符串,然后将字符串保存至文件。
1String xml = network3d.toXML(true);
2JFileChooser chooser = new JFileChooser();
3chooser.setFileFilter(filter);
4int answer = chooser.showSaveDialog(EditorDemo.this);
5if (answer == JFileChooser.APPROVE_OPTION) {
6 String fileName = chooser.getSelectedFile().getAbsolutePath();
7 if (fileName != null) {
8 if (!fileName.toUpperCase().endsWith(".XML")) {
9 fileName += ".xml";
10 }
11 OutputStream out = new FileOutputStream(fileName);
12 out.write(xml.getBytes());
13 out.close();
14 }
15 }
从文件打开
对于保存的数据,可以写一个按钮,选择对应的文件,通过DataBox的parse方法进行解析,打开数据文件。
1JFileChooser chooser = new JFileChooser();
2chooser.setFileFilter(filter);
3int answer = chooser.showOpenDialog(EditorDemo.this);
4if (answer == JFileChooser.APPROVE_OPTION) {
5 File file = chooser.getSelectedFile();
6 if (file.exists()) {
7 network2d.getDataBox().clear();
8 network3d.clearObject3D();
9 InputStream input = new FileInputStream(file);
10 network2d.getDataBox().parse(input);
11 }
12}
Element3D主要属性
通过以上例子可以发现,创建一个3D视图非常简单。在3D视图中创建一个3D数据的主要代码只有如下几行:
1Element3D element = new Element3D();
2element.setLocation(100, 100);
3element.setDeep(50);
4element.setFillColor(Color.green.darker());
5network2d.getDataBox().addElement(element);
Element3D实际上是一个普通的TWaver Element对象,它从TWaver预定义的Element——ResizableNode继承而来,并封装了3D物体的基本属性,例如左侧图片、右侧图片、顶部图片等等。通过设置和修改这些属性,即可实现对3D物体的外观定制。
Element3D类的主要属性罗列如下。注意:所有setLeft***均作用域左侧里面,而右侧立面均有对应的setRight***方法,其功能是对右侧立面进行相应的功能设置。
- setFillColor:设置填充颜色;
- setDeep:设置3D物体的厚度(也就是三维中的高度);
- setPaintLeft:是否左侧立面可见。当设置false,整个左侧立面将消失;
- setLeftImageUrl:设置左侧立面的贴图。和TWaver Java中的惯例一样,给定图片的URL字符串即可。注意:默认的贴图方法,是将图片拉伸并铺满整个左侧立面。如需要边距或自定义边界,可以使用setLeftImageBounds方法进行设置;
- setLeftImageBounds:设置左侧立面的固定边界。该函数可以强制指定左侧立面图片的起始坐标位置和图片的宽高;
- setLeftImageTexture:指定左侧立面贴图是否使用纹理模式。如果设置true,则贴图会被作为纹理进行全立面贴图。注意:一旦启用纹理,ImageBounds参数将不再起作用;
- setLeftImageTextureScale:设置左侧立面纹理贴图的放大比例。默认值为3。当该值越大,纹理的单位贴图尺寸也更大;
- isPaintTop:是否顶面可见。当设置false,物体的顶面将会消失;
- setTopImageUrl:物体的顶面贴图。注意:一旦设置顶面贴图,则Network2D中物体的图片将使用该顶面贴图进行显示。
Network3D主要属性
Network3D类是用来呈现3D场景的组件。注意它并非从TWaver Java的TNetwork或上面提到的TNetwork2D继承而来,而是直接从Swing的JPanel扩展而来。Network3D必须依赖于Network2D而存在,从而从Network2D获得数据。此外,Network3D还必须设置一个Floor2D的类,用来绘制3D的场景地平面。Network3D还提供了构造函数可以直接设置其3D坐标原点的屏幕坐标位置。
Network3D的构造函数:
1//给定Network2D和Floor2D进行构造
2public Network3D(Network2D network2d, Floor2D floor2d)
3//给定Network2D和Floor2D进行构造,同时指定3D场景的坐标原点的位置。
4public Network3D(Network2D network2d, Floor2D floor2d, int originX, int originY)
Network3D的主要函数和用法如下:
- getNetwork2D:获得与Network3D绑定的Network2D对象。
- setScale:设置3D场景的放大系数。设置1为原始比例。也可以通过调用zoomIn或zoomOut函数进行单次的缩放。
- zoomIn/zoomOut:放大或缩小。每次缩放会用当前缩放因数乘以(放大)或除以(缩小)scale step数值(可以通过getScaleStep函数获得,其默认值是1.2)。
- setScaleStep:设置单次缩放变化因数。
- setOrigin:设置3D坐标原点的x和y数值。
- setHorizontalAngle:设置3D场景水平旋转角度。必须为0-90度。
- setVerticalAngle:设置3D场景垂直旋转角度。必须为0-90度。
- getObjectAt:获得给定坐标的3D物体。如果多个物体存在该点,则返回所有物体。
- toXML:将3D场景所有数据输出到XML中。
告警呈现
在TWaver Java中,Network以及其他各组件都有完整的告警呈现能力,例如告警渲染、告警冒泡等。在TWaver 3D机房中,我们也同样也提供了该功能。
告警冒泡
当一个物体在发生新发告警后,在2D的Network组件一样,3D视图也会绘制完全相同的告警冒泡。告警冒泡的位置在物体的顶面中央位置(如下图)。同时,3D物体的三个面的图片也会被动态的渲染为告警颜色:
3D物体颜色自定义
TWaver Java的Network本身具有自定义数据颜色的接口。通过一个插入的Generator接口,开发者可以拦截所有数据并返回任意颜色用于绘制数据。例如,一个node的fillColor为绿色,则默认会使用绿色来填充该node;但通过Generator拦截后,可以返回红色;此时,TWaver Network会使用红色来填充物体,而不再是绿色。
同样,TWaver 3D视图也提供了同样的能力。而且,3D视图直接使用了2D视图的该Generator,以便3D和2D保持同样的外观特性。也就是说,我们不需要在3D视图上设置Generator,而是只在3D视图对应的2D视图上设置Generator即可让2D、3D视图同时生效。以下例子强行将2D、3D视图中所有物体绘制颜色设置为MAGENTA颜色:
1network2d.setElementBodyColorGenerator(new Generator() {
2public Object generate(Object o) {
3 return Color.MAGENTA;
4}
5});
数据的输入与输出
默认输出方法
在3D视图程序中,3D机房数据使用和TWaver Java提供的同样的对象XML序列化方法进行数据的序列化和反序列化,从而实现数据的输入与输出。通过TDataBox.output()方法和TDataBox.parse()方法完成数据的导出和输入。不过,为了方便开发者使用,Network3D类提供了toXML函数可以直接输出XML字符串:
1String xml = this.network3d.toXML(true);
其中的boolean值是用来控制是否对各立面贴图进行BASE64编码输出。
自定义格式输出
如果要进行进一步细致的输出参数控制,也可以不适用toXML函数,而直接使用TDataBox的output方法进行。以下代码显示了如何将内存中的3D数据导出到XML格式的字符串中并进行输出,同时对输出的各种参数进行控制,例如是否输出ID、是否输出告警信息等:
1ByteArrayOutputStream out = new ByteArrayOutputStream();
2setting.setWithElementId(false);
3setting.setWithUserProperty(true);
4setting.setOutputStream(out);
5box.output(setting);
6String xml = out.toString("utf8");
以上代码可以对数据进行XML序列化输出。
为何使用BASE64图片编码输出
3D物体中的各个立面贴图将以URL字符串的方式输出到XML中。当XML被传输到其他机器或应用环境中,如果图片的URL资源位置发生变化,则无法读到这些图片。解决这一问题的一个方法是:在数据输出过程中,使用BASE64编码对所有图片资源进行编码并序列化输出。这样,XML中就包含了所有的图片资源,无论XML被传输到哪里,都可以无外部依赖的打开。
使用Network3D的toXML函数可以直接进行图片的BASE64编码输出。实际上,该函数也是使用了TDataBox的输出参数控制机制,对用到的图片进行遍历和编码设置。以下代码展示了其具体实现方法:
1//use base64 image encoding output setting.
2DataBoxOutputSetting setting = new DataBoxOutputSetting();
3Map images = new HashMap();
4Iterator it = this.view2d.getDataBox().iterator();
5while (it.hasNext()) {
6 Element element = (Element) it.next();
7 if (element instanceof RoomNode) {
8 RoomNode roomNode = (RoomNode) element;
9 this.addImage(images, roomNode.getLeftImageUrl());
10 this.addImage(images, roomNode.getRightImageUrl());
11 this.addImage(images, roomNode.getTopImageUrl());
12 }
13}
14setting.setImages(images);
经过以上代码的预处理之后,再使用这个setting对象对box进行序列化,即可实现BASE64的图片编码存储。
3D物体图片导出
TWaver 3D视图提供了对单个3D物体进行图片导出的功能。通过下面的方法可以直接将指定的3D物体绘制在一个Image图片上:
1public Image createObjectImage(Object objectID)
以上代码会将3D视图中指定的3D物体绘制在一个单独的Image对象中返回。该物体在图片上的大小、角度、材质等,均与3D视图保持一致;Image图片的大小也会自动裁剪到物体边缘位置,保持图片尺寸最小。
请注意该方法并不需要提供导出图片的大小尺寸。要使用更大或更小的尺寸,或使用不同的旋转角度,可以先通过API或鼠标调整3D视图的缩放系数、旋转角度,再使用该方法进行图片导出。
通过这一功能,可以将一些3D物体导出为小图片或Icon,为其他软件使用。为了演示这一功能,以下代码通过右键菜单来为某个3D物体创建导出图片,并显示在一个弹出对话框中:
1PopupMenuGenerator generator=new PopupMenuGenerator() {
2
3 public JPopupMenu generate(TView tview, MouseEvent me) {
4 TDataBox box = tview.getDataBox();
5 if (box.getSelectionModel().size() == 1) {
6 Element element = box.getSelectionModel().lastElement();
7 final Object id = element.getID();
8
9 JPopupMenu menu = new JPopupMenu();
10 JMenuItem item = new JMenuItem("Create Object Icon");
11 item.addActionListener(new ActionListener() {
12
13 public void actionPerformed(ActionEvent e) {
14 Image image = network3d.createObjectImage(id);
15 if (image != null) {
16 JDialog dialog = new JDialog(EditorDemo.this);
17 dialog.setModal(true);
18 dialog.add(new JLabel(new ImageIcon(image)));
19 dialog.pack();
20 dialog.setTitle("Object Icon");
21 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
22 TWaverUtil.centerWindow(dialog);
23 dialog.setVisible(true);
24 }
25 }
26 });
27 menu.add(item);
28 return menu;
29 } else {
30 return null;
31 }
32 }
33 };
34 network3d.setPopupMenuGenerator(generator);
运行以上代码:
也可以用ImageIO等机制进一步改造以上代码,把图片保存到文件中或数据库中。
I18N国际化
切换语言
TWaver Java本身已经提供了完整的国际化功能。通过Java的I18N机制以及TWaver Java包内置的多语言资源文件,可以动态的支持不同语言。TWaver 3D直接使用并扩展了TWaver Java的国际化机制,对TWaver 3D中的组件、对象属性名称等内容进行了国际化。
要在TWaver 3D机房中切换国际化,在程序的启动初期,直接调用TWaver Java提供的API进行设置即可:
1//set Chinese language for TWaver 3D
2TWaverUtil.setLocale(TWaverConst.ZH_CN);
3//set English language for TWaver 3D
4TWaverUtil.setLocale(TWaverConst.EN_US);
TWaver Java本身提供了中文、英文、西班牙语、日语等多种语言。而TWaver 3D目前仅支持英文、中文两种语言。
扩展资源字符串
TWaver 3D中所有的字符串都通过I18N机制存放在以下资源文件中:
- \twaver\network\d3\d3_en_US.properties(英文资源文件)
- \twaver\network\d3\d3_zh_CN.properties(中文资源文件)
以上资源文件都被直接打包在twaver3d.jar运行文件中。以下是资源文件中的几个例子:
d3_zh_CN.properties |
d3_en_US.properties |
image_texture=纹理图片pick_color=选择颜色room_name=机房名称:
floor_grid_span_count=地板各单位数量:
network_grid_unit_size=最小单元格大小:
|
image_texture=Image Texturepick_color=Pick Colorroom_name=Room Name:
floor_grid_span_count=Floor Grid Span Count:
network_grid_unit_size=Network Grid Unit Size:
|
其中,等号左侧是资源键值,右侧是对应的语言翻译。我们可以直接对翻译进行修改,并更新到包中,来修改TWaver 3D的默认翻译。另外,还可以在资源文件中增加自定义的资源,并在程序中使用。
例如,增加一个新的国际化字符串:
d3_zh_CN.properties |
d3_en_US.properties |
test_key =测试字符串 |
test_key =Test String |
然后,通过twaver.network.d3.Util.getString(String key)方法来获取字符串资源:
1TWaverUtil.setLocale(TWaverConst.ZH_CN);
2String resource = Util.getString(“test_key”);
3System.out.println(resource);
4TWaverUtil.setLocale(TWaverConst.EN_US);
5String resource = Util.getString(“test_key”);
6System.out.println(resource);
运行结果:
总结
通过这个简单的介绍,你一定对TWaver 3D机房充满了好奇。要想获得TWaver 3D机房产品包,可与我们联系。进一步深入的探索,会给你带来更多的惊喜。