Optimize path distance from circle calculation in

2019-08-18 18:38发布

问题:

In this D3.js graph I'm using the magic numbers 20 when the line is straight and 30 when the line is at an angle as the margin to withdraw from the path in order to add a margin between the circles (graph nodes) and the paths (graph edges).

I'm also using the fairly random number 1.5 as a multiplier to further the distance from the circle of the path's end, since it has a marker-end attached to it.

Given the circle radius of 19 and the arrow path coordinates M0,0 V4 L2,2 Z, I assume it should be possible to calculate the exact distance ("margin") the paths should have from the circles, but my math skills just aren't up for it. I also assume it's possible to figure out the direction of the paths so the margin inversion is no longer required.

If you have ideas on how the distance and margin calculation within the withMargins() function can be optimized, please provide an answer. All optimizations and code reductions are welcome. Here's the relevant code:

this.withMargins = function() {
    var diff = {
        x: edge.end.x - edge.start.x,
        y: edge.end.y - edge.start.y
    };

    var margins = {
        start: {
            x: 0,
            y: 0
        },
        end: {
            x: 0,
            y: 0
        }
    };

    if (diff.x > 0 && diff.y === 0) {
        margins.start.x = 30;
    } else if (diff.x < 0 && diff.y === 0) {
        margins.start.x = -30;
    } else if (diff.x > 0) {
        margins.start.x = 20;
    } else if (diff.x < 0) {
        margins.start.x = -20;
    }

    if (diff.y > 0 && diff.x === 0) {
        margins.start.y = 30;
    } else if (diff.y < 0 && diff.x === 0) {
        margins.start.y = -30;
    } else if (diff.y > 0) {
        margins.start.y = 20;
    } else if (diff.y < 0) {
        margins.start.y = -20;
    }

    if (margins.start.x != 0) {
        margins.end.x = margins.start.x < 0 ?
            Math.abs(margins.start.x * 1.5) :
            margins.start.x * -1.5;
    }

    if (margins.start.y != 0) {
        margins.end.y = margins.start.y < 0 ?
            Math.abs(margins.start.y * 1.5) :
            margins.start.y * -1.5;
    }

    // The top branch edges are inverted, so their margins
    // needs to be inverted too.
    if (edge.branch === 'top') {
        var startX = margins.start.x;
        var startY = margins.start.y;
        margins.start.x = margins.end.x * -1;
        margins.start.y = margins.end.y * -1;
        margins.end.x = startX * -1;
        margins.end.y = startY * -1;
    }

    edge.start.x += margins.start.x;
    edge.start.y += margins.start.y;
    edge.end.x += margins.end.x;
    edge.end.y += margins.end.y;

    return edge;
};

The code might look a bit wonky, but that can largely be explained by it being torn out of a Reveal.js presentation I'm working on. I'm all up for ideas on how to improve other parts of the code as well, but it's particularly the body of the withMargins() function I'm unhappy with.

回答1:

As far I understand - you have center of the first circle (x0,y0), center of the second circle (x1,y1), circle radius R, desired margin from circle circumference M, and want to find starting and ending coordinates of arrow.

(Seems you are using full distance from circle center, in this case set Marg)

dx = x1 - x0
dy = y1 - y0
Len = math.sqrt(dx * dx + dy * dy)  //use math.hypot if available
Marg = R + M  //full distance from circle center to arrow end. 30 in your case
Coeff = Marg / Len
ax0 = x0 + Coeff * dx   //arrow start coords
ay0 = y0 + Coeff * dy
ax1 = x1 - Coeff * dx   //arrow end
ay1 = y1 - Coeff * dy

That's all.