在前面《电信网络拓扑图自动布局》一文中,我们大体介绍了 HT for Web 电信网络拓扑图自动布局的相关知识,但是都没有深入地描述各种自动布局的用法,我们今天在这边就重点介绍总线的具体实现方案。

在 HT for Web 的连线手册中,有说明可以自定义连线类型,通过 ht.Default.setEdgeType(type, func, mutual) 函数定义,我们今天要描述的总线也是通过这样的方法来实现的。

我们来简单地描述下这个方法,虽然在文档中已经描述得很详细了,为了下面的工作能够更好的开展,我这边还是再强调下。

这个函数名是 setEdgeType,顾名思义,它是用来自定义一个 EdgeType 的,那么第一个参数 type 就是用来定义这个 EdgeType 的名称,在 Edge 的样式上设置 edge.type 属性为 type 值,就可以让这条连线使用是我们自定义的 EdgeType。

那么第二个参数呢,就是用来计算连线的走线信息的函数,这个回调函数将会传入四个参数,分别是:edge、gap、graphView、sameSourceWithFirstEdge,其中 edge 就是样式上设置 edge.type 属性为 type 值的连线对象,这个参数是最重要的,通常有这个参数就可以完成格式各样的连线了。其他参数在手册中都描述得很清楚,可以转到手册中阅读,http://www.hightopo.com/guide/guide/plugin/edgetype/ht-edgetype-guide.html。

那这第三个参数呢,是决定连线是否影响起始或结束节点上的所有连线,这个参数解释起来比较复杂,后续有机会的话,我们再详细说明。

先来看看一个简单的例子,

上图中,可以看到节点间的连线并不是普通的直线,或者简单的折线,而是漂亮的曲线,那么这样的曲线是怎么生成的呢?既然将这个例子放到这边作为案例,那么它一定使用了自定义 EdgeType 的功能,观察图片可以发现曲线其实可以用二次方曲线来表示,所以呢,我们在 setEdgeType 函数的回调中返回的连线走向信息中,将其描述为一条二次方曲线就可以了。说得有些绕,我们来看看代码实现吧。

ht.Default.setEdgeType('custom', function(edge, gap, graphView, sameSourceWithFirstEdge){ var sourcePoint = edge.getSourceAgent().getPosition(), targetPoint = edge.getTargetAgent().getPosition(), points = new ht.List();  points.add(sourcePoint); points.add({ x: (sourcePoint.x + targetPoint.x)/2,  y: (sourcePoint.y + targetPoint.y)/2 + 300 });  points.add(targetPoint);  return { points: points, segments: new ht.List([1, 3]) }; });

从代码中可以看出,返回到顶点是连线的起点和终点,还有中间的二次方曲线的控制点,还有设置了顶点的连线方式,就是在 return 中的 segments,1 代表是路径的起点,3 代表的是二次方曲线,这些相关知识点在 HT for Web 的形状手册中描述得很清楚,不懂的可以转到手册详细了解,http://www.hightopo.com/guide/guide/core/shape/ht-shape-guide.html。

前面的废话太多了,下面就是我们今天的主要内容了,看看如何通过自定义 EdgeType 来实现总线的效果。

上图就是一个总线的简单例子,所有的节点都通过线条链接黑色的总线,连线的走向都是节点到总线的最短距离。

来讲讲整体的设计思路吧,其实总的来说,就是点的线的垂直点计算问题。那么问题来了,碰到曲线怎么办?其实曲线也是可以微分成线条来处理的,至于这个线段的划分精细度就需要用户来自定义了。

EdgeType 结合 ShapeLayout 实现均匀自动布局:

但是,像上图所示的椭圆形总线该如何处理呢?对于这种有固定表达式的形状,我们就不需要用曲线分割的方法来做总线布局了,我们完全可以获取到圆或者椭圆上的一点,所以在处理圆和椭圆上,我们获取 Edge 连边节点中线连成线,然后计算出夹角,通过圆或者椭圆的三角函数表示法计算出总线上的一点,这样来构成连线。

在上图中,我们用到了 ShapeLayout 来自动布局和总线相连的节点,让其相对均匀地分布在总线周围,对于 ShapeLayout 的相关设计思路我们在后面的章节中再具体介绍。

那么我们今天的内容就到这里了,对于总线的设计是不是很简单呢,下面附上总线的所有代码,有需要的话,可以直接复制出来,在页面中引入 HT for Web 的核心包 ht.js 后面引入以下代码就可以直接使用总线功能了。代码不多,也就将近 200 行,感兴趣的朋友可以通读一遍,有什么问题,还请不吝赐教。

;(function(window, ht) { var getPoint = function(node, outPoint) { var rect = node.getRect(), pos = node.getPosition(), p = ht.Default.intersectionLineRect(pos, outPoint, rect); if (p) return { x: p[0], y: p[1] }; return pos; }; var pointToInsideLine = function(p1, p2, p) { var x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y, x = p.x, y = p.y, result = {}, dx = x2 - x1, dy = y2 - y1, d = Math.sqrt(dx * dx + dy * dy), ca = dx / d, // cosine sa = dy / d, // sine mX = (-x1 + x) * ca + (-y1 + y) * sa; result.x = x1 + mX * ca; result.y = y1 + mX * sa;  if (!isPointInLine(result, p1, p2)) { result.x = Math.abs(result.x - p1.x) < Math.abs(result.x - p2.x) ? p1.x : p2.x; result.y = Math.abs(result.y - p1.y) < Math.abs(result.y - p2.y) ? p1.y : p2.y; } dx = x - result.x; dy = y - result.y; result.z = Math.sqrt(dx * dx + dy * dy); return result; }; var isPointInLine = function(p, p1, p2) { return p.x >= Math.min(p1.x, p2.x) && p.x <= Math.max(p1.x, p2.x) && p.y >= Math.min(p1.y, p2.y) && p.y <= Math.max(p1.y, p2.y); }; var bezier2 = function(t, p0, p1, p2) { var t1 = 1 - t; return t1*t1*p0 + 2*t*t1*p1 + t*t*p2; }; var bezier3 = function(t, p0, p1, p2, p3 ) { var t1 = 1 - t; return t1*t1*t1*p0 + 3*t1*t1*t*p1 + 3*t1*t*t*p2 + t*t*t*p3; }; var distance = function(p1, p2) { var dx = p2.x - p1.x, dy = p2.y - p1.y; return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); }; var getPointWithLength = function(length, p1, p2) { var dis = distance(p1, p2), temp = length / dis, dx = p2.x - p1.x, dy = p2.y - p1.y; return { x: p1.x + dx * temp, y: p1.y + dy * temp }; }; var getPointInOval = function(l, r, p1, p2) { var a = Math.atan2(p2.y - p1.y, p2.x - p1.x); return { x: l * Math.cos(a) + p1.x, y: r * Math.sin(a) + p1.y }; }; ht.Default.setEdgeType('bus', function(edge, gap, graphView, sameSourceWithFirstEdge) { var source = edge.getSourceAgent(), target = edge.getTargetAgent(), shapeList = ['circle', 'oval'], shape, beginNode, endNode;  if (shapeList.indexOf(source.s('shape')) >= 0) { shape = source.s('shape'); beginNode = source; endNode = target; } else if (shapeList.indexOf(target.s('shape')) >= 0) { shape = target.s('shape'); beginNode = target; endNode = source; }  if (shapeList.indexOf(shape) >= 0) { var w = beginNode.getWidth(), h = beginNode.getHeight(), l = Math.max(w, h) / 2, r = Math.min(w, h) / 2; if (shape === 'circle') l = r = Math.min(l, r); var p = getPointInOval(l, r, beginNode.getPosition(), endNode.getPosition()); return { points: new ht.List([ p, getPoint(endNode, p) ]), segments: new ht.List([ 1, 2 ]) }; }  var segments, points, endPoint; if (source instanceof ht.Shape) { segments = source.getSegments(); points = source.getPoints(); beginNode = source; endPoint = target.getPosition(); endNode = target; } else if (target instanceof ht.Shape) { segments = target.getSegments(); points = target.getPoints(); beginNode = target; endPoint = source.getPosition(); endNode = source; }  if (!points) { return { points: new ht.List([ getPoint(source, target.getPosition()), getPoint(target, source.getPosition()) ]), segments: new ht.List([ 1, 2 ]) }; }  if (!segments && points) { segments = new ht.List(); points.each(function() { segments.add(2); }); segments.set(0, 1); }  var segLen = segments.size(), segV, segNextV, beginPoint, j, p1, p2, p3, p4, p, tP1, tP2, tRes, curveResolution = beginNode.a('edge.curve.resolution') || 50, pointsIndex = 0; for (var i = 0; i < segLen - 1; i++) { segNextV = segments.get(i + 1); if (segNextV === 1) { pointsIndex++; continue; }  p1 = points.get(pointsIndex++);  if (segNextV === 2 || segNextV === 5) { p2 = points.get((segNextV === 5) ? 0 : pointsIndex); p = pointToInsideLine(p1, p2, endPoint); if (!beginPoint || beginPoint.z > p.z) beginPoint = p; } else if (segNextV === 3) { p2 = points.get(pointsIndex++); p3 = points.get(pointsIndex); tP2 = { x: p1.x, y: p1.y }; for (j = 1; j <= curveResolution; j++) { tP1 = tP2; tRes = j / curveResolution; tP2 = { x: bezier2(tRes, p1.x, p2.x, p3.x), y: bezier2(tRes, p1.y, p2.y, p3.y), }; p = pointToInsideLine(tP1, tP2, endPoint); if (!beginPoint || beginPoint.z > p.z) beginPoint = p; } } else if (segNextV === 4) { p2 = points.get(pointsIndex++); p3 = points.get(pointsIndex++); p4 = points.get(pointsIndex); tP2 = { x: p1.x, y: p1.y }; for (j = 1; j <= curveResolution; j++) { tP1 = tP2; tRes = j / curveResolution; tP2 = { x: bezier3(tRes, p1.x, p2.x, p3.x, p4.x), y: bezier3(tRes, p1.y, p2.y, p3.y, p4.y), }; p = pointToInsideLine(tP1, tP2, endPoint); if (!beginPoint || beginPoint.z > p.z) beginPoint = p; } } } endPoint = getPoint(endNode, beginPoint); return { points: new ht.List([ { x: beginPoint.x, y: beginPoint.y }, endPoint ]), segments: new ht.List([ 1, 2 ]) }; });}(window, ht));

自动生成网络拓扑图开源_电信网络拓扑图自动布局之总线相关推荐

  1. 网络监控工具 开源_在不到一个月的时间内,我如何使用开源工具成为网络漫画家

    网络监控工具 开源 如果您正在阅读本文,那么您可能会关心或至少对开放源代码感兴趣. 像您一样,我对开源也很感兴趣. 也许与您不同,我也是网络漫画家. 您可以在Herpaderp.party中找到我的作 ...

  2. 网络监控工具 开源_排名前5位的开源网络监控工具

    网络监控工具 开源 维持活动的网络是系统管理员最重要的任务之一,并且对连接的系统保持警惕对于保持网络的最佳状态至关重要. 有许多不同的方法可以使标签保持在现代网络上. 网络监视工具专门用于监视网络流量 ...

  3. dcn网络与公网_电信运营商DCN网络的演变与规划方法(The evolution and plan method of DCN)...

    摘要:随着电信业务的发展和电信企业经营方式的转变,DCN网络的定位发生了重大的演变.本文基于这种变化,重点讨论DCN网络的规划方法和运维管理方法. Digest: With the developme ...

  4. 网络日志管理_企业网络日志对具体对网络安全维护有哪些帮助?

    网络日志管理对企业网络健康发展非常重要,众所周知,日志是判定企业网络中各类用户行为的重要依据,其不仅能排查出各类网络漏洞,还对企业网络合规性审计有很大帮助.因此网络日志的采集.分析.归档.留存成为目前 ...

  5. excel自动生成舒尔特表_财务总监:超完美Excel全套账财务系统,自动生成报表,收好喽...

    对于很多初学会计的小伙伴来说,很多账务不知该如何处理,三大会计报表也不知道该怎么做.或者是新手会计处理账务经常熬夜加班. 今天,小编给大家分享一套超级实用的Excel全套账财务系统,可以自动生成三大财 ...

  6. cnpm 网络不能连接_移动站网络的另一种方式——移动站手机网络模式

    现在用CORS网络越来越多,特别是现在2000坐标推广使用以来,用网络CORS的方便性就越来越直观了. 用网络卡接收数据当然是要有网络信号,但是很多客户的仪器网络模块制式还是老的模块,只支持移动2G网 ...

  7. 自动生成网络拓扑图开源_为视频自动生成字幕,一款神奇的开源工具!

    在之前的文章中老Y给大家推荐过一款视频实时翻译软件,文章如下: 老司机们最想要的神器!我点开了隐藏文件夹-- 虽然不少同学发来贺电,说终于能稍稍看懂那些年保留下来的日本电影了~老Y为这些同学感到高兴, ...

  8. 玩转Keras之Seq2Seq自动生成标题 | 附开源代码

    作者丨苏剑林 单位丨广州火焰信息科技有限公司 研究方向丨NLP,神经网络 个人主页丨kexue.fm 话说自称搞了这么久的 NLP,我都还没有真正跑过 NLP 与深度学习结合的经典之作--Seq2Se ...

  9. 网络研讨室_免费网络研讨会:Java应用程序中的吞咽异常

    网络研讨室 1月30日参加我们的网络研讨会,以发现Java应用程序中的"隐藏"异常. 如果一棵树落在森林中,但是没有写到原木上,它会发出声音吗? 答案是肯定的. 这些类型的错误可能 ...

  10. 普度网络营销策划_普度网络营销策划-齐宁_新浪博客

    标签: 杂谈 自媒体的盛行,让我们知道了文案的重要性.一条好的文案,可以让你的广告如虎添翼.一篇好的内容可以瞬间刷爆朋友圈.今天我要总结的就是如何做一个打动人内心心智的好文案. 文案最主要的目的是让消 ...

最新文章

  1. ++和--操作符分析
  2. leetcode 203 移除链表元素 C++
  3. java对字符串归一化_搜索引擎中的字符串归一化 | 学步园
  4. html5与python哪个好_3个原因告诉你,为什么选择HTML5大前端?
  5. 蓝天采集系统的安装和遇到的问题及解决方案
  6. mysql 跳过checksum_MySQL-Utilities:mysqldbcompare及跳过复制错误
  7. 最近学到一些linq和面向对象的经验分享
  8. Jquery 数组操作(转)
  9. 并联串联混合的电压和电流_16.2 串、并联电路中电压的规律
  10. Editor: 维护一个整数编辑器 HDOJ4699
  11. shopxp商城系统对接个人支付宝免签h5支付
  12. 算法第3章上机实践报告
  13. P3398 仓鼠找sugar(LCA,树剖)
  14. 如何利用“跑腿系统”来提高生活效率?
  15. JDBC 第一章 JDBC概述
  16. tomcat更改默认端口
  17. JVM调优简单梳理,一分钟了解JVM调优
  18. 下载xcode 10
  19. 咖啡色的羊驼学习Golang
  20. java进程、线程知识扩充

热门文章

  1. LeetCode_859_python_亲密字符串
  2. 数据结构——结构体总结
  3. 【狂神说Redis】1NoSQL概述 1-1为什么使用NoSQL
  4. laravel input值必须不等于0_【第十一期】实现 Javascript 版本的 Laravel 风格参数验证器...
  5. String.format(“0:D2}“,a)字符串格式化
  6. 莞工计算机学院杀手,莞工oj第40题神奇的fans
  7. Java学习路线图,知识点总结大全图,从入门到精通,你要好好学习Java,千万不能只学习python
  8. layui表格合并单元格多表_layui合并单元格各种办法
  9. prototype.js学习(1)
  10. Python--day45--pymysql模块初识以及SQL注入