TWaver - 专注UI技术

http://twaver.servasoft.com/
posts - 171, comments - 191, trackbacks - 0, articles - 2
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

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, 300100);
14
15    public SimpleTest() {
16        this.setTitle("TWaver 3D");
17        this.setSize(900600);
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(100100);
27        element.setSize(20050);
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(5050);
 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(200100);
11        node2.setDeep(50);
12        node2.setFillColor(Color.green.darker());
13        network2d.getDataBox().addElement(node2);
14
15        Element3D node3 = new Element3D();
16        node3.setLocation(300200);
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, 30050);
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(1000700);
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(0000));
43        createNodeButton.addActionListener(new ActionListener() {
44
45            public void actionPerformed(ActionEvent e) {
46                Element3D element = new Element3D();
47                element.setLocation(100100);
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(100100);
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);

运行结果:

测试字符串
Test String

总结


 

通过这个简单的介绍,你一定对TWaver 3D机房充满了好奇。要想获得TWaver 3D机房产品包,可与我们联系。进一步深入的探索,会给你带来更多的惊喜。


评论

# re: 3D的致命诱惑——TWaver 3D机房初探(二次开发篇)  回复  更多评论   

2010-08-17 21:55 by 军品两
写了这么多年的swing程序竟然不知道有twaver这样的精品,顶楼主

# re: 3D的致命诱惑——TWaver 3D机房初探(二次开发篇)  回复  更多评论   

2010-08-18 16:11 by yho
随是自卖自夸但不得不顶

只有注册用户登录后才能发表评论。


网站导航: