2012/12/27

Bundled Links in Java

Link-bundling is the way to solve the problems arising from links in TWaver: when there are several links between two nodes, we can double-click one of them to bundle them and represent them with only a link-agent, hence the simplification visually.
There will appear the number of the links once they are bundled together. However, in so much link- bundles, how can we know that a particular number is of which link-bundle?

In fact, if you make some adjustment, you will see clearly how many links every link-bundle has on the network. For example, the sentence link.putLinkLabelRotatable(true); is used to rotate the label to be paralleled to the link-agent and the label of links can also be set to be placed in the middle. What’s more, the content to be shown in the label can also be edited by labelgenerator. For instance:

Before:
After:

In this way, the label is directly shown on the link to clearly divide up the links in the image above. The code is quite simple:
network.setElementLabelGenerator(new Generator(){
   public Object generate(Object object) {
    if (object instanceof Link) {
     Link link = (Link)object;
     if(link.isLinkBundleExpand()){
      return link.getName();
     }else if(link.isBundleAgent()){
      return "spring will come ("+link.getLinkBundleSize()+")"//
     }else{
      return null;
     }
    } else {
     return ((Element) object).getName();
    }
   }
  });
The functions on link-bundle provided in TWaver Java are listed as below:
Link:
isLinkBundleExpand() //to judge whether the links are bundled or not

isBundleAgent() // to judge whether a link is the agent-link or not

getLinkBundleSize() // to obtain the number of the links bundled together

getLinkBundleIndex // to obtain the index of a link in a link-bundle

setBundleExpand(boolean bundleExpand) //to set whether a link-bundle is expanded or not

putLinkBundleExpand  //to set whether the links are expanded or not

putLinkBundleIndex  //to set the index of a link in a link-bundle

putLinkBundleSize  //to set the number of the links bundled together
The functions related to bundle are also provided in DataBox:

setLinkBundleFilter(VisibleFilter linkBundleFilter) //to set the filter which filters out the links to be bundled: to specify which links are to be bundled and which are not

setLinkBundleAgentGenerator //This method is used to set the generator of link-agent in a link-bundle to change the default that a random link is specified as the agent-link in TWaver.

getBundledLinks(Node node1, Node node2) //to obtain the bundled links between two nodes


2012/12/13

To Add Icons at Any Place on Elements in TWaver Flex

With the help of iconAttachment system provided in TWaver Java, we can be able to put any number of images, characters and figures on an element as you want. For example:

The following codes can also be called to show the icons on an element in TWaver Flex.


server1.setStyle(Styles.ICONS_NAMES, ["att5","att6", "att7","att8"]);
server1.setStyle(Styles.ICONS_POSITION, Consts.POSITION_BOTTOMRIGHT_TOPRIGHT);
server1.setStyle(Styles.ICONS_ORIENTATION, Consts.ORIENTATION_TOP);
server1.setStyle(Styles.ICONS_XOFFSET, 5);

The function to add icons at different positions on an element is not provided in TWaver Flex as default, but it can be realized through function-expansion, since the FlexMVC model is so flexible.
Several iconAttachments can be created to show icons at different places with the Attachment component in TWaver.

If you are interested in it, you can try to show attachments at different places and directions, mainly by changing the following two methods in CustomIconAttachment:
private function getIconsSize(names:Array, orientation:String, xgap:Number, ygap:Number):Size
and
override public function draw(graphics:Graphics):void

2012/12/02

Tree of files in JTree

In fact, what this article will say has nothing to do with the components in TWaver just because I find it quite interesting. Therefore, I would like to share it with you. The file tree is completely based on JTree of swing. Now let's first see the final effect:

Screenshot:



Introduction on Functions:
  • to show the structure of files in tree
  • the icon of files should be systematic ones
  • The background color of the current node will be changed when you mouse over it.(such as the bricky-red background of the text "Windows" in the image above)
First let' s see the class structure:
  • main program
  • file tree, which inherits from JTree
  • the encapsulated node, including the name and the icon of a file, the File class and other identity
  • The customized node renderer which inherits from DefaultTreeCellRenderer
  • the customized TreeModel which inherits from DefaultTreeModel
Considering that there will be quite a few system files, it is not reasonable to initialize the whole tree at starting a program. So we have taken measures to delay loading: to only initialize the child nodes of a node only when it is to be expanded. Now add a listener into the construction of FileTree:
addTreeWillExpandListener(new TreeWillExpandListener() {
            @Override
            public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
                DefaultMutableTreeNode lastTreeNode =
(DefaultMutableTreeNode) event.getPath().getLastPathComponent();
                FileNode fileNode = (FileNode) lastTreeNode.getUserObject();
                if (!fileNode.isInit) {
                    File[] files;
                    if (fileNode.isDummyRoot) {
                        files = fileSystemView.getRoots();
                    } else {
                        files = fileSystemView.getFiles(
                                ((FileNode) lastTreeNode.getUserObject()).file,
                                false);
                    }
                    for (int i = 0; i < files.length; i++) {
                       //The name and the icon of the file is obtained through fileSystemView.
                        FileNode childFileNode = new FileNode(
                                fileSystemView.getSystemDisplayName(files[i]),
                                fileSystemView.getSystemIcon(files[i]), files[i],
                                false);
                        DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(childFileNode);
                        lastTreeNode.add(childTreeNode);
                    }
                    //To notify that a node has been changed
                    DefaultTreeModel treeModel1 = (DefaultTreeModel) getModel();
                    treeModel1.nodeStructureChanged(lastTreeNode);
                }
                //to change the identifier to avoid loading repeatedly
                fileNode.isInit = true;
            }
            @Override
            public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {

            }
        });
Of course, this method must be combined with TableModel. I have reloaded DefaultTreeModel and initialized the root node in the construction. Then load the method isLeaf.

Now let's think how to change the background color of a node when you mouse over it.

Right at this time I recalled the mistake I made during the time when I first learned Renderer that every node has a Renderer and I even tend to add a listener to it! The thing I have to emphasize is that Renderer is just a renderer. JTree will call it to render nodes onto the screen to show them. But remember however nodes there are, there is only one Renderer in a JTree!

Since it is useless to add a listener to Renderer, we have to change our focus, that is, to add listener which listens to mouse-moves in JTree and then repaint the node on which the mouse is.

addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
//to obtain the TreePath of the mouse
                TreePath path=getPathForLocation(e.getX(), e.getY());

//to calculate the area to be reprinted and to repaint JTree
                if(path!=null){
                    if(mouseInPath!=null){
                        Rectangle oldRect=getPathBounds(mouseInPath);
                        mouseInPath=path;
                        repaint(getPathBounds(path).union(oldRect));
                    }else{
                        mouseInPath=path;
                        Rectangle bounds=getPathBounds(mouseInPath);
                        repaint(bounds);
                    }
                }else if(mouseInPath!=null){
                    Rectangle oldRect=getPathBounds(mouseInPath);
                    mouseInPath=null;
                    repaint(oldRect);
                }
            }
        });
The background color of mouseInPath can be changed in Renderer only when the TreePath(mouseInPath) of MouseOver is saved in JTree.
FileTree fileTree=(FileTree)tree;
        JLabel label= (JLabel) super.getTreeCellRendererComponent(tree,value,sel,expanded,leaf,row,hasFocus);

        DefaultMutableTreeNode node=(DefaultMutableTreeNode)value;
        FileNode fileNode=(FileNode)node.getUserObject();
        label.setText(fileNode.name);
        label.setIcon(fileNode.icon);

        label.setOpaque(false);
//to change the background color if the node in rendering now is the node under the mouse over
        if(fileTree.mouseInPath!=null&&
                fileTree.mouseInPath.getLastPathComponent().equals(value)){
            label.setOpaque(true);
            label.setBackground(new Color(255,0,0,90));
        }
        return label;
Now it is the end of the article. If you have interest in it, welcome for your communication and good ideas.

2012/11/22

TWaverGIS Flex/.Net Has Added the Support for E-map

TWaverGIS has already realized the support for engines like Google Map, GeoServer and MapABC as default. Now some clients would like to combine TWaver with E-map engine, so we add the function to support it as default.

2012/11/15

An Appreciation and Analysis on an Enterprise Application of TWaver

Automatic scheduling function for production can be added into ERP software through the interface for Application Modules developed by TWaver with Swing. We find it quite efficient after the test: it will only cost about one hour to list the 2000 progressing production orders of a medium-sized manufacturer enterprise.

This interface will be projected onto the wall-screen of the company through a big screen or a projector, so that all the members will be able to see the arrangement of tasks and the plans at any time. Some technical points:


2012/11/08

To Test the Code Coverage in Flex with FlexCover

During the process of development, when you want to have a look at the current code coverage as whether there are any unexecuted codes, you can do it with FlexCover. Here are the steps:
  1. Download FlexCover-SDK,http://code.google.com/p/flexcover/
  2. Install CoverageViewer.air:
  3. Please copy a sdk, take the edition 4.1 as an example, and then copy the sdk-modifications-4_0 in the download package into sdk, covering the original file.

  4. Add the revised SDK into the Flash Builder.
  5. Set the current SDK in use as copySDK.
  6. The operation you have done is successful if there is a file named *.cvm now in catalogue bin-debug.
  7. Run CoverageViewer and load the cvm file that has been generated.
  8. Run the demo and see the code coverage:

2012/11/01

How to Export the Base64 Message of an Image

The export of network has been provided in TWaver, so the Network in it can be exported as an image and the data of that Network can be done as binary information or Persistent XML data. Now we will emphasize on how to save images in the form of base64 format when serializing a DataBox.

If you have seen TWaver demo, you must know that the button “Save XML File” has been available on the toolbar of TWaver Network. In addition, there is also a button “Save Image Data” which is to save the image of an element and to serialize it into xml in the form of base64 when TWaver is saving XMLs.
Now let’s see the xml after it has been saved:
<void property="imageURL">
     <string>/demo/resource/images/usa.gif</string>
    </void>
   </object>
  </void>
  <void property="images">
   <object>
    <void method="put">
     <string>/resource/image/twaver/node_icon.png</string>
     <string>iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAASElEQVR42mP4TwGYPGnSfwYQ4/z5&#13;
82RhFANKS0tJwqMGjBqA1QBSsMx3GVQDQAxyMQMusGDevP/ImIFUQLIBu3bt+k8KhukDAHrNjasT&#13;
jbHgAAAAAElFTkSuQmCC&#13;
</string>
    </void>
    <void method="put">
     <string>/resource/image/twaver/link_icon.png</string>
     <string>iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAKUlEQVR42mNgoAYoLS39j4zJMgAb&#13;
m74GUOwNbIaNGjKYDRlGKZYqORkAfhloKfeZLLAAAAAASUVORK5CYII=&#13;
</string>
It can be seen that TWaver saves not only the path of the image of an element, but also the Base64 message of it. If you are careful enough, you will find that the Base64 message of the background image of the network is not included in this xml. Rather, it only saves the Base64 data of the image and the icon of that node. If we have other images or we want to invoke the serialize function ourselves, how to save the Base64 message of images? Now let’s first see the realization of “SaverXML” on the toolbar.
DataBoxOutputSetting setting = new DataBoxOutputSetting();
         setting.setWithElementId(saveElementID);
         setting.setWithAlarmState(saveAlarmState);
         setting.setWithLayers(saveLayers);
      if (saveCurrentSubNetwork) {
       TSubNetwork subNetwork = network.getCurrentSubNetwork();
    setting.setElementFilter(new SubNetworkPersistentFilter(subNetwork));
   }
      if(saveImageData){
       Map images = new HashMap();
       Iterator it = network.getDataBox().iterator();
       while(it.hasNext()){
        Element element = (Element)it.next();
        this.addImage(images, element.getImageURL());
        this.addImage(images, element.getIconURL());
       }
       setting.setImages(images);      
      }
      network.getDataBox().output(fileName, setting); 

private void addImage(Map map, String url) {
    ImageIcon image = TWaverUtil.getImageIcon(url, false);
    if (image != null) {
        String formatName = url.substring(url.length() - 3, url.length());
        byte[] data = TWaverUtil.getByteArrayFromImage(image.getImage(), formatName);
        map.put(url, TWaverUtil.encodeBase64Buffer(data));
    }
}

From the codes above, we can see that TWaver saves the corresponding base64 message of the url of an image into a map, and then “setting.setImages(images);”(in this way, everytime TWaver saves images with their urls when serializing, base64 can be used.) When invoking databox.output or databox.toXML by yourself, you can set DataBoxOutputSetting and save the Base64 messages of the images you need.

At last, there is a complete example in which the Base64 messages of the image of an element and the background image of a network are saved.

import java.awt.BorderLayout;
import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;

import twaver.DataBoxOutputSetting;
import twaver.Element;
import twaver.Link;
import twaver.Node;
import twaver.TDataBox;
import twaver.TWaverUtil;
import twaver.network.TNetwork;
import twaver.network.background.ImageBackground;

public class Base64ImageDemo extends JPanel {
    private TDataBox box = new TDataBox();
    private TNetwork network = new TNetwork(box);

    public Base64ImageDemo() {
        Node fromNode = new Node();
        fromNode.setLocation(585, 406);
        fromNode.setName("boy");
        fromNode.setImage("/images/boy.png");
        box.addElement(fromNode);

        Node toNode = new Node();
        toNode.setLocation(99, 222);
        toNode.setImage("/images/girl.png");
        toNode.setName("girl");
        box.addElement(toNode);
        this.setLayout(new BorderLayout());
        this.add(network, BorderLayout.CENTER);

        Link link = new Link(fromNode, toNode);
        link.putRenderColor(Color.GREEN);
        box.addElement(link);

        network.setImageBackground("/images/china_map.gif");

        exportDataBox();
    }

    private void exportDataBox() {
        DataBoxOutputSetting setting = new DataBoxOutputSetting();
        Map images = new HashMap();
        // add all element images
        Iterator it = network.getDataBox().iterator();
        while (it.hasNext()) {
            Element element = (Element) it.next();
            this.addImage(images, element.getImageURL());
            this.addImage(images, element.getIconURL());
        }
        // add network background image
        if (network.getDataBox().getBackground() instanceof ImageBackground) {
            ImageBackground imageBackground = (ImageBackground) network
                    .getDataBox().getBackground();
            this.addImage(images, imageBackground.getImageURL());
        }

        setting.setImages(images);
        System.out.println(box.toXML(setting));
    }

    private void addImage(Map map, String url) {
        ImageIcon image = TWaverUtil.getImageIcon(url, false);
        if (image != null) {
            String formatName = url.substring(url.length() - 3, url.length());
            byte[] data = TWaverUtil.getByteArrayFromImage(image.getImage(), formatName);
            map.put(url, TWaverUtil.encodeBase64Buffer(data));
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Base64ImageDemo demo = new Base64ImageDemo();
        frame.setContentPane(demo);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(880, 800);
        frame.setTitle("Image Base64 Demo");
        TWaverUtil.centerWindow(frame);
        frame.setVisible(true);
    }
}

2012/10/25

To Build Outdoor Scenes Topology with TWaver 3D

It is quite common in general application system to present the inside of a machine room by means of 2D which is characteristic of being efficient, direct-viewing and exact. With the increase of the amount of information and the development of the technology, clients hope that there can be a more realistic 3D scene to make it more readable. TWaver Flex 3D has realized the representation of 3D scenes with the Element objects in TWaver, by expanding 3D Style of them.

Among all the requirements, the commonest one is to create an array of devices with regular shapes in a machine room and to automatically construct a cabinet with the specified rows and columns.
This standard scene can be constructed through the following codes easily:
public static function getRoomBox(id:Object):ElementBox {
            var size:Number=1600;
            var box:ElementBox=new ElementBox();
            var xgap:Number=size / 8;
            var ygap:Number=size / 8;
            var startX:Number=-xgap * 2.5;
            var starty:Number=-ygap * 2.5;
            for (var i:int=0; i < 6; i++)
            {
                for (var j:int=0; j < 6; j++)
                {
                    box.add(createRack(i + ":" + j, startX + i * xgap, starty + j * xgap));
                }
            }

            var floor:IElement=new Element();

            floor.setStyle(Style3D.MAPPINGTYPE, Consts3D.MAPPINGTYPE_COLOR);
            floor.setStyle(Style3D.THREED_SHAPE_TYPE, Consts3D.THREED_SHAPE_TYPE_CUBE);
            floor.setStyle(Style3D.MATERIAL_ALPHA, 0.6);
            floor.setStyle(Style3D.MATERIAL_COLOR, 0x999999);
            floor.setStyle(Style3D.ZORDERING_OFFSET, 2500);

            floor.setStyle(Style3D.BOTH_SIDES_VISIBLE, true);
            floor.setStyle(Style3D.PROPERTY_SPACE_LOCATION, new Vector3D(0, 0, 0));
            floor.setStyle(Style3D.PROPERTY_SIZE, new Vector3D(size + 10, 0, size + 10));
            box.add(floor);

            return box;
}
private static function createRack(id:String, x:Number, z:Number):IElement{
            var element:IElement=new Element(id);
            element.setStyle(Style3D.MAPPINGTYPE, Consts3D.MAPPINGTYPE_MAP6);
            element.setStyle(Style3D.MAPPING_COMMON_PATH, "rack3DImage");
            element.setStyle(Style3D.PROPERTY_SIZE, new Vector3D(80, 150, 80));
            element.setStyle(Style3D.PROPERTY_SPACE_LOCATION, new Vector3D(x, 150 / 2 + 10, z));
            return element;
}
After the indoor scene, clients have a new requirement that it will be better if the outside of the room can be seen, such as the representation of the connection between the room and another one. To satisfy it, we have to transfer the scene to the outside to present the storey and the connection between it and another. The following codes can help us realize that:
  public static function buildSpacialTopo(box:ElementBox):void{
                var o:Vector3D = new Vector3D(0,0,0);
                var cbp:Vector3D = new Vector3D(0,50,0);
                var lup:Vector3D = new Vector3D(100,10,-60);
                var rup:Vector3D = new Vector3D(-100,10,-60);
                var fp:Vector3D = new Vector3D(0,10,100);
                var l1:Node = createLine([o,new Vector3D(rup.x,3,0),new Vector3D(rup.x,3,rup.z)],5,0x000000);
                box.add(l1);
                l1.name = "black line";
                var l2:Node = createLine([o,new Vector3D(0,3,lup.z),new Vector3D(lup.x,3,lup.z)],5,0x00FF00);
                l2.name = "green line";
                box.add(l2);
                var l3:Node = createLine([o,new Vector3D(fp.x,2,fp.z)],3,0xFF0000);
                l3.name = "red line";
                box.add(l3);
                var cb:Node = createCube(cbp,"BI",new Vector3D(50,100,30));
                cb.name = "building";
                box.add(cb);
                var ds1:Node = createCube(lup,"NO",new Vector3D(25,40,10));
                ds1.name = "NO.1";
                box.add(ds1);
                var ds2:Node = createCube(rup,"NT",new Vector3D(35,20,10));
                ds2.name = "No.2";
                box.add(ds2);
                var f:Node = createCube(fp,"SM",new Vector3D(20,35,10));
                f.setStyle(Style3D.PROPERTY_ROT_ANGLE,new Vector3D(0,180,0));
                f.name = "SM";
                box.add(f);
}
public static function createLine(ps,width,color):Node{
                var line:Node = new Node();
                line.setStyle(Style3D.THREED_SHAPE_TYPE,Consts3D.THREED_SHAPE_TYPE_LINE);
                line.setStyle(Style3D.LINE_SHAPE_POINTS,ps);
                line.setStyle(Style3D.MAPPINGTYPE,Consts3D.MAPPINGTYPE_COLOR);
                line.setStyle(Style3D.MATERIAL_COLOR,color);
                line.setStyle(Style3D.LINE_SHAPE_WIDTH,width);
                return line;
}
public static function createCube(pos,mappingsource,size):Node {
                var node:Node = new Node();
                node.setClient("TYPE","building");
                node.setStyle(Style3D.PROPERTY_SPACE_LOCATION,pos);
                node.setStyle(Style3D.PROPERTY_SIZE,size);
                if(mappingsource!=null){
                    node.setStyle(Style3D.MAPPINGTYPE,Consts3D.MAPPINGTYPE_MAP6);
                    node.setStyle(Style3D.MAPPING_COMMON_PATH,mappingsource);
                }else{
                    node.setStyle(Style3D.MAPPINGTYPE,Consts3D.MAPPINGTYPE_COLOR);
                    node.setStyle(Style3D.MATERIAL_COLOR,0x33FF88);
                }

                return node;
}
We use the 3D model of Line to simulate the linking between these architectures. Let’s have a look:
When we are smooth in applications, clients again have a new idea: Now there have been the indoor and outdoor scenes, but in the outdoor one the architectures are alone and too simple, so the geographical relationship between the architectures is invisible. Can there be more functions like this? O-K. We have to serve clients, to meet the changeable requirements as long as we are coders.

Why not load the simulated the altitude data?
public static function buildTerrain(box:ElementBox):void{
                var node:Node = new Node();
                node.setStyle(Style3D.THREED_SHAPE_TYPE,Consts3D.THREED_SHAPE_TYPE_TERRAIN);
                node.setStyle(Style3D.MAPPINGTYPE,Consts3D.MAPPINGTYPE_COMMON);
                node.setStyle(Style3D.MAPPING_COMMON_PATH,"TT");
                node.setStyle(Style3D.MAPPING_HEIGHTMAP,"HM");
                node.setStyle(Style3D.EXTRUSION_SAMPLES,70);
                node.setStyle(Style3D.EXTRUSION_XSCALE,1);
                node.setStyle(Style3D.EXTRUSION_YSCALE,1);
                node.setStyle(Style3D.EXTRUSION_RECENTER,true);
                node.setStyle(Style3D.PROPERTY_ROT_ANGLE,new Vector3D(90,0,0));
                node.setStyle(Style3D.EXTRUSION_HSCALE,0.1);
                node.setStyle(Style3D.PROPERTY_SPACE_LOCATION,new Vector3D(-100,-100,0));
                node.setClient("TYPE","terrain");
                box.add(node);
}
Now we are able to simulate their geographical relations.


Life is colorful with a series of unexpectedness. A new requirement comes again: How do I operate? How do I interact with it? How do I lead my users to enter the computer room, operate the cabinet, and check the devices and the panes in the room?

Yeah, we have to combine these elements together. Then we add the interactional system. When a user clicks a storey, there will pop out the indoor scene of the machine room in which he can select the cabinet; when double-clicking that cabinet, there appears the 3D model of the devices in it; when double-clicking a device I can again see its panes.

network.selectionFunction = function(element:Element){
        if("terrain" == element.getClient("TYPE")){
            return false;
        }
        return true;
}
network.addEventListener(MouseEvent.MOUSE_MOVE,onClick);
                               .............
private function onSelectionChanged(evt:SelectionChangeEvent):void{
                hideShapedWindow();
                level = 0;
                b1 = null;
                b2 = null;
                var data:Element = box.selectionModel.lastData as Element;
                if(box.selectionModel.selection.contains(data)){
                    showRoomInfo(data,this.mousePos);
                }
}
private function onClick(evt:MouseEvent):void{
                var pos:Point = new Point(evt.stageX,evt.stageY);
                this.mousePos = network.globalToContent(pos);
            }
private function showRoomInfo(element:Element,pos:Point):void{
                if("building" == element.getClient("TYPE")){
                    var box:ElementBox =DataUtils.getRoomBox("fd");
                    room = createRoom();
                    showShapedWindow(room,pos);
                }
}

 

2012/10/18

Partial AutoLayout and Layout in Nested Groups in TWaver

TWaver has provided some layout algorithms to help us quickly set the positions for elements. They are enough for us to use in many cases, but in some complicated ones we need to make some operations ourselves. Please look at the following example:
Screenshot:
Under this circumstance, it is not so agreeable to use TNetwork#doLayout directly, for doLayout will use the same layout algorithm to all elements in the network. But the several sub-groups under the group in the screenshot have their own layout. My solution is that we can first arrange the layout wholly to the group at the outermost layer and then recurse and go through the sub-groups, namely, arrange the layout to them separately.

Code Implementation:
There are two ways available to use the partial layout in TWaver:
  • Add the Element to be in the layout into a new TNetwork, and arrange the layout with the new TNetwork.
  • TWaver only arranges the layout to the selected element as default, so the element to be in the layout can be put in to the SelectionModel.
In this demo, at first, use the first way above to arrange the layout to the outermost group:
//layoutNetwork is a temporary network which is used to arrange a layout.
//The layout has been saved in ClientProperty when generating the group.
layoutDatabox.addElementWithDescendant(group1);
layoutNetwork.doLayout((Integer)group1.getClientProperty("layout"),false);
Then recurse and go through the sub-groups to arrange the layout to them.
recursionChild(group1);
............................................

 private void recursionChild(Group group){
        for(int i=0;i<group.getChildren().size();i++){
            Element element= (Element) group.getChildren().get(i);
            if(element instanceof Group){
                Group childGroup=((Group)element);
                recursionChild(childGroup);//If there are sub-groups, the recursion will be continued.
//to combine the two ways together and add the sub-group to be in a layout into the SelectonModel of layoutNetwork
                layoutDatabox.getSelectionModel().clearSelection();
                layoutDatabox.getSelectionModel().appendSelection(childGroup.getChildren());
                layoutNetwork.doLayout((Integer) ((Group) element).getClientProperty("layout"), false);
                layoutDatabox.getSelectionModel().clearSelection();
            }
        }
    }
All codes:
public class LayoutTest extends JPanel {
    TDataBox layoutDatabox=new TDataBox();
    TNetwork layoutNetwork=new TNetwork(layoutDatabox);
    public LayoutTest(){

        TNetwork network=new TNetwork();

        ResizableNode gatewayNode=new ResizableNode();

        gatewayNode.putCustomDraw(true);
        gatewayNode.putCustomDrawShapeFactory(TWaverConst.SHAPE_ROUND_RECTANGLE_HALF);
        gatewayNode.putCustomDrawGradient(false);
        gatewayNode.putCustomDrawFillColor(new Color(254, 254, 206));
        gatewayNode.setSize(70, 30);
        gatewayNode.setName("Gateway");
        gatewayNode.putLabelPosition(TWaverConst.POSITION_CENTER);

        Group group1=createGroup(4,TWaverConst.LAYOUT_TREE);
        Group group1_1=createGroup(12,TWaverConst.LAYOUT_CIRCULAR);
        Group group1_2=createGroup(5,TWaverConst.LAYOUT_EAST);

        Link group1_2Link=new Link((Node)group1.getChildren().get(0),(Node)group1_2.getChildren().get(0));
        setLinkStyle(group1_2Link);
        group1.addChild(group1_2);
        group1.addChild(group1_2Link);

        Link group1_1Link=new Link((Node)group1.getChildren().get(0),(Node)group1_1.getChildren().get(0));
        setLinkStyle(group1_1Link);
        group1.addChild(group1_1);
        group1.addChild(group1_1Link);

//layoutNetwork is a temporary network which is used to arrange the layout.
//The layout has been saved in ClientProperty when generating the group.
        layoutDatabox.addElementWithDescendant(group1);
        layoutNetwork.doLayout((Integer)group1.getClientProperty("layout"),false);
        recursionChild(group1);

        TDataBox box=new TDataBox();
        box.addElementWithDescendant(group1);
        box.addElement(gatewayNode);

        group1.putGroupOpaque(true);
        group1.putGroupFillColor(new Color(154,206,254));
        network.setDataBox(box);
        this.setLayout(new GridLayout(1,3));
        this.add(network);

        Layer layer=new Layer();
        box.getLayerModel().addLayer(layer);
        box.getLayerModel().moveToTop(layer);

        Link link1=new Link(group1,gatewayNode);
        setLinkStyle(link1);
        link1.setLinkType(TWaverConst.LINK_TYPE_ORTHOGONAL);
        link1.setLayerID(layer.getID());
        box.addElement(link1);
    }

    /**
     * arrange the layout:to recurse the nested group
     * @param group
     */
    private void recursionChild(Group group){
        for(int i=0;i<group.getChildren().size();i++){
            Element element= (Element) group.getChildren().get(i);
            if(element instanceof Group){
                Group childGroup=((Group)element);
                recursionChild(childGroup);//If there are sub-groups, the recursion will be continued.
//to combine the two ways together and add the sub-group to be in a layout into the SelectonModel of layoutNetwork
                layoutDatabox.getSelectionModel().clearSelection();
                layoutDatabox.getSelectionModel().appendSelection(childGroup.getChildren());
                layoutNetwork.doLayout((Integer) ((Group) element).getClientProperty("layout"), false);
                layoutDatabox.getSelectionModel().clearSelection();
            }
        }
    }
    /**
     * to generate the test group and node
     * @param childCount
     * @param layout
     * @return
     */
    private Group createGroup(int childCount,int layout){
        Group group=new Group();
        group.setExpand(true);
        group.putClientProperty("layout",layout);

        ResizableNode router=new ResizableNode();
        router.putCustomDraw(true);
        router.putCustomDrawShapeFactory(TWaverConst.SHAPE_RECTANGLE);
        router.putCustomDrawGradient(false);
        router.putCustomDrawFillColor(new Color(254, 254, 206));
        router.setSize(60, 20);
        router.setName("Router");
        router.putLabelPosition(TWaverConst.POSITION_CENTER);
        group.addChild(router);

        for(int i=0;i<childCount;i++){
            Node childNode=new Node();
            Link link=new Link(router,childNode);
            childNode.putCustomDraw(true);
            childNode.putCustomDrawShapeFactory(TWaverConst.SHAPE_RECTANGLE);
            childNode.putCustomDrawGradient(false);
            childNode.putCustomDrawFillColor(new Color(254,202,2));
            childNode.setName("C"+i);
            childNode.putLabelPosition(TWaverConst.POSITION_CENTER);

            setLinkStyle(link);
            group.addChild(childNode);
            group.addChild(link);
        }
        return group;
    }

    /**
     * to set the style of the link
     * @param link
     */
    public void setLinkStyle(Link link){
        link.putLinkColor(Color.black);
        link.putLinkOutlineWidth(0);
        link.putLinkWidth(1);
        link.putLinkAntialias(true);
        link.putLinkToArrow(true);
        link.putLinkToArrowOutline(false);
    }
    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame=new JFrame("layout");
                frame.setSize(1000,700);
                frame.setContentPane(new LayoutTest());
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}