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視圖可以通過鼠標的滾輪、拖拽等方式進行縮放、平移等操作。
代碼如下:
1
import java.awt.Color;
2
import javax.swing.JFrame;
3
import javax.swing.SwingUtilities;
4
import twaver.TDataBox;
5
import twaver.TWaverConst;
6
import twaver.TWaverUtil;
7
8
public 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視圖中創建了幾個節點和連線:
1
private 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的右鍵菜單。下面代碼展示了這種用法:
1
network2d.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視圖中,如下面代碼:
1
PopupMenuGenerator 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屬性表等組件,共同構成了簡單的編輯器程序。
代碼如下:
1
import java.awt.Color;
2
import java.awt.Insets;
3
import java.awt.event.ActionEvent;
4
import java.awt.event.ActionListener;
5
import javax.swing.JButton;
6
import javax.swing.JComponent;
7
import javax.swing.JFrame;
8
import javax.swing.JSplitPane;
9
import javax.swing.SwingUtilities;
10
import twaver.TDataBox;
11
import twaver.TWaverConst;
12
import twaver.TWaverUtil;
13
import twaver.table.TPropertySheetPane;
14
15
public 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字符串,然后將字符串保存至文件。
1
String xml = network3d.toXML(true);
2
JFileChooser chooser = new JFileChooser();
3
chooser.setFileFilter(filter);
4
int answer = chooser.showSaveDialog(EditorDemo.this);
5
if (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方法進行解析,打開數據文件。
1
JFileChooser chooser = new JFileChooser();
2
chooser.setFileFilter(filter);
3
int answer = chooser.showOpenDialog(EditorDemo.this);
4
if (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數據的主要代碼只有如下幾行:
1
Element3D element = new Element3D();
2
element.setLocation(100, 100);
3
element.setDeep(50);
4
element.setFillColor(Color.green.darker());
5
network2d.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進行構造
2
public Network3D(Network2D network2d, Floor2D floor2d)
3
//給定Network2D和Floor2D進行構造,同時指定3D場景的坐標原點的位置。
4
public 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顏色:
1
network2d.setElementBodyColorGenerator(new Generator()
{
2
public Object generate(Object o)
{
3
return Color.MAGENTA;
4
}
5
});

數據的輸入與輸出
默認輸出方法
在3D視圖程序中,3D機房數據使用和TWaver Java提供的同樣的對象XML序列化方法進行數據的序列化和反序列化,從而實現數據的輸入與輸出。通過TDataBox.output()方法和TDataBox.parse()方法完成數據的導出和輸入。不過,為了方便開發者使用,Network3D類提供了toXML函數可以直接輸出XML字符串:
1
String xml = this.network3d.toXML(true);
其中的boolean值是用來控制是否對各立面貼圖進行BASE64編碼輸出。
自定義格式輸出
如果要進行進一步細致的輸出參數控制,也可以不適用toXML函數,而直接使用TDataBox的output方法進行。以下代碼顯示了如何將內存中的3D數據導出到XML格式的字符串中并進行輸出,同時對輸出的各種參數進行控制,例如是否輸出ID、是否輸出告警信息等:
1
ByteArrayOutputStream out = new ByteArrayOutputStream();
2
setting.setWithElementId(false);
3
setting.setWithUserProperty(true);
4
setting.setOutputStream(out);
5
box.output(setting);
6
String 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.
2
DataBoxOutputSetting setting = new DataBoxOutputSetting();
3
Map images = new HashMap();
4
Iterator it = this.view2d.getDataBox().iterator();
5
while (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
}
14
setting.setImages(images);
經過以上代碼的預處理之后,再使用這個setting對象對box進行序列化,即可實現BASE64的圖片編碼存儲。
3D物體圖片導出
TWaver 3D視圖提供了對單個3D物體進行圖片導出的功能。通過下面的方法可以直接將指定的3D物體繪制在一個Image圖片上:
1
public Image createObjectImage(Object objectID)
以上代碼會將3D視圖中指定的3D物體繪制在一個單獨的Image對象中返回。該物體在圖片上的大小、角度、材質等,均與3D視圖保持一致;Image圖片的大小也會自動裁剪到物體邊緣位置,保持圖片尺寸最小。
請注意該方法并不需要提供導出圖片的大小尺寸。要使用更大或更小的尺寸,或使用不同的旋轉角度,可以先通過API或鼠標調整3D視圖的縮放系數、旋轉角度,再使用該方法進行圖片導出。
通過這一功能,可以將一些3D物體導出為小圖片或Icon,為其他軟件使用。為了演示這一功能,以下代碼通過右鍵菜單來為某個3D物體創建導出圖片,并顯示在一個彈出對話框中:
1
PopupMenuGenerator 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
2
TWaverUtil.setLocale(TWaverConst.ZH_CN);
3
//set English language for TWaver 3D
4
TWaverUtil.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)方法來獲取字符串資源:
1
TWaverUtil.setLocale(TWaverConst.ZH_CN);
2
String resource = Util.getString(“test_key”);
3
System.out.println(resource);
4
TWaverUtil.setLocale(TWaverConst.EN_US);
5
String resource = Util.getString(“test_key”);
6
System.out.println(resource);
運行結果:
總結
通過這個簡單的介紹,你一定對TWaver 3D機房充滿了好奇。要想獲得TWaver 3D機房產品包,可與我們聯系。進一步深入的探索,會給你帶來更多的驚喜。