# 电信网络拓扑图自动布局之曲线布局

ShapeLayout 从字面上的意思理解，就是根据曲线路径来布局节点，省去手动布局节点的繁琐操作，还能保证平滑整齐地排布，这是手动调整很难做到的。ShapeLayout 结合前面提到的总线，是最普遍的应用。

http://www.hightopo.com/demo/EdgeType/ShapeLayout-Oval.html

var radians = Math.PI * 2 / nodeCount,
w = width / 2,
h = height / 2,
a = Math.max(w, h),
b = Math.min(w, h),
if (shape === 'circle') a = b = Math.min(a, b);
for (var i = 0; i < nodeCount; i++) {
x = a * Math.cos(rad) + position.x + offset.x;
y = b * Math.sin(rad) + position.y + offset.y;
node = this._nodes[i];
if (!node) continue;
if (!anim)
node.setPosition({ x: x, y: y });
else {
anim.action = function(pBegin, pEnd, v) {
this.setPosition({
x: pBegin.x + (pEnd.x - pBegin.x) * v,
y: pBegin.y + (pEnd.y - pBegin.y) * v
});
}.bind(node, node.getPosition(), { x: x, y: y });
ht.Default.startAnim(anim);
}
}

http://www.hightopo.com/demo/EdgeType/ShapeLayout.html

preP = beginP;
var nodeIndex = 0, indexLength, node;
for (; i < pointsCount;) {
p = this._calculationPoints[i];
indexLength = padding + resolution * nodeIndex;
if (p.totalLength < indexLength) {
preP = p;
i++;
continue;
}
node = this._nodes[nodeIndex++];
if (!node) break;

dis = indexLength - preP.totalLength;
tP = getPointWithLength(dis, preP.point, p.point);

p = {
x: tP.x + position.x + offset.x - width / 2,
y: tP.y + position.y + offset.y - height / 2
};
if (!anim)
node.setPosition(p);
else {
anim.action = function(pBegin, pEnd, v) {
this.setPosition({
x: pBegin.x + (pEnd.x - pBegin.x) * v,
y: pBegin.y + (pEnd.y - pBegin.y) * v
});
}.bind(node, node.getPosition(), p);
ht.Default.startAnim(anim);
}

preP = {
point: tP,
distance: dis,
totalLength: indexLength
};
}

;(function(window, ht) {
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 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 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 ShapeLayout = ht.ShapeLayout = function() {};

ht.Default.def('ht.ShapeLayout', Object, {
ms_fire: 1,
ms_ac: ['padding', 'offset', 'shape', 'closePath', 'position', 'width', 'height'],

calculationSize: function() {
if (!this._points) return;
var min = { x: Infinity, y: Infinity},
max = { x: -Infinity, y: -Infinity},
p, len = this._points.length;
for (var i = 0; i < len; i++) {
p = this._points[i];
min.x = Math.min(min.x, p.x);
min.y = Math.min(min.y, p.y);
max.x = Math.max(max.x, p.x);
max.y = Math.max(max.y, p.y);
}
this._width = max.x - min.x;
this._height = max.y - min.y;
this._position = {
x: min.x + this._width / 2,
y: min.y + this._height / 2
};
},

_points: null,
getPoints: function() { return this._points; },
setPoints: function(value) {
if (value instanceof Array)
this._points = value.slice(0);
else if (value instanceof ht.List)
this._points = value._as.slice(0);
else
this._points = null;
this.__calcuPoints = !!this._points;

this.calculationSize();
},

_segments: null,
getSegments: function() { return this._segments; },
setSegments: function(value) {
if (value instanceof Array)
this._segments = value.slice(0);
else if (value instanceof ht.List)
this._segments = value._as.slice(0);
else
this._segments = null;
this.__calcuPoints = !!this._segments;
},

_style: {},
s: function() {
return this.setStyle.apply(this, arguments);
},
setStyle: function() {
var name = arguments[0],
value = arguments[1];
if (arguments.length === 1) {
if (typeof name === 'object'){
for (var n in name)
this._style[n] = name[n];
}
else
return this._style[name];
}
else
this._style[name] = value;
},

_nodes: null,
getNodes: function() { return this._nodes; },
setNodes: function(value) {
if (value instanceof Array)
this._nodes = value.slice(0);
else if (value instanceof ht.List)
this._nodes = value._as.slice(0);
else
this._nodes = null;
},
if (!this._nodes) this._nodes = [];
this._nodes.push(node);
},

_calculationPoints: [],
splitPoints: function() {
if (!this._points || this._points.length === 0) {
return;
}

var points = this._points.slice(0),
segments;
if (!this._segments || this._segments.length === 0) {
segments = points.map(function(p, index) { return 2; });
segments[0] = 1;
}
else {
segments = this._segments.slice(0);
}

this._calculationPoints.length = 0;
var beginPoint = points[0],
preP = {
point: { x: beginPoint.x, y: beginPoint.y },
distance: 0,
totalLength: 0
};
this._calculationPoints.push(preP);
var length = segments.length,
pointIndex = 1, seg, p, tP, dis,
p0, p1, p2, p3, j,
curveResolution = this.s('curve.resolution') || 50;

var calcuPoints = function(currP) {
dis = distance(preP.point, currP);
p = {
point: { x: currP.x, y: currP.y },
distance: dis,
totalLength: preP.totalLength + dis
};
this._calculationPoints.push(p);
preP = p;
}.bind(this);
for (var i = 1; i < length; i++) {
seg = segments[i];
if (seg === 1) {
tP = points[pointIndex++];
p = {
point: { x: tP.x, y: tP.y },
distance: 0,
totalLength: preP.totalLength
};
this._calculationPoints.push(p);
preP = p;
}
else if (seg === 2) { calcuPoints(points[pointIndex++]); }
else if (seg === 3) {
p1 = points[pointIndex++];
p2 = points[pointIndex++];
p0 = preP.point;
for (j = 1; j <= curveResolution; j++) {
tP = {
x: bezier2(j / curveResolution, p0.x, p1.x, p2.x),
y: bezier2(j / curveResolution, p0.y, p1.y, p2.y)
};
calcuPoints(tP);
}
}
else if (seg === 4) {
p1 = points[pointIndex++];
p2 = points[pointIndex++];
p3 = points[pointIndex++];
p0 = preP.point;
for (j = 1; j <= curveResolution; j++) {
tP = {
x: bezier3(j / curveResolution, p0.x, p1.x, p2.x, p3.x),
y: bezier3(j / curveResolution, p0.y, p1.y, p2.y, p3.y)
};
calcuPoints(tP);
}
}
else if (seg === 5) {
tP = this._calculationPoints[0].point;
calcuPoints(tP);
}
}
this._totalLength = preP.totalLength;
},

layout: function(anim) {
if (!this._nodes || this._nodes.length === 0) {
return;
}

var nodeCount = this._nodes.length,
shape = this._shape,
shapeList = ['circle', 'oval'],
offset = this._offset || { x: 0, y: 0 },
position = this._position || { x: 0, y: 0 },
width = this._width || 0,
height = this._height || 0;
if (shape && shapeList.indexOf(shape) >= 0) {
var radians = Math.PI * 2 / nodeCount,
w = width / 2,
h = height / 2,
a = Math.max(w, h),
b = Math.min(w, h),
if (shape === 'circle') a = b = Math.min(a, b);
for (var i = 0; i < nodeCount; i++) {
x = a * Math.cos(rad) + position.x + offset.x;
y = b * Math.sin(rad) + position.y + offset.y;
node = this._nodes[i];
if (!node) continue;
if (!anim)
node.setPosition({ x: x, y: y });
else {
anim.action = function(pBegin, pEnd, v) {
this.setPosition({
x: pBegin.x + (pEnd.x - pBegin.x) * v,
y: pBegin.y + (pEnd.y - pBegin.y) * v
});
}.bind(node, node.getPosition(), { x: x, y: y });
ht.Default.startAnim(anim);
}
}
return;
}

if (!this._calculationPoints || this.__calcuPoints)
this.splitPoints();

length = this._totalLength - 2 * padding,
resolution = length / (nodeCount - (this._closePath ? 0 : 1)),
i = 1, p, preP, beginP, dis,
pointsCount = this._calculationPoints.length;
for (; i < pointsCount; i++) {
p = this._calculationPoints[i];
preP = this._calculationPoints[i - 1];
beginP = {
point: getPointWithLength(dis, preP.point, p.point),
distance: p.distance - dis,
};
break;
}

preP = beginP;
var nodeIndex = 0, indexLength, node;
for (; i < pointsCount;) {
p = this._calculationPoints[i];
indexLength = padding + resolution * nodeIndex;
if (p.totalLength < indexLength) {
preP = p;
i++;
continue;
}
node = this._nodes[nodeIndex++];
if (!node) break;

dis = indexLength - preP.totalLength;
tP = getPointWithLength(dis, preP.point, p.point);

p = {
x: tP.x + position.x + offset.x - width / 2,
y: tP.y + position.y + offset.y - height / 2
};
if (!anim)
node.setPosition(p);
else {
anim.action = function(pBegin, pEnd, v) {
this.setPosition({
x: pBegin.x + (pEnd.x - pBegin.x) * v,
y: pBegin.y + (pEnd.y - pBegin.y) * v
});
}.bind(node, node.getPosition(), p);
ht.Default.startAnim(anim);
}

preP = {
point: tP,
distance: dis,
totalLength: indexLength
};
}
}
});
}(window, ht));

162 篇文章51 人订阅

0 条评论

## 相关文章

### gan生成图像at 1024² 的 代码 论文

https://github.com/tkarras/progressive_growing_of_gans

792

1744

### OpenPose: Keypoint Detection And Multi-Threading C++ Library

https://github.com/CMU-Perceptual-Computing-Lab/openpose

5244

1931

3407

1552

2245

2287

### Auto-Encoding GAN

Mihaela Rosca, Balaji Lakshminarayanan, David Warde-Farley, Shakir Mohamed

871

2736