这个例子原理是这样的,将连线 Edge 设置成透明不可见的,然后针对每个 Edge 对应一个 Node 节点,这个节点的形状就是被拉伸并定位到连线位置替代连线来显示,而 Node 图形在还没拉伸之前长得如下:
这里还有个细节是通过 createMatrix 函数,为每个管线设置一个指向两节点位置的矩阵坐标变换参数到 style 的 mat 属性上,矩阵预算不理解也没关系,直接照抄例子中代码即可,为了方便大家理解我搞了个两个节点一条连线更简单的例子供参考:
ht.Default.setShape3dModel(
'custom', ht.Default.createRingModel(
[0.5, 0.5, -0.2, 0, 0.5, -0.5], [1, 3]
)
);
var colorList = ['#FFAFA4', '#B887C5', '#B9EA9C', '#CFD9E7', '#4590B8', '#FF9C30'],
colorLen = colorList.length;
var randomColor = function() {
var ran = Math.random() * colorLen;
return colorList[Math.floor(ran)];
};
var init = function() {
var dm = new ht.DataModel(),
g3d = window.g3d = new ht.graph3d.Graph3dView(dm);
g3d.getBrightness = function() { return null; };
g3d.isMovable = function(node) { return node.s('shape3d') !== 'custom'; };
g3d.addToDOM();
var edgeList = initDataModel(dm),
forceLayout = new ht.layout.Force3dLayout(g3d);
forceLayout.onRelaxed = function() {
edgeList.forEach(updatePipeline);
};
forceLayout.start();
initFormPane(g3d);
};
var initDataModel = function(dm) {
var root = createNode(dm),
iNode, jNode, j,
edgeList = [];
for (var i = 0; i < 3; i++) {
iNode = createNode(dm);
edgeList.push(createEdge(dm, root, iNode));
for (j = 0; j < 3; j++) {
jNode = createNode(dm);
edgeList.push(createEdge(dm, iNode, jNode));
}
}
return edgeList;
};
var createNode = function(dm) {
var node = new ht.Node();
node.s({
'shape3d': 'sphere',
'shape3d.color': randomColor()
});
node.s3(40, 40, 40);
dm.add(node);
return node;
};
var createEdge = function(dm, node1, node2) {
var node = new ht.Node();
node.s({
'shape3d': 'custom',
'shape3d.color': '#ECE0D4',
'layoutable': false
});
dm.add(node);
var edge = new ht.Edge(node1, node2);
edge.a('pipeline', node);
edge.s('edge.color', 'rgba(0, 0, 0, 0)');
dm.add(edge);
return edge;
};
var updatePipeline = function(edge) {
var pipeline = edge.a('pipeline');
pipeline.s3(1, 1, 1);
pipeline.p3(0, 0, 0);
var node1 = edge.getSourceAgent(),
node2 = edge.getTargetAgent();
pipeline.s('mat', createMatrix(node1.p3(), node2.p3(), 20));
};
var createMatrix = function(p1, p2, width) {
var vec = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]],
dist = ht.Default.getDistance(p1, p2);
return ht.Default.createMatrix({
s3: [width, dist, width],
r3: [Math.PI/2 - Math.asin(vec[1]/dist), Math.atan2(vec[0], vec[2]), 0],
rotationMode: 'xyz',
t3: [(p1[0]+p2[0])/2, (p1[1]+p2[1])/2, (p1[2]+p2[2])/2]
});
};
var initFormPane = function(g3d) {
var formPane = new ht.widget.FormPane();
formPane.setWidth(230);
formPane.setHeight(125);
formPane.addToDOM();
var view = formPane.getView();
view.style.background = 'rgba(186, 186, 186, 0.7)';
view.style.top = '10px';
view.style.left = 'auto';
view.style.right = '7px';
formPane.addRow([{ element: 'Headlight:', font: 'bold 12px arial, sans-serif' }, {
id: 'disable',
checkBox: {
label: 'disable',
value: g3d.isHeadlightDisabled(),
onValueChanged: function(oV, nV) {
g3d.setHeadlightDisabled(nV);
}
}
}], [70, 0.1]);
formPane.addRow([], [0.1], 1.01, {background: '#43AFF1'});
['Color', 'Range', 'Intensity'].forEach(function(name) {
var obj = { id: name },
func = function(oV, nV) {
g3d['setHeadlight' + name](nV);
};
if (name === 'Color')
obj.colorPicker = {
instant: true,
value: g3d['getHeadlight' + name](),
onValueChanged: func
};
else
obj.slider = {
min: 0,
max: name === 'Range' ? 20000 : 3,
step: 0.1,
value: g3d['getHeadlight' + name](),
onValueChanged: func
};
formPane.addRow([ name, obj ], [ 70, 0.1 ]);
});
};