From 323e3a7185c8310fe9a966f2ef6b57363da27de3 Mon Sep 17 00:00:00 2001 From: Evaldas Laureckas Date: Tue, 21 Jan 2020 12:04:01 +0200 Subject: [PATCH] feat(edges): make self reference position configurable (#230) * no message * selfReference property * no message * no message * no message * no message * no message * changed side property to angle * lint fixes * unit test fix * comments and small changes * formatting * no message * no message * no message * no message * no message * no message * no message * no message * no message * no message * no message * test html * no message * no message * no message * no message * no message * no message * no message --- docs-kr/network/edges.html | 25 ++++++ docs/network/edges.html | 32 ++++++- .../network/edgeStyles/selfReference.html | 84 +++++++++++++++++++ lib/network/modules/EdgesHandler.js | 5 +- lib/network/modules/components/Edge.js | 42 ++++++---- .../components/edges/util/edge-base.ts | 49 ++++++----- .../components/shared/ComponentUtil.js | 50 ++++++++++- lib/network/options.js | 9 ++ test/edges/bezier-edge-dynamic.test.ts | 5 +- test/edges/cubic-bezier-edge.test.ts | 59 +++++++------ types/network/Network.d.ts | 5 ++ 11 files changed, 300 insertions(+), 65 deletions(-) create mode 100644 examples/network/edgeStyles/selfReference.html diff --git a/docs-kr/network/edges.html b/docs-kr/network/edges.html index 46a70188ed..9437a4ae2c 100644 --- a/docs-kr/network/edges.html +++ b/docs-kr/network/edges.html @@ -204,6 +204,10 @@

옵션

}, selectionWidth: 1, selfReferenceSize:20, + selfReference:{ + size: 20, + angle: Math.PI / 4 + }, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', @@ -886,6 +890,27 @@

옵션

false to Node와 from Node가 동일한 경우, 원이 그려집니다. 이것은 그 원의 반지름입니다. + + selfReference + Object + Object + TODO + + + + selfReference.size + Number + 20 + TODO + + + + selfReference.angle + Number + π / 4 + TODO + + shadow Object or Boolean diff --git a/docs/network/edges.html b/docs/network/edges.html index 11dcc4bc1d..1bcaecbe3f 100644 --- a/docs/network/edges.html +++ b/docs/network/edges.html @@ -203,7 +203,11 @@

Options

} }, selectionWidth: 1, - selfReferenceSize:20, + selfReferenceSize: 20, + selfReference:{ + size: 20, + angle: Math.PI / 4 + }, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', @@ -895,7 +899,31 @@

Options

selfReferenceSize Number false - When the to and from nodes are the same, a circle is drawn. This is the radius of that circle. + When the to and from nodes are the same, a circle is drawn. This is the radius of that circle. + This property is deprecated please use selfReference instead. + + + + selfReference + Object + Object + When the to and from nodes are the same, a circle is drawn. This provides radius of circle and position. + + + + selfReference.size + Number + 20 + This is the radius of circle. + + + + selfReference.angle + Number + π / 4 + This is the angle (Provided in radians) to indicate position for the circle. If position is not provided then it will + be top-right (π / 4) corner. + shadow diff --git a/examples/network/edgeStyles/selfReference.html b/examples/network/edgeStyles/selfReference.html new file mode 100644 index 0000000000..7fb2eccf77 --- /dev/null +++ b/examples/network/edgeStyles/selfReference.html @@ -0,0 +1,84 @@ + + + + + Vis Network | Edge Styles | Self Reference + + + + + + + + +
+ + + + + + + \ No newline at end of file diff --git a/lib/network/modules/EdgesHandler.js b/lib/network/modules/EdgesHandler.js index 3faa6c2873..32d5de5e28 100644 --- a/lib/network/modules/EdgesHandler.js +++ b/lib/network/modules/EdgesHandler.js @@ -94,7 +94,10 @@ class EdgesHandler { } }, selectionWidth: 1.5, - selfReferenceSize:20, + selfReference: { + size: 20, + angle: Math.PI / 4, + }, shadow:{ enabled: false, color: 'rgba(0,0,0,0.5)', diff --git a/lib/network/modules/components/Edge.js b/lib/network/modules/components/Edge.js index 6e55885e92..45892d3e25 100644 --- a/lib/network/modules/components/Edge.js +++ b/lib/network/modules/components/Edge.js @@ -130,6 +130,7 @@ class Edge { 'scaling', 'selectionWidth', 'selfReferenceSize', + 'selfReference', 'to', 'title', 'value', @@ -249,6 +250,15 @@ class Edge { if (allowDeletion === true && newOptions.font === null) { parentOptions.font = util.bridgeObject(globalOptions.font); // set the object back to the global options } + + if(newOptions.hasOwnProperty("selfReferenceSize")){ + console.log('The selfReferenceSize property has been deprecated. Please use selfReference property instead. The selfReference can be set like thise selfReference:{size:30, angle:Math.PI / 4}'); + parentOptions.selfReference = { + size: newOptions.selfReferenceSize, + angle: parentOptions.selfReference.angle + } + } + } @@ -676,17 +686,22 @@ class Edge { else { // Ignore the orientations. this.labelModule.pointToSelf = true; - var x, y; - var radius = this.options.selfReferenceSize; - if (node1.shape.width > node1.shape.height) { - x = node1.x + node1.shape.width * 0.5; - y = node1.y - radius; - } - else { - x = node1.x + radius; - y = node1.y - node1.shape.height * 0.5; - } - point = this._pointOnCircle(x, y, radius, 0.125); + + // get circle coordinates + const coordinates = ComponentUtil.getSelfRefCoordinates( + ctx, + this.options.selfReference.angle, + this.options.selfReference.size, + node1 + ); + + point = this._pointOnCircle( + coordinates.x, + coordinates.y, + this.options.selfReference.size, + this.options.selfReference.angle + ); + this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover); } } @@ -796,12 +811,11 @@ class Edge { * @param {number} x * @param {number} y * @param {number} radius - * @param {number} percentage Value between 0 (line start) and 1 (line end) + * @param {number} angle * @return {Object} point * @private */ - _pointOnCircle(x, y, radius, percentage) { - var angle = percentage * 2 * Math.PI; + _pointOnCircle(x, y, radius, angle) { return { x: x + radius * Math.cos(angle), y: y - radius * Math.sin(angle) diff --git a/lib/network/modules/components/edges/util/edge-base.ts b/lib/network/modules/components/edges/util/edge-base.ts index 187f4783b9..813fe5656b 100644 --- a/lib/network/modules/components/edges/util/edge-base.ts +++ b/lib/network/modules/components/edges/util/edge-base.ts @@ -16,6 +16,7 @@ import { VNode } from "./types"; import { drawDashedLine } from "./shapes"; +import * as ComponentUtil from "./../../shared/ComponentUtil"; export interface FindBorderPositionOptions { via: Via; @@ -106,6 +107,7 @@ export abstract class EdgeBase implements EdgeType { */ public setOptions(options: EdgeOptions): void { this.options = options; + this.from = this._body.nodes[this.options.from]; this.to = this._body.nodes[this.options.to]; this.id = this.options.id; @@ -318,26 +320,23 @@ export abstract class EdgeBase implements EdgeType { protected _getCircleData( ctx?: CanvasRenderingContext2D ): [number, number, number] { - let x: number; - let y: number; - const node = this.from; - const radius = this.options.selfReferenceSize; + const radius = this.options.selfReference.size; if (ctx !== undefined) { - if (node.shape.width === undefined) { - node.shape.resize(ctx); + if (this.from.shape.width === undefined) { + this.from.shape.resize(ctx); } } // get circle coordinates - if (node.shape.width > node.shape.height) { - x = node.x + node.shape.width * 0.5; - y = node.y - radius; - } else { - x = node.x + radius; - y = node.y - node.shape.height * 0.5; - } - return [x, y, radius]; + const coordinates = ComponentUtil.default.getSelfRefCoordinates( + ctx, + this.options.selfReference.angle, + radius, + this.from + ); + + return [coordinates.x, coordinates.y, radius]; } /** @@ -387,9 +386,10 @@ export abstract class EdgeBase implements EdgeType { const direction = options.direction; const maxIterations = 10; - const radius = this.options.selfReferenceSize; + const radius = this.options.selfReference.size; const threshold = 0.05; let pos: Point; + let middle = (low + high) * 0.5; let iteration = 0; @@ -746,28 +746,35 @@ export abstract class EdgeBase implements EdgeType { const [x, y, radius] = this._getCircleData(ctx); if (position === "from") { + const low = this.options.selfReference.angle - 2 * Math.PI; + const high = this.options.selfReference.angle; + const pointT = this._findBorderPositionCircle(this.from, ctx, { x, y, - low: 0.25, - high: 0.6, + low, + high, direction: -1 }); angle = pointT.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; arrowPoint = pointT; } else if (position === "to") { + const low = this.options.selfReference.angle - 2 * Math.PI; + const high = this.options.selfReference.angle; + const pointT = this._findBorderPositionCircle(this.from, ctx, { x, y, - low: 0.6, - high: 1.0, + low, + high, direction: 1 }); angle = pointT.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; arrowPoint = pointT; } else { - arrowPoint = this._pointOnCircle(x, y, radius, 0.175); - angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; + const pos = this.options.selfReference.angle / (2 * Math.PI); + arrowPoint = this._pointOnCircle(x, y, radius, pos); + angle = pos * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; } } diff --git a/lib/network/modules/components/shared/ComponentUtil.js b/lib/network/modules/components/shared/ComponentUtil.js index bbaafe01cd..43b2f7b96d 100644 --- a/lib/network/modules/components/shared/ComponentUtil.js +++ b/lib/network/modules/components/shared/ComponentUtil.js @@ -114,6 +114,54 @@ class ComponentUtil { // Note that this is quite strict: types that *might* be converted to string are disallowed return (typeof text === 'string' && text !== ''); } + + /** + * Returns x, y of self reference circle based on provided angle + * + * @param {Object} ctx + * @param {number} angle + * @param {number} radius + * @return {Object} node + * @returns {Object} x and y coordinates + */ + static getSelfRefCoordinates(ctx, angle, radius, node){ + let x = node.x; + let y = node.y; + + if(ctx && typeof node.distanceToBorder === "function"){ + //calculating opposite and adjacent + //distaneToBorder becomes Hypotenuse. + //Formulas sin(a) = Opposite / Hypotenuse and cos(a) = Adjacent / Hypotenuse + const toBorderDist = node.distanceToBorder(ctx, angle); + const yFromNodeCenter = Math.sin(angle)*toBorderDist; + const xFromNodeCenter = Math.cos(angle)*toBorderDist; + //xFromNodeCenter is basically x and if xFromNodeCenter equals to the distance to border then it means + //that y does not need calculation because it is equal node.height / 2 or node.y + //same thing with yFromNodeCenter and if yFromNodeCenter equals to the distance to border then it means + //that x is equal node.width / 2 or node.x + if(xFromNodeCenter === toBorderDist){ + x += toBorderDist; + y = node.y; + }else if(yFromNodeCenter === toBorderDist){ + x = node.x; + y -= toBorderDist; + }else{ + x += xFromNodeCenter; + y -= yFromNodeCenter; + } + + + } else if (node.shape.width > node.shape.height) { + x = node.x + node.shape.width * 0.5; + y = node.y - radius; + } else { + x = node.x + radius; + y = node.y - node.shape.height * 0.5; + } + + return {x,y}; + } + } -export default ComponentUtil; +export default ComponentUtil; \ No newline at end of file diff --git a/lib/network/options.js b/lib/network/options.js index f0aed6e50a..6baf4e7d10 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -156,6 +156,11 @@ let allOptions = { }, selectionWidth: { 'function': 'function', number }, selfReferenceSize: { number }, + selfReference: { + size: { number }, + angle: { number }, + __type__: { object } + }, shadow: { enabled: { boolean: bool }, color: { string }, @@ -580,6 +585,10 @@ let configureOptions = { }, selectionWidth: [1.5, 0, 5, 0.1], selfReferenceSize: [20, 0, 200, 1], + selfReference: { + size: [20, 0, 200, 1], + angle: [Math.PI / 2, Math.PI * 2, 3 * Math.PI / 2, Math.PI], + }, shadow: { enabled: false, color: 'rgba(0,0,0,0.5)', diff --git a/test/edges/bezier-edge-dynamic.test.ts b/test/edges/bezier-edge-dynamic.test.ts index f966d05cfd..7a19eea2b3 100644 --- a/test/edges/bezier-edge-dynamic.test.ts +++ b/test/edges/bezier-edge-dynamic.test.ts @@ -239,7 +239,10 @@ describe('BezierEdgeDynamic', function(): void { smooth: { roundness: 2, }, - selfReferenceSize: 2, + selfReference: { + size: 2, + angle: Math.PI / 2, + }, }, body, {} as any diff --git a/test/edges/cubic-bezier-edge.test.ts b/test/edges/cubic-bezier-edge.test.ts index 9394034745..884a8862a8 100644 --- a/test/edges/cubic-bezier-edge.test.ts +++ b/test/edges/cubic-bezier-edge.test.ts @@ -146,7 +146,10 @@ describe('CubicBezierEdge', function(): void { smooth: { roundness: 2, }, - selfReferenceSize: 42, + selfReference: { + size: 42, + angle: Math.PI / 2, + }, }, body as any, {} as any @@ -511,14 +514,14 @@ describe('CubicBezierEdge', function(): void { to: 4, expected: { from: { - t: 0.250341796875, - x: 420.9098019990056, - y: -483.99990314656236, + t: 0.5077148437499999, + x: 358.049334219333, + y: -841.9648978996685, }, to: { t: 0.7998046875, - x: 433.92968488174444, - y: -402.0397291193016, + x: 412.92968488174444, + y: -804.0397291193017, }, }, }, @@ -535,7 +538,10 @@ describe('CubicBezierEdge', function(): void { smooth: { roundness: 2, }, - selfReferenceSize: 42, + selfReference: { + size: 42, + angle: Math.PI / 2, + }, }, body, {} as any @@ -559,16 +565,16 @@ describe('CubicBezierEdge', function(): void { }, position: 'from', expected: { - angle: 3.4536043458457324, + angle: 1.9037437282114893, core: { - x: 447.9532857144876, - y: -417.2972622260487, + x: 396.26792442306333, + y: -871.0373387250717, }, length: 31.5, point: { - t: 0.250341796875, - x: 420.9720815711208, - y: -425.999970021555, + t: 0.4970097752749478, + x: 387.0022943938739, + y: -844.2442313994518, }, type: 'arrow', }, @@ -584,15 +590,15 @@ describe('CubicBezierEdge', function(): void { }, position: 'middle', expected: { - angle: 3.9269908169872414, + angle: 3.4557519189487724, core: { - x: 447.9029478978546, - y: -403.58201341320836, + x: 428.24637853396604, + y: -847.8221952670641, }, length: 33, point: { - x: 426.90187649661414, - y: -424.5830848144488, + x: 400, + y: -857, }, type: 'bar', }, @@ -608,16 +614,16 @@ describe('CubicBezierEdge', function(): void { }, position: 'to', expected: { - angle: -5.024093876483052, + angle: 13.863514558139903, core: { - x: 424.06346980869967, - y: -443.80680503833094, + x: 404.23324559789233, + y: -874.6679591046823, }, length: 36, point: { - t: 0.999609375, - x: 433.99996084448463, - y: -412.96809323164604, + t: -2.006446870554419, + x: 412.9893362093595, + y: -843.4735545230305, }, type: 'circle', }, @@ -741,7 +747,10 @@ describe('CubicBezierEdge', function(): void { enabled: smooth, roundness: 2, }, - selfReferenceSize: 13, + selfReference: { + size: 13, + angle: Math.PI / 2, + }, }, body, {} as any diff --git a/types/network/Network.d.ts b/types/network/Network.d.ts index 3be7d045b9..0c67caeff4 100644 --- a/types/network/Network.d.ts +++ b/types/network/Network.d.ts @@ -968,6 +968,11 @@ export interface EdgeOptions { selfReferenceSize?: number; + selfReference?: { + size?: number, + angle?: number, + }; + shadow?: boolean | OptionsShadow; smooth?: boolean | {