Skip to content

Commit

Permalink
Merge pull request #3163 from cytoscape/feature/edge-dist-endpt
Browse files Browse the repository at this point in the history
Add support for `edge-distances:endpoints`
  • Loading branch information
maxkfranz authored Sep 28, 2023
2 parents 576121a + 1a6c407 commit ae20223
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 10 deletions.
24 changes: 21 additions & 3 deletions documentation/md/style.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,13 @@ A bezier edge is bundled with all other parallel bezier edges. Each bezier edge
* **`control-point-step-size`** : Along the line perpendicular from source to target, this value specifies the distance between successive bezier edges.
* **`control-point-distance`** : A single value that overrides `control-point-step-size` with a manual value. Because it overrides the step size, bezier edges with the same value will overlap. Thus, it's best to use this as a one-off value for particular edges if need be.
* **`control-point-weight`** : A single value that weights control points along the line from source to target. The value usually ranges on [0, 1], with 0 towards the source node and 1 towards the target node --- but larger or smaller values can also be used.
* **`edge-distances`** : With value `intersection` (default), the line from source to target for `control-point-weight` is from the outside of the source node's shape to the outside of the target node's shape. With value `node-position`, the line is from the source position to the target position. The `node-position` option makes calculating edge points easier --- but it should be used carefully because you can create invalid points that `intersection` would have automatically corrected.
* **`edge-distances`** :
* With value `intersection` (default), the line from source to target for `segment-weights` is from the outside of the source node's shape to the outside of the target node's shape.
* With value `node-position`, the line is from the source position to the target position.
* The `node-position` option makes calculating edge points easier --- but it should be used carefully because you can create invalid points that `intersection` would have automatically corrected.
* With value `endpoints`, the line is from the manually-specified source endpoint (via `source-endpoint`) to the manually-specified target endpoint (via `target-endpoint`).
* A manual endpoint may be specified with a position, e.g. `source-endpoint: 20 10`.
* A manual endpoint may be alternatively specified with an angle, e.g. `target-endpoint: 90deg`.
## Loop edges
Expand Down Expand Up @@ -389,7 +395,13 @@ When two or more control points are specified for an unbundled bezier edge, each
* **`control-point-distances`** : A series of values that specify for each control point the distance perpendicular to a line formed from source to target, e.g. `-20 20 -20`.
* **`control-point-weights`** : A series of values that weights control points along a line from source to target, e.g. `0.25 0.5 0.75`. A value usually ranges on [0, 1], with 0 towards the source node and 1 towards the target node --- but larger or smaller values can also be used.
* **`edge-distances`** : With value `intersection` (default), the line from source to target for `control-point-weights` is from the outside of the source node's shape to the outside of the target node's shape. With value `node-position`, the line is from the source position to the target position. The `node-position` option makes calculating edge points easier --- but it should be used carefully because you can create invalid points that `intersection` would have automatically corrected.
* **`edge-distances`** :
* With value `intersection` (default), the line from source to target for `segment-weights` is from the outside of the source node's shape to the outside of the target node's shape.
* With value `node-position`, the line is from the source position to the target position.
* The `node-position` option makes calculating edge points easier --- but it should be used carefully because you can create invalid points that `intersection` would have automatically corrected.
* With value `endpoints`, the line is from the manually-specified source endpoint (via `source-endpoint`) to the manually-specified target endpoint (via `target-endpoint`).
* A manual endpoint may be specified with a position, e.g. `source-endpoint: 20 10`.
* A manual endpoint may be alternatively specified with an angle, e.g. `target-endpoint: 90deg`.
## Haystack edges
Expand All @@ -411,7 +423,13 @@ A segment edge is made of a series of one or more straight lines, using a co-ord
* **`segment-distances`** : A series of values that specify for each segment point the distance perpendicular to a line formed from source to target, e.g. `-20 20 -20`.
* **`segment-weights`** : A series of values that weights segment points along a line from source to target, e.g. `0.25 0.5 0.75`. A value usually ranges on [0, 1], with 0 towards the source node and 1 towards the target node --- but larger or smaller values can also be used.
* **`edge-distances`** : With value `intersection` (default), the line from source to target for `segment-weights` is from the outside of the source node's shape to the outside of the target node's shape. With value `node-position`, the line is from the source position to the target position. The `node-position` option makes calculating edge points easier --- but it should be used carefully because you can create invalid points that `intersection` would have automatically corrected.
* **`edge-distances`** :
* With value `intersection` (default), the line from source to target for `segment-weights` is from the outside of the source node's shape to the outside of the target node's shape.
* With value `node-position`, the line is from the source position to the target position.
* The `node-position` option makes calculating edge points easier --- but it should be used carefully because you can create invalid points that `intersection` would have automatically corrected.
* With value `endpoints`, the line is from the manually-specified source endpoint (via `source-endpoint`) to the manually-specified target endpoint (via `target-endpoint`).
* A manual endpoint may be specified with a position, e.g. `source-endpoint: 20 10`.
* A manual endpoint may be alternatively specified with an angle, e.g. `target-endpoint: 90deg`.
## Straight edges
Expand Down
59 changes: 53 additions & 6 deletions src/extensions/renderer/base/coord-ele-math/edge-control-points.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,57 @@ import Map from '../../../../map';

let BRp = {};

BRp.findMidptPtsEtc = function(edge, pairInfo) {
let { posPts, intersectionPts, vectorNormInverse } = pairInfo;

let midptPts;

// n.b. assumes all edges in bezier bundle have same endpoints specified
let srcManEndpt = edge.pstyle('source-endpoint');
let tgtManEndpt = edge.pstyle('target-endpoint');
const haveManualEndPts = srcManEndpt.units != null && tgtManEndpt.units != null;

const recalcVectorNormInverse = (x1, y1, x2, y2) => {
let dy = ( y2 - y1 );
let dx = ( x2 - x1 );
let l = Math.sqrt( dx * dx + dy * dy );

return {
x: -dy / l,
y: dx / l
};
};

const edgeDistances = edge.pstyle('edge-distances').value;

switch(edgeDistances) {
case 'node-position':
midptPts = posPts;
break;

case 'intersection':
midptPts = intersectionPts;
break;

case 'endpoints': {
if (haveManualEndPts) {
const [x1, y1] = this.manualEndptToPx( edge.source()[0], srcManEndpt );
const [x2, y2] = this.manualEndptToPx( edge.target()[0], tgtManEndpt );
const endPts = { x1, y1, x2, y2 };

vectorNormInverse = recalcVectorNormInverse(x1, y1, x2, y2);
midptPts = endPts;
} else {
util.warn(`Edge ${edge.id()} has edge-distances:endpoints specified without manual endpoints specified via source-endpoint and target-endpoint. Falling back on edge-distances:intersection (default).`);
midptPts = intersectionPts; // back to default
}
break;
}
}

return { midptPts, vectorNormInverse };
};

BRp.findHaystackPoints = function( edges ){
for( let i = 0; i < edges.length; i++ ){
let edge = edges[i];
Expand Down Expand Up @@ -64,8 +115,6 @@ BRp.findSegmentsPoints = function( edge, pairInfo ){
// Segments (multiple straight lines)

const rs = edge._private.rscratch;
const { posPts, intersectionPts, vectorNormInverse } = pairInfo;
const edgeDistances = edge.pstyle('edge-distances').value;
const segmentWs = edge.pstyle( 'segment-weights' );
const segmentDs = edge.pstyle( 'segment-distances' );
const segmentsN = Math.min( segmentWs.pfValue.length, segmentDs.pfValue.length );
Expand All @@ -80,7 +129,7 @@ BRp.findSegmentsPoints = function( edge, pairInfo ){
let w1 = 1 - w;
let w2 = w;

let midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
let { midptPts, vectorNormInverse } = this.findMidptPtsEtc(edge, pairInfo);

let adjustedMidpt = {
x: midptPts.x1 * w1 + midptPts.x2 * w2,
Expand Down Expand Up @@ -192,8 +241,6 @@ BRp.findStraightEdgePoints = function( edge ){

BRp.findBezierPoints = function( edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped ){
const rs = edge._private.rscratch;
const { vectorNormInverse, posPts, intersectionPts } = pairInfo;
const edgeDistances = edge.pstyle('edge-distances').value;
const stepSize = edge.pstyle('control-point-step-size').pfValue;
const ctrlptDists = edge.pstyle('control-point-distances');
const ctrlptWs = edge.pstyle('control-point-weights');
Expand Down Expand Up @@ -230,7 +277,7 @@ BRp.findBezierPoints = function( edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapp
let w1 = 1 - ctrlptWeight;
let w2 = ctrlptWeight;

let midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
let { midptPts, vectorNormInverse } = this.findMidptPtsEtc(edge, pairInfo);

let adjustedMidpt = {
x: midptPts.x1 * w1 + midptPts.x2 * w2,
Expand Down
2 changes: 1 addition & 1 deletion src/style/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const styfn = {};
angle: { number: true, units: 'deg|rad', implicitUnits: 'rad' },
textRotation: { number: true, units: 'deg|rad', implicitUnits: 'rad', enums: [ 'none', 'autorotate' ] },
polygonPointList: { number: true, multiple: true, evenMultiple: true, min: -1, max: 1, unitless: true },
edgeDistances: { enums: ['intersection', 'node-position'] },
edgeDistances: { enums: ['intersection', 'node-position', 'endpoints'] },
edgeEndpoint: {
number: true, multiple: true, units: '%|px|em|deg|rad', implicitUnits: 'px',
enums: [ 'inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label' ], singleEnum: true,
Expand Down

0 comments on commit ae20223

Please sign in to comment.