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 this article as a summary of this project.

GIF of this article:

Getting to the point, the whole example took over 200 lines of code to implement, which is why I like using HT, to be 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.

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(); document.body.appendChild(view); Style. Left = '0'; //HT default position is absolute, so set position style.right = '0'; style.top = '0'; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false); // Trigger event when window size changes, call iv function, refresh 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 body g3d.setDashDisabled(false); G3d.setmovablefunc (function(){return false; // Return false, all primitives are immovable}); g3d.setEye([-813, 718, 1530]); // Set eyeg3d.setCenter ([140, -25, 217]); // Set center (target) gv = new ht.graph.graphView (dm); //2d component gv.getView().className = 'graphView '; / / HT component root layer is a div, through the getView () function to obtain the document. The body. The appendChild (gv) getView ()); // Add the topology component to the body gv.fitContent(true); Var form = new ht.widget.formpane (); Var view = form.getView(); / / retrieve the underlying forms div document. The body. The appendChild (view); // Add the underlying div of the form component to the body. Form.setwidth (200); // Set the width of the form panel form.setheight (140); view.style.top = '5px'; // Set the bottom div of the form panel to view.style.right = '5px'; 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.

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);
http://www.hightopo.com/demo/3DEdgeFlow/index.html
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){var node = new ht.node (); Dm.add (Node); // Create a Node of type ht.Node. // Add the node to the 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 'label.position': 23,// text display position' label.transparent': 'label.color': '#eee',// Text color' label.t3': [0, 0, -151],// Text offset 'label.r3': [0, Math. PI, 0], / / text under the 3 d rotation 'label. Scale' : 2 / / text zoom}); node.r3(0, Math.PI, 0); // Rotate node.p3(p3); Node.s3 (s3); // Set the location of the node in 3d. // Set the size of the node in 3d node.setname (name); // Set the node display name return node; // return node}Copy the code

And the creation of wires:

CreateEdge (exchange, service){var edge = new ht. edge (exchange, service); dm.add(edge); Edge. s({'edge.width': 4,// line width' edge.color': 'red',// line color' edge.dash': true,// whether to display dashed line 'edge.dash. Color ': ' 'yellow', / / dotted line color edge. The dash. The pattern ': [32, 32], / / dotted line attachment style is the default [16, 16]}); Edge. a({// User-defined attributes for setAttr abbreviation 'flow.enable': true,// whether to enable flow 'flow.direction': 1,// direction' flow.step': 4// 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); // At the same time, we also need to set animation to control the time interval to make the dotted line offset and form a state of “flow”. Please refer to the schedule manual for 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 animationCopy 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]); / / figure yuan floor. The floor s ({' all. Color ':' RGB (47, 79, 79), '3 d. Selectable: false,}); Exchange = createNode([0, 300, -400], [200, 20, 150], 'H3C Switch ', 'Models/machine/cabinet/cabinet 6. Json ', 'symbols/computer room/rack component 1. Json') s (' label. T3, [0, 0, - 151]). Service1 = createNode([-400, 140, 0], [100, 260, 100], 'Standby ',' Models/machine/cabinet/cabinet 2.json', 'Symbols/computer/resistors. Json '); Service2 = createNode([-200, 140, 0], [100, 260, 100], 'website ',' Models/engine/rack/rack 2. Json ', 'Symbols/engine/rack. Service3 = createNode([0, 140, 0], [100, 260, 100], 'OA', 'Models/engine/rack/rack 2.json',' Symbols/engine/rack. Json '); Service4 = createNode([200, 140, 0], [100, 260, 100], 'ads ',' Models/engine/rack/rack 2.json', 'Symbols/engine/rack. Json '); Service5 = createNode([400, 140, 0], [100, 260, 100], 'Load ',' Models/engine/rack / 2. Json ', 'Symbols/engine/rack. Json '); var arr = [service1, service2, service3, service4, service5]; for(var i = 0; i < arr.length; I ++){var service = arr[I]; 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); // Exchange2 = createNode([-100, 60, 400], [200, 20, 150], 'Procurve Switch 2010-23 ', 'models/computer room/cabinet/rack equipment 6. Json' and 'symbols/computer room/rack component 1. Json') s (' label. T3, [0, 0, - 151]). // The rest of the created node 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(); Var view = form.getView(); / / retrieve the underlying forms div document. The body. The appendChild (view); // Add the underlying div of the form component to the body. Form.setwidth (200); // Set the width of the form panel form.setheight (140); view.style.top = '5px'; // Set the bottom div of the form panel to view.style.right = '5px'; Background = 'rgba(255, 255, 255, 0.2)'; form.setLabelColor('#fff'); Form.addrow ([// use this function to add a line of component 'Enable Flow' to the form panel,// Use this function to access the display text value of the property name, if empty, display the name property value {checkBox: {// CheckBox, HT will automatically build the ht.widget.CheckBox object based on the property value and store it on the element property value: true, onValueChanged: Function (){flowtask.enabled = this.getValue();}}}, [0.1, 0.1]); form.addrow ([{name: 'flow.direction', accessType: 'attr', element: 'Flow Direction', }, { comboBox: { value: 1, labels: [' float ', 'float '], values: [-1, 1], onValueChanged: function(){ for(var i = 0; i < dm.size(); i++){ var data = dm.getDatas().get(i); Data. A ('. The flow direction, enclosing getValue ());}}}}], [0.1, 0.1]); form. AddRow ([{name: 'flow. Step' element: 'Flow Step', accessType: 'attr',}, {slider: {// If set, HT will automatically build an HT.Widget.Slider based on the property value and save it on the Element property min: 0,// min Max: 10,// Max step: 0.1,// 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 picker box instant: true,// Get and set whether to be in the instant state, representing as the table and property page editor, will change the model value in real time: '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', // Dash style attribute 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. The dash. Color, enclosing getValue ());}}}}], [0.1, 0.1]);}Copy the code

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