Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for edge-distances:endpoints #3163

Merged
merged 1 commit into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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