基于 HTML5 WebGL 的 3D 收集拓扑图

在数据量很大的2D 场景下,要找到详细的模子比较难题,而且只能显现出模子的的某一部份,显现也不够直观,这类时刻能疾速搭建出 3D 场景就有很大需求了。然则搭建 3D 应用场景又依赖于经由过程 3ds Max 或 Maya 的专业 3D 设想师来建模,Unity 3D 引擎做图形衬着等,这对用户来讲都是应战!不过,HT 一站式的供应了从建模到衬着,包含和 2D 组件显现和数据融会的一站式解决方案。HT 基于 WebGL 的 3D 手艺的图形组件 ht.graph3dView 组件经由过程对 WebGL 底层手艺的封装,与 HT 其他组件一样, 基于 HT 一致的 DataModel 数据模子来驱动图形显现,极大降低了 3D 图形手艺开辟的门坎,在熟习HT 数据模子基础上,平常程序员只须要 1 小时的进修即可上手 3D 图形开辟。

好了,空话不多说,先附上 Demo:http://www.hightopo.com/demo/…

《基于 HTML5 WebGL 的 3D 收集拓扑图》

固然,这里的我只是用简朴的图形来示意装备,脑洞大开的你固然能够将其换成更有意义的模子。

接下来看看我们是怎样做到的:

1、准备工作:

3D 和 2D 的 API 的设想上坚持了许多一致性,3D 视图组件是 ht.graph3d.Graph3dView, 2D 视图组件是 ht.graph.GraphView,二者可同享统一数据模子 DataModel。在 HT 中,为了让了取得靠近实在三维物体的视觉结果,我们经由过程透视投影使得远的对象变小,近的对象变大,平行线会涌现先交等更靠近人眼视察的视觉结果:

《基于 HTML5 WebGL 的 3D 收集拓扑图》

如上图所示,透视投影终究显现到屏幕上的内容只要截头椎体部份的内容,因而 GraphView 供应了 eye,center,up,far,near, fovy 和 aspect 参数来掌握截头椎体的详细局限,我们在现实应用中用到更多的是 eye 和 center:

getEye() | setEye([x, y, z]),决议眼睛(或 Camera)所在位置,默许值为 [0, 300, 1000];

getCenter() | setCenter([x, y, z]),决议目的中间点(或 Target)所在位置,默许值为 [0, 0, 0];

概况看 HT for Web 3D 手册 手册 (http://www.hightopo.com/guide…)。

dataModel = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dataModel);    
g3d.setEye(1800, 800, 1000);
g3d.setCenter(0, 100, 0);
g3d.setDashDisabled(false);
g3d.getView().style.background = 'rgb(10, 20, 36)';
g3d.addToDOM();

2、建立装备:

服务器,Demo 中的服务器现实上是经由过程 addStyleIcon 体式格局在服务器的位置增添图片,概况可看 HT for Web 入门手册(http://www.hightopo.com/guide…):

//注册图片
ht.Default.setImage('server', 'server.png');

var server = new ht.Node();
        server.s3(0, 0, 0);
        server.p3(0, 60, 0);
        server.addStyleIcon('icon', {
            position: 0,
            width: 200, 
            autorotate: true,
            transparent: true,               
            height: 200,
            names: ['server']
        });
        dataModel.add(server);

工作台,这里的工作台现实上是平面圆柱来示意的,HT 在 GraphView 的 2D 图形上,显现种种图形是经由过程 style 的shape 属性决议,相似的 HT 在 3D 上供应了 shape3d属性,预定义了多种 3D 的形体,概况见HT for Web 3D 手册。不过在这里我并没有效预定义的图形,而是经由过程 ht.Default.createRingModel 的体式格局建立圆柱,该要领能够依据 xy 平面的曲线,围绕一周构成 3D 模子,所以能够用来定义多种圆形 3D 模子。

var desktop = new ht.Node();
        desktop.s({
            '3d.selectable': false,
            'shape3d': ht.Default.createRingModel([
                        0, 40,
                        450, 40,
                        450, 0,
                        0, 40
                    ], null, 20, false, false, 50),
            'shape3d.color': '#003333'
        });
desktop.s3(1, 1, 1);
dataModel.add(desktop);

平台上的装备,我们一共建立了 32 个装备:

var count = 32;
            radius = 400;
            index = count/2;
        for (var i =  1; i <= count/2; i++) {
            var device1_angle1 = Math.PI * 2 * (index - i) / count;
                device1_angle2 = Math.PI * 2 * (index + i) / count;
                device1_angle3 = Math.PI * 2 * index / count;

            var device1_1 = createDevice(device1_angle1, radius, 60);
                device1_2 = createDevice(device1_angle2, radius, 60);
                device1_3 = createDevice(device1_angle3, radius, 60);

            layoutDevice1(device1_1, device1_angle1);
            var device1_edge1 = createEdge(device1_1, server, 'line1');
            device1_edge1.s({'shape3d.color': 'rgb(205, 211, 34)'});
            dataModel.add(device1_1);
            dataModel.add(device1_edge1);

            layoutDevice1(device1_2, device1_angle2);
            var device1_edge2 = createEdge(device1_2, server, 'line1');
            device1_edge2.s({'shape3d.color': 'rgb(205, 211, 34)'});
            dataModel.add(device1_2);
            dataModel.add(device1_edge2);

            layoutDevice1(device1_3, device1_angle3);
            var device1_edge3 = createEdge(device1_3, server, 'line1');
            device1_edge3.s({'shape3d.color': 'rgb(205, 211, 34)'});
            dataModel.add(device1_3);
            dataModel.add(device1_edge3);
        }

为了让建立的装备在平台上的规划越发合理,依据 index 计算出装备摆放角度,而且依据圆柱中间,圆盘半径和角度计算出每一个装备摆放的位置:

function createDevice (angle, x, y) {
        var node = new ht.Node();
            cos = Math.cos(angle);
            sin = Math.sin(angle);
        node.p3(x*sin, y, x*cos);
        return node;
    }

其他装备,

var num = 18;
    var h = [800, 900, 1000, 1100, 1200];
    var v = [40, 60, 80, 100];
    var colors = ['#fcfc63', '#00E1E4'];
    for (var j = 0; j < num; j++) {
            var device2_angle = Math.PI * j / num;
            var device2 = createDevice(device2_angle, 
                                       h[Math.floor(Math.random()*5)], v[Math.floor(Math.random()*4)]);
            device2.s3(100, 20, 100); 
            device2.s({
               'shape3d': 'cylinder',
               'shape3d.color': colors[Math.floor(Math.random()*2)]
    });
    var device2_edge = createEdge(device2, desktop , 'line2');
    device2_edge.s({'shape3d.color': 'rgb(0, 203, 94)'});

    dataModel.add(device2);
    dataModel.add(device2_edge);         
 }

3、连线

HT for Web 供应了默许的直线和多点的连线范例能满足大部份基础拓扑图形应用,但在这里我们须要依据现实需求绘制曲线,所以,须要用到自定义连线范例,概况看HT for Web 连线范例手册:

用 ht.Default.setEdgeType(type, func, mutual) 函数可用于自定义新连线范例:

type:字符串范例的连线范例,对应 style 的 edge.type 属性;
func:函数范例,依据传入参数(edge,gap,graphView,sameSourceWithFirstEdge)返回连线走向信息:

edge:当前连线对象;

gap:多条连线成捆时,本连线对象对应中间连线的间距;

graphView:当前对应拓扑组件对象;

sameSourceWithFirstEdge:boolean 范例,改连线是不是与同组的第一条同源;

返回值为 {points: new ht.List(…),segments:new ht.List(…)} 构造的连线走向信息,segments 可取值以下:

 1、moveTo,占用 1 个点信息;

 2、lineTo,占用 1 个点信息;

 3、quadraticCurveTo,占用 2 个点信息;

 4、bezierCurveTo,占用 3 个点信息;

 5、closePath,不占用点信息;

mutual:该参数决议连线是不是影响肇端或完毕节点上的一切连线,默许为 false 代表只影响同 source 和 target 的 EdgeGroup 中的连线,HT 预定义的连线范例中,后缀为 2 的范例都是 mutural 为 true 的庞杂连线范例。
在 Demo 中定义了两种范例的连线,分别为 line1 和 line :

ht.Default.setEdgeType('line1', function(edge){
            var sourcePoint1 = edge.getSourceAgent().getPosition(),
                targetPoint1 = edge.getTargetAgent().getPosition(),
                points1 = new ht.List();       
                points1.add(sourcePoint1);
                points1.add({
                    x: (sourcePoint1.x + targetPoint1.x)/2 + 200,
                    e: sourcePoint1.e,
                    y: (sourcePoint1.y + targetPoint1.y)/2
                });
                points1.add(targetPoint1);                          
            return {
                points: points1,
                segments: new ht.List([1, 3])
            };
        });
ht.Default.setEdgeType('line2', function(edge){
            var sourcePoint = edge.getSourceAgent().getPosition(),
                targetPoint = edge.getTargetAgent().getPosition(),
                points = new ht.List();       
                points.add(sourcePoint);
                points.add({
                    x: (sourcePoint.x + targetPoint.x)/2,
                    e: ((sourcePoint.e + targetPoint.e)/2 || 0) - 300,
                    y: (sourcePoint.y + targetPoint.y)/2
                });
                points.add({
                    x: targetPoint.x,
                    e: targetPoint.e -80, 
                    y: targetPoint.y                            
                });
            return {
                points: points,
                segments: new ht.List([1, 3])
            };
        });

连线范例定义好,接下来就是建立连线,然则连线上另有活动结果,这个又怎样完成呢?我们 HT 有扩大活动线插件,能够在 ht.Shape 和 ht.Edge 上增添活动结果,支撑内部活动元素或用户自定义的活动元素沿着途径步进,要运用也异常轻易,只须要引入 ht-flow.js 文件,概况可见 HT for Web活动线手册(http://www.hightopo.com/guide…),然则插件并不适用于 3D 模子中,那在 3D 模子中该怎样办呢?纵然不能运用现成的插件,我们也能够完成活动结果,能够看HT for Web 入门手册 中连线部份,我们能够将连线款式经由过程 edge.dash 设置为虚线后,动态转变 edge.dash.offset 虚线偏移,即可完成活动结果,所以,我们建立连线时:

function createEdge (source, target , type) {
        var edge = new ht.Edge(source, target);
        edge.s({
            'edge.color': 'yellow',
            'edge.dash': true,
            'edge.dash.3d': true,
            'edge.dash.width': 4,
            'edge.type': type,                    
            'edge.dash.color': 'rgb(10, 20, 36)',                    
            'edge.dash.pattern': [20, 25]
        });
        edge.a({
            'flow.enabled': true,
            'flow.direction': -1,
            'flow.step': 4
        });
        return edge;
    }

末了,要让虚线活动起来,能够运用 HT 中的调理,概况可看HT for Web 调理手册(http://www.hightopo.com/guide…):

flowTask = {
            interval: 50,
            action: function(data){
                if(data.a('flow.enabled')){
                    var offset = data.s('edge.dash.offset') + data.a('flow.step') * data.a('flow.direction');
                    data.s('edge.dash.offset', offset);                        
                }
            }
        };
        dataModel.addScheduleTask(flowTask);

到这里,Demo 中的重要手艺点都已引见了一遍,能够看出我们 HT 的壮大的地方,固然我们官网上另有许多很有意义的结果,人人也能够看一看,也能够玩一玩我们的 HT 感觉它的壮大的地方,再次附上 Demo 地点: http://www.hightopo.com/demo/…

    原文作者:hightopo
    原文地址: https://segmentfault.com/a/1190000009996165
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞