preface

This example was originally intended to simulate server-client communication, and I simplified the entire requirement into this example. 3D simulation generally needs the assistance of hawk-eye, so it will be more clear to find products and summarize the whole space. In this example, I also added it. This article can be regarded as a summary of this project.

Example link: www.hightopo.com/demo/3DEdge…

GIF of this article:

Code implementation

Getting to the point, the whole example was implemented in just over 200 lines of code, which is what I like about using HT, being able to develop quickly. The rise of Web3D technology is roughly divided into two factions: plug-in faction and HTML5 faction. HT is HTML5 based, there is no need to install any plugins.

Scenario building

First of all, the scene is built again. The interface adds three parts on the body: 3D component, form component and topology component (2D component). For ease of loading and filling Windows for the outermost components, all components of HT have addToDOM functions that implement the following logic, where iv is short for invalidate:

addToDOM = function(){   
    var self = this,
        view = self.getView(), // Get the underlying div of the component
        style = view.style;
    document.body.appendChild(view); // Add the underlying div to the body
    style.left = '0'; // The default position of the HT component is absolute, so the position must be set
    style.right = '0';
    style.top = '0';
    style.bottom = '0';      
    window.addEventListener('resize'.function () { self.iv(); }, false); // Trigger the event when the window size changes, call the iv function, refresh the page
}
Copy the code

While HT components are typically embedded in containers such as BorderPane, SplitView, and TabView, the outermost HT components require the user to manually add the underlying div element returned by getView() to the DOM element of the page. When the parent container size changes, if the parent container is a predefined container component such as BorderPane and SplitView, the HT container automatically recursively calls the child component invalidate function to notify the update. However, if the parent container is a native HTML element, the HT component cannot know that it needs to be updated. Therefore, the outermost HT component usually needs to listen for the window size change event and call the invalidate function of the outermost component to update.

Since this function fixes the position in style, we cannot use this function for all components. We add topology components and property components to the interface in the same way as this function. 3D components can directly use addToDOM function:

dm = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dm); / / 3 d component
g3d.addToDOM(); // Add the component to the body
g3d.setDashDisabled(false); // Start the dashed line flow
g3d.setMovableFunc(function(){ // Override the move function
    return false; // Return false, all primitives are immovable
});
g3d.setEye([- 813..718.1530]); / / set the eye
g3d.setCenter([140.- 25.217]); // Set center (target)
gv = new ht.graph.GraphView(dm); / / 2 d component
gv.getView().className = 'graphview'; // The HT component root layer is a div, obtained by the getView() function
document.body.appendChild(gv.getView()); // Add the topology component to the body
gv.fitContent(true); // Zoom and pan the entire topology to show all the primiples
var form = new ht.widget.FormPane(); // Form panel components
var view = form.getView(); // Get the form's underlying div
document.body.appendChild(view); // Add the form component's underlying div to the body
form.setWidth(200); // Set the width of the form panel
form.setHeight(140);
view.style.top = '5px'; // Sets the position of the bottom div of the form panel
view.style.right = '5px';
view.style.background = 'rgba (255, 255, 255, 0.2)';
Copy the code

I won’t go into detail about the styles of the topology and properties components, just setting a background color and the left right top bottom position. HT components are usually positioned in absolute mode with position set to Absolute.

Add eagle eye

You might be wondering, how did this hawk-eye come about? In HT, as long as 2D and 3D share the same dataModel, they can have all the elements in the dataModel in the same location, just do something like this:

dm = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dm);
gv = new ht.graph.GraphView(dm);
Copy the code

Isn’t it very simple… Can save a lot of time development…

In this example, all elements except wires are hT. Node types, so we encapsulate the creation method of this Node for reuse:

function createNode(p3, s3, name, shape){ // Create a node
    var node = new ht.Node(); // Create a Node of type HT. Node
    dm.add(node); // Add the node to the data container dataModel
    node.s({ // Set the style of the node. S is short for setStyle
        'shape3d': shape, // Specify the shape of the node. Here is the JSON file passed in to the 3D model
        'label.position': 23.// Text display position
        'label.transparent': true.// Whether the text is transparent in 3D can eliminate the jagged edges around the font
        'label.color': '#eee'.// Text color
        'label.t3': [0.0.- 151.].// Text offset in 3D
        'label.r3': [0.Math.PI, 0].// Rotate text in 3D
        'label.scale': 2 // Text zoom
    });
    node.r3(0.Math.PI, 0); // Node rotation
    node.p3(p3); // Set the node position in 3D
    node.s3(s3); // Set the size of the node in 3D
    node.setName(name); // Set the display name of the node
    return node; // returns the node
}
Copy the code

And the creation of wires:

function createEdge(exchange, service){ // Create a connection
    var edge = new ht.Edge(exchange, service);
    dm.add(edge);
    edge.s({
        'edge.width': 4.// Line width
        'edge.color': 'red'.// Line colors
        'edge.dash': true.// Whether to display dashed lines
        'edge.dash.color': 'yellow'.// Dashed line color
        'edge.dash.pattern': [32.32].// Line dashed style defaults to [16, 16]
    });
    edge.a({ // User-defined attributes are short for setAttr
        'flow.enable': true.// Whether to enable flow
        'flow.direction': 1./ / direction
        'flow.step': 4 / / step by step
    });
    return edge;
}
Copy the code

All the lines shown in our interface are dotted line flow. HT turns off the dotted line flow function by default. Turn on the dotted line flow function through the following sentence:

g3d.setDashDisabled(false); // Start the dashed line flow
Copy the code

At the same time, we also need to set the animation to control the time interval and make the dotted line offset to form a “flow” state. Please refer to the schedule scheduling manual for the animation:

flowTask = {
    interval: 40.action: function(data){
        if(data.a('flow.enable')){
            data.s('edge.dash.offset', data.s('edge.dash.offset')+(data.a('flow.step')*data.a('flow.direction'))); }}}; dm.addScheduleTask(flowTask);// Add the flowTask animation
Copy the code

The following are all the server and client node declarations that appear on the screen, which are created based on the createNode and createEdge functions:

function initModel(){
    floor = createNode([0.5.0], [1000.10.500]); // Floor graphics
    floor.s({
        'all.color': 'rgb(47, 79, 79)'.'3d.selectable': false}); exchange = createNode([0.300.- 400.], [200.20.150].'H3C Core Switch '.'Models/machine room/Cabinet/cabinet 6. Json'.'Symbols/Machine room/Cabinet Component 1. Json').s('label.t3'[0.0.- 151.]); / / switches

    // Five servers with different functions
    service1 = createNode([- 400..140.0], [100.260.100].'standby'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
    service2 = createNode([- 200..140.0], [100.260.100].'the website'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
    service3 = createNode([0.140.0], [100.260.100].'OA'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
    service4 = createNode([200.140.0], [100.260.100].'advertising'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
    service5 = createNode([400.140.0], [100.260.100].'accept'.'Models/machine Room/Cabinet 2. Json'.'Symbols/computer/resistance cabinet. Json');
    var arr = [service1, service2, service3, service4, service5];
    for(var i = 0; i < arr.length; i++){ // The node is created with a rotation and font rotation and position, which are specific to the device
        var service = arr[i];
        service.r3(0.0.0);
        service.s({
            'label.r3': [0.0.0].'label.t3': [0.30.50]}); }// Create a connection between the switch and the server
    createEdge(exchange, service1);
    createEdge(exchange, service2);
    createEdge(exchange, service3);
    createEdge(exchange, service4);
    createEdge(exchange, service5);
    // Switch number 2
    exchange2 = createNode([- 100..60.400], [200.20.150].'Procurve Switch 2010-23 '.'Models/machine room/Cabinet/cabinet 6. Json'.'Symbols/Machine room/Cabinet Component 1. Json').s('label.t3'[0.0.- 151.]);
    // The rest of the node creation part is too repetitive, I will not post code
}
Copy the code

Finally, in the form, manipulate the flow, flow direction, flow step, line color, and dotted color of the edge. We’re going to add these properties to the form that we need to manipulate, five properties in total, This includes the properties flow.direction, flow.step and the style properties edge.color and edge.dash Property to control the flow of wires. We realize access to Data node through name attribute and accessType attribute:

function createForm(){
    var form = new ht.widget.FormPane(); // Form panel components
    var view = form.getView(); // Get the form's underlying div
    document.body.appendChild(view); // Add the form component's underlying div to the body
    form.setWidth(200); // Set the width of the form panel
    form.setHeight(140);
    view.style.top = '5px'; // Sets the position of the bottom div of the form panel
    view.style.right = '5px';
    view.style.background = 'rgba (255, 255, 255, 0.2)';
    form.setLabelColor('#fff'); // Set the text color in the form panel
   
    form.addRow([ // Use this function to add a row of components to the form panel
        'Enable Flow'.// Display text value used to access the property name, if empty display name property value
        {
            checkBox: { // CheckBox, HT automatically builds an ht.widget.CheckBox object based on the property value and saves it on the element property
                value: true.onValueChanged: function(){ // Call the function after the value changes
                    flowTask.enabled = this.getValue(); }}}, [0.1.0.1]);
    form.addRow([
        {
            name: 'flow.direction'.accessType: 'attr'.element: 'Flow Direction'}, {comboBox: {
                value: 1.labels: ['Forward flow'.'Reverse flow'].values: [- 1.1].onValueChanged: function(){
                    for(var i = 0; i < dm.size(); i++){
                        var data = dm.getDatas().get(i);
                        data.a('flow.direction'.this.getValue()); }}}}], [0.1.0.1]);
    form.addRow([
        {
            name: 'flow.step'.element: 'Flow Step'.accessType: 'attr'}, {slider: { // Slider, HT will automatically build an HT.Widget. Slider based on the property value and save it on the Element property
                min: 0./ / the minimum
                max: 10./ / Max
                step: 0.1./ / step by step
                value: 1.onValueChanged: function(){
                    for(var i = 0; i < dm.size(); i++){
                        var data = dm.getDatas().get(i);
                        data.a('flow.step'.this.getValue()); }}}}], [0.1.0.1]);
    form.addRow([
        {
            name: 'edge.color'.accessType: 'style'.element: 'Edge Color'}, {colorPicker: { // Color selection box
                instant: true.// Gets and sets whether the model values will change in real time as the editor of the table and property pages
                value: 'rgb(255, 0, 0)'.onValueChanged: function(){
                    for(var i = 0; i < dm.size(); i++){
                        var data = dm.getDatas().get(i);
                        data.s('edge.color'.this.getValue()); }}}}], [0.1.0.1]);
    form.addRow([
        {
            name: 'edge.dash.color'.// Dashed style properties
            element: 'Dash Color'.accessType: 'style'
        },
        {
            colorPicker: {
                instant: true.value: 'rgb(255, 255, 0)'.onValueChanged: function(){
                    for(var i = 0; i < dm.size(); i++){
                        var data = dm.getDatas().get(i);
                        data.s('edge.dash.color'.this.getValue()); }}}}], [0.1.0.1]);
}
Copy the code

Is not very simple ~ quick hands-on practice it!