Positioning a cytoscape.js "loop" edge in the bottomright of a node, going counter clockwise

Viewed 439

How can I set the styling of a cytoscape.js 'loop edge' so that it rotates counterclockwise about the bottom right corner of a long (150px wide) rectangular node?

I have been fiddling with the style settings and just can't figure it out. The best I can tell I should be able to tune these for styles to get what I want:

      selector: '.loop',
        style: {
          'loop-direction': '?deg',
          'loop-sweep': '?deg',
          'target-endpoint': '90deg',
          'source-endpoint': '105deg',
        }
      },

Which is something like this arrow, in red:

enter image description here

But I can't really get anything better than this snippet. I just can't get the curve to "flip" to the other side.

window.addEventListener('DOMContentLoaded', function() {

  var cy = window.cy = cytoscape({
    container: document.getElementById('cy'),
    
    style: [{
        selector: 'node',
        style: {
          'width': 150,
          'shape': 'rectangle',
          'background-opacity': 0.5,
        }
      },

      {
        selector: 'edge',
        style: {
          'line-color': 'black',
          'target-arrow-color': 'black',
          'target-arrow-shape': 'triangle',
          'curve-style': 'bezier'
        }
      },
      {
        selector: '.loop',
        style: {
          'loop-direction': '90deg', 
          'loop-sweep': '-90deg',
          'target-endpoint': '90deg',
          'source-endpoint': '105deg',
        }
      },

    ],

    elements: {
      nodes: [{
        data: {
          id: 'n16'
        }
      }],
      edges: [{
        data: {
          source: 'n16',
          target: 'n16'
        },
        classes: 'loop'
      }, ]
    }
  });

});
#cy {
  width: 300px;
  height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.15.1/cytoscape.min.js"></script>

<body>
  <h1>cytoscape loop edge demo</h1>

  <div id="cy"></div>

</body>

2 Answers

Your thinking is correct yet it seems Cytoscape will always default to clockwise loops when the endpoints are specified with angles (they are relative to the shape border!).

If you don't resort to "unbundled bezier-edges" where you can manually specify control points (and thus the curve of the edge) then using predefined values for endpoints might be good enough:

window.addEventListener('DOMContentLoaded', function() {

  var cy = window.cy = cytoscape({
    container: document.getElementById('cy'),
    
    style: [{
        selector: 'node',
        style: {
          'width': 150,
          'shape': 'rectangle',
          'background-opacity': 0.5,
        }
      },

      {
        selector: 'edge',
        style: {
          'line-color': 'black',
          'target-arrow-color': 'black',
          'target-arrow-shape': 'triangle',
          'curve-style': 'bezier'
        }
      },
      {
        selector: '.loop',
        style: {
          'loop-direction': '90deg', 
          'loop-sweep': '-90deg',
          'target-endpoint': 'outside-to-line',
          'source-endpoint': 'outside-to-line',
        }
      },

    ],

    elements: {
      nodes: [{
        data: {
          id: 'n16'
        }
      }],
      edges: [{
        data: {
          source: 'n16',
          target: 'n16'
        },
        classes: 'loop'
      }, ]
    }
  });

});
#cy {
  width: 300px;
  height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.15.1/cytoscape.min.js"></script>

<body>
  <h1>cytoscape loop edge demo</h1>

  <div id="cy"></div>

</body>

You can find a reference for these values here:

https://js.cytoscape.org/#style/edge-endpoints

Was able to get it to work by adding the control-point-step-size key and then fiddling with all the dials. I wish there was a "debug" mode where I could see the control points and bezier stuff that these dials manipulate:

  'loop-direction': '100deg', 
  'loop-sweep': '-20deg',
  'target-endpoint': '90deg',  // Up is 0 deg, going clockwise. From center of node, where the edge ends (pointing back into the node. 
  'source-endpoint': '105deg',  // Up is 0 deg, going clockwise. From center of node, where the edge comes out of the node.  
  'control-point-step-size': 72,

enter image description here

window.addEventListener('DOMContentLoaded', function() {

  var cy = window.cy = cytoscape({
    container: document.getElementById('cy'),
    
    style: [{
        selector: 'node',
        style: {
          'width': 150,
          'shape': 'rectangle',
          'background-opacity': 0.5,
        }
      },

      {
        selector: 'edge',
        style: {
          'line-color': 'black',
          'target-arrow-color': 'black',
          'target-arrow-shape': 'triangle',
          'curve-style': 'bezier'
        }
      },
      {
        selector: '.loop',
        style: {
          'loop-direction': '100deg', 
          'loop-sweep': '-20deg',
          'target-endpoint': '90deg',
          'source-endpoint': '105deg',
          'control-point-step-size': 72,
        }
      },

    ],

    elements: {
      nodes: [{
        data: {
          id: 'n16'
        }
      }],
      edges: [{
        data: {
          source: 'n16',
          target: 'n16'
        },
        classes: 'loop'
      }, ]
    }
  });

});
#cy {
  width: 300px;
  height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.15.1/cytoscape.min.js"></script>

<body>
  <h1>cytoscape loop edge demo</h1>

  <div id="cy"></div>

</body>

Related