The Mathematics Behind Communication Explorer Arrows

Creating lines to represent one way communication.

D3 can draw a line between nodes using their force directed layout. Let’s call these two points (x1, y1) and (x2, y2). Now we need to determine the point (Xp, Yp) to plot the arrow at. We can identify the coordinates using trigonometry.

Note: boundarySpace is the amount of additional space we want between the edge of the target circle and the end of the line, which is set to 8px (large enough to accommodate a marker).


renderLinks: function(lines, settings){
    var that = this;
    lines.attr("x1", function(d) { return d.source.x.toFixed(2); })
        .attr("y1", function(d) { return d.source.y.toFixed(2); })
        .attr("x2", function(d) { return that.getLinkEndPoint(d, settings)[0].toFixed(2); })
        .attr("y2", function(d) { return that.getLinkEndPoint(d, settings)[1].toFixed(2); });
getLinkEndPoint: function(d, settings){
    var theta = Math.atan2( - d.source.y, - d.source.x),
    Py = parseInt(, 10) - ( + settings.boundarySpace) * Math.sin(theta),
    Px = parseInt(, 10) - ( + settings.boundarySpace) * Math.cos(theta);
    return [Py, Px];

Creating arcs to represent two way communication.
D3 can also draw an arc between the center point of two nodes. Lets call these points (x1, y1) and (x2, y2). Since D3 cannot draw an arrow on an arc for any possible target circle radius, we need to plot a line at the point (P1x, P1y) where the arc intersects the target circle and put the arrow on line instead of the arc. The point (P1x, P1y) can be determined using the native svg function getPointAtLength.

Next, we get another point along the arc at a very small distance away (PSx, PSy). Now that two points are known, the slope can be calculated and the line extended to point (P2x, P2y).


plotSlopeLinesAndArrows: function(paths, settings){
    var that = this;
    paths.each(function(l, i) {
        var pathLength = this.getTotalLength() - (parseInt(, 10) + settings.boundarySpace);
        if(pathLength > 0){
            var p1 = this.getPointAtLength(pathLength),
            pS = this.getPointAtLength(pathLength - 5),
            p2 = that.getSlopeLineEndPoint(point1, point2),
            strokeW = that.calculateLinkWidth(l.quantity),
            path = "M" + endPoint.x.toFixed(2) + "," + endPoint.y.toFixed(2) + " L" + point1.x.toFixed(2) +","+ point1.y.toFixed(2);
            $("#slope" + i).attr("d", path).attr("marker-end", function(d) {
                var marker = null;
                if( l.source.state === "hover" || l.source.state === "clicked" )    {
                    marker = that.getMarker("A", strokeW, true);
                    marker = that.getMarker("A", strokeW);
                return "url(#" + marker + ")";
            that.renderPhantomLink(l, i, point1);

getSlopeLineEndPoint: function(p1, p2){
     var slopeLineLength = 90,
         slope = (p1.y - p2.y) / (p1.x - p2.x),
         theta = Math.atan2(slope, 1),
         x2 = 0,
         y2 = 0;
     if(p1.x - p2.x > 0){
         y2 = p1.y - (slopeLineLength * Math.sin(theta));
         x2 = p1.x - (slopeLineLength * Math.cos(theta));
         y2 = p1.y + (slopeLineLength * Math.sin(theta));
         x2 = p1.x + (slopeLineLength * Math.cos(theta));
     return {'x': x2, 'y': y2};

Debug Mode:
All of this can be seen in action by looking at the GIF below. The red lines are the slope lines, which are drawn at edge of the target circle when there is two way communication. A marker is placed on them to give the desired effect..


Final Appearance:
By changing the CSS to hide the slope lines and the original arcs, the desired appearance can be created.


Taking a closer look, we can see that we have achieved a clean look between the end of the arcs, their marker, and the target circle for all possible radius and stroke sizes.


Progressive Shrinking of Text

We needed a way to shrink the text of a document if we found it to be too large for our limits on indexing. To keep the methods consistent, we developed a quick web service in PHP called Shrinkray. Shrinkray receives a large text document and takes steps to make the text smaller by progressively removing information from the document.  This script is a one page web service.  It requires no additional code.

Actions for Shrinkray

  •  ax=lim – reports on the current limit being enforced by Shrinkray
  •  ax=sr – the main shrink ray method

Variables for Shrinkray

  • srpath = the path to a file you want to GET with shrinkray (the web server needs the RIGHT permissions to this path)

Typical Usage Patterns

curl --silent http://REDACTED/shrinkray/v1/?ax=lim

This will return the limit being enforced by Shrinkray.

curl --silent -v http://REDACTED/shrinkray/v1/?ax=sr&srpath=/path/to/file

This will have Shrinkray fetch the document specified by ‘srpath’ and shrink it.


Response code + payload. If the action is ‘lim’ the payload is the string representing the size limit. If the method is ‘sr’ and the status code is less than 399, the payload will be the shrunk document.

Response Codes for Shrinkray

  • HTTP/1.1 200 OK
  • HTTP/1.1 230 Shrunk via tags
  • HTTP/1.1 235 Shurnk via duplication
  • HTTP/1.1 239 Shrunk via punctuation
  • HTTP/1.1 240 Shrunk via number
  • HTTP/1.1 245 Shrunk via lowercase
  • HTTP/1.1 250 Shrunk via header
  • HTTP/1.1 400 Bad Request
  • HTTP/1.1 410 Gone
  • HTTP/1.1 412 Precondition Failed
  • HTTP/1.1 413 Request Entity Too Large
  • HTTP/1.1 500 Internal Server Error

Response Codes and Events

  • 200 when you ask for the limit over GET or POST
  • 230 when the HTML tags have been removed
  • 235 when the duplicate strings have been removed
  • 239 when punctuation (defined by unicode character class \p{Punctuation}) has been removed
  • 240 when numbers (defined by the unicode character class \p{Number}) have been removed
  • 245 when the words have been lower cased and the duplicates have been removed
  • NOTE: 230-245 are incremental, they include the previous steps
  • 250 when the text could not be shrunk and a chunk of the document lower than ax=lim has been returned to you in the payload
  • 400 when you use the ax=sr and do not provide srpath (GET)
  • 410 when you specify the srpath and the file does not exist
  • 412 when you provide a document that is smaller than ax=lim and no action is taken
  •  413 when all the shrinking processes have failed and nothing is done to your document
  • 500 when something unexpected happens

Using the Response Code

If your response is within 230 to 250, your document has been shrunk and the smaller document is in the payload. It is up to you to persist this document.

You can download a redacted version of the PHP script here