Ok, here’s a script that should work to convert old segmentOffset values to the same corresponding position in 2.1:
function convertSegmentOffsets(links) {
links.each(function(l) {
var oldPoint = midPointOneEight(l);
var newPoint = l.midPoint;
var oldAngle = normalizeAngle(midAngleOneEight(l));
var newAngle = normalizeAngle(l.midAngle);
console.log("Midpoints - old: " + oldPoint + ", new: " + newPoint);
console.log("Midangles - old: " + oldAngle + ", new: " + newAngle);
if (l.data.xOff !== undefined && l.data.yOff !== undefined) {
l.diagram.model.commit(function(m) {
var x = l.data.xOff;
var y = l.data.yOff;
var t = (newAngle - oldAngle) * Math.PI / 180 * -1;
var newXOffset = x * Math.cos(t) - y * Math.sin(t) + (newPoint.x - oldPoint.x);
var newYOffset = x * Math.sin(t) + y * Math.cos(t) + (newPoint.y - oldPoint.y);
m.set(l.data, "xOff", newXOffset);
m.set(l.data, "yOff", newYOffset);
}, null);
}
});
}
function normalizeAngle(a) {
a = a % 360;
if (a < 0) a += 360;
return a;
}
function midPointOneEight(l) {
var result = new go.Point();
var numpts = l.pointsCount;
if (numpts === 0) { // no point means call UpdatePoints/ComputeMidPoint again
result.setTo(NaN, NaN);
return result;
} else if (numpts === 1) {
result.set(l.getPoint(0));
return result;
} else if (numpts === 2) {
var a = l.getPoint(0);
var b = l.getPoint(1);
result.setTo((a.x + b.x) / 2, (a.y + b.y) / 2);
return result;
}
if (l.computeCurve() === go.Link.Bezier && numpts >= 3 && !l.isOrthogonal) {
if (numpts === 3) return l.getPoint(1);
var numsegs = ((numpts - 1) / 3) | 0;
var idx = ((numsegs / 2) | 0) * 3;
if (numsegs % 2 === 1) {
var a = l.getPoint(idx);
var b = l.getPoint(idx + 1);
var c = l.getPoint(idx + 2);
var d = l.getPoint(idx + 3);
bezierMidPoint(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y, result);
return result;
} else {
result.set(l.getPoint(idx));
return result;
}
}
var distanceAlongRoute = 0.0;
var segmentDistanceQueue = [];
for (var i = 0; i < numpts - 1; i++) {
var segmentDistance = 0.0;
var a = l.getPoint(i);
var b = l.getPoint(i + 1);
//Handle computationally simple Orthogonal cases
if (isApprox(a.x, b.x)) {
segmentDistance = b.y - a.y;
if (segmentDistance < 0) segmentDistance = -segmentDistance;
segmentDistanceQueue.push(segmentDistance);
distanceAlongRoute += segmentDistance;
} else if (isApprox(a.y, b.y)) {
segmentDistance = b.x - a.x;
if (segmentDistance < 0) segmentDistance = -segmentDistance;
segmentDistanceQueue.push(segmentDistance);
distanceAlongRoute += segmentDistance;
} else {
//Handle non-Orthogonal lines
segmentDistance = Math.sqrt(a.distanceSquaredPoint(b));
segmentDistanceQueue.push(segmentDistance);
distanceAlongRoute += segmentDistance;
}
}
var currentDistance = 0.0;
var currentPointIndex = 0;
var nextSegmentLength = 0.0;
while (currentDistance < distanceAlongRoute / 2 && currentPointIndex < numpts) {
nextSegmentLength = segmentDistanceQueue[currentPointIndex];
if (currentDistance + nextSegmentLength > distanceAlongRoute / 2) break;
currentDistance += nextSegmentLength;
currentPointIndex++;
}
var currentPoint = l.getPoint(currentPointIndex);
var nextPoint = l.getPoint(currentPointIndex + 1);
// Handle Orthogonal cases first
if (currentPoint.x === nextPoint.x) {
if (currentPoint.y > nextPoint.y)
result.setTo(currentPoint.x, currentPoint.y - (distanceAlongRoute / 2 - currentDistance));
else
result.setTo(currentPoint.x, currentPoint.y + (distanceAlongRoute / 2 - currentDistance));
} else if (currentPoint.y === nextPoint.y) {
if (currentPoint.x > nextPoint.x)
result.setTo(currentPoint.x - (distanceAlongRoute / 2 - currentDistance), currentPoint.y);
else
result.setTo(currentPoint.x + (distanceAlongRoute / 2 - currentDistance), currentPoint.y);
} else { // Handle non-Orthogonal lines
var similarTriangleRatio = (distanceAlongRoute / 2 - currentDistance) / nextSegmentLength;
var dx = similarTriangleRatio * (nextPoint.x - currentPoint.x);
var dy = similarTriangleRatio * (nextPoint.y - currentPoint.y);
result.setTo(currentPoint.x + dx, currentPoint.y + dy);
}
return result;
}
function midAngleOneEight(l) {
var numpts = l.pointsCount;
if (numpts < 2) return NaN; // no angle means call ComputeMidAngle again
if (l.computeCurve() === go.Link.Bezier && numpts >= 4 && !l.isOrthogonal) {
var numsegs = ((numpts - 1) / 3) | 0;
var idx = ((numsegs / 2) | 0) * 3;
if (numsegs % 2 === 1) {
idx = Math.floor(idx);
var a = l.getPoint(idx);
var b = l.getPoint(idx + 1);
var c = l.getPoint(idx + 2);
var d = l.getPoint(idx + 3);
return bezierMidAngle(a.x, a.y, b.x, b.y, c.x, c.y, d.x, d.y);
} else {
if (idx > 0 && idx + 1 < numpts) {
var a = l.getPoint(idx - 1);
var b = l.getPoint(idx + 1);
return a.directionPoint(b);
}
// else drop through and treat as a non-Bezier-curve
}
}
var midEnd = (numpts / 2) | 0;
if (numpts % 2 === 0) { // even number of points means odd number of segments
// get the middle segment (perhaps the only one)
var a = l.getPoint(midEnd - 1);
var b = l.getPoint(midEnd);
// if both of these segment points are the same, treat both as a single point and check either side
if (numpts >= 4 && pointEqualsApprox(a, b)) {
a = l.getPoint(midEnd - 2);
var c = l.getPoint(midEnd + 1);
var d1 = a.distanceSquaredPoint(b);
var d2 = b.distanceSquaredPoint(c);
if (d1 > d2+10)
return a.directionPoint(b);
else if (d2 > d1+10)
return b.directionPoint(c);
else
return a.directionPoint(c);
}
if (l.geometry !== null && !l.isOrthogonal)
return getAngleAlongPath(l.geometry, 0.5);
return a.directionPoint(b);
} else {
if (l.geometry !== null && !l.isOrthogonal)
return getAngleAlongPath(l.geometry, 0.5);
// also find the points on either side of the middle point
// then figure out if one segment is much longer than the other
var a = l.getPoint(midEnd - 1);
var b = l.getPoint(midEnd);
var c = l.getPoint(midEnd + 1);
var d1 = a.distanceSquaredPoint(b);
var d2 = b.distanceSquaredPoint(c);
if (d1 > d2+10)
return a.directionPoint(b);
else if (d2 > d1+10)
return b.directionPoint(c);
else
return a.directionPoint(c);
}
}
function bezierMidPoint(sx, sy, c1x, c1y, c2x, c2y, ex, ey, midp) {
var m1x = (sx + c1x) / 2;
var m1y = (sy + c1y) / 2;
var m2x = (c1x + c2x) / 2;
var m2y = (c1y + c2y) / 2;
var m3x = (c2x + ex) / 2;
var m3y = (c2y + ey) / 2;
var vx = (m1x + m2x) / 2;
var vy = (m1y + m2y) / 2;
var wx = (m2x + m3x) / 2;
var wy = (m2y + m3y) / 2;
midp.x = (vx + wx) / 2;
midp.y = (vy + wy) / 2;
return midp;
}
function bezierMidAngle(sx, sy, c1x, c1y, c2x, c2y, ex, ey) {
var m1x = (sx + c1x) / 2;
var m1y = (sy + c1y) / 2;
var m2x = (c1x + c2x) / 2;
var m2y = (c1y + c2y) / 2;
var m3x = (c2x + ex) / 2;
var m3y = (c2y + ey) / 2;
var vx = (m1x + m2x) / 2;
var vy = (m1y + m2y) / 2;
var wx = (m2x + m3x) / 2;
var wy = (m2y + m3y) / 2;
return go.Point.direction(vx, vy, wx, wy);
}
function pointEqualsApprox(a, b) {
return isApprox(a.x, b.x) && isApprox(a.y, b.y);
}
function isApprox(x, y) {
var d = x - y;
return d < 0.5 && d > -0.5;
}
function getAngleAlongPath(geo, fraction) {
if (fraction < 0) fraction = 0.0;
else if (fraction > 1) fraction = 1.0;
var result = 0;
if (geo.type === go.Geometry.Line) {
result = Math.atan2(geo.endY - geo.startY, geo.endX - geo.startX) * 180 / Math.PI;
return result;
}
// build allPaths array if needed
var allPaths = geo.flattenedSegments;
var allDists = geo.flattenedLengths;
var totalDist = geo.flattenedTotalLength;
var l = allPaths.length;
var fractionalDist = totalDist * fraction;
var currentDist = 0.0;
for (var i = 0; i < l; i++) {
var dists = allDists[i];
var ll = dists.length;
for (var j = 0; j < ll; j++) {
var dist = dists[j];
if (currentDist + dist >= fractionalDist) {
var paths = allPaths[i];
var lastptx = paths[j * 2];
var lastpty = paths[j * 2 + 1];
var ptx = paths[j * 2 + 2];
var pty = paths[j * 2 + 3];
result = Math.atan2(pty - lastpty, ptx - lastptx) * 180 / Math.PI;
return result;
}
currentDist += dist;
}
}
// only if there was no path on the Geometry, shouldn't be hit
return NaN;
}
To use the script, you should call convertSegmentOffsets
with the full set of links in the diagram. The midPointOneEight and midAngleOneEight functions give the values that get computed in 1.8, so you can use those if for some reason the script doesn’t work as expected.