diff --git a/dist/workers/graph.js b/dist/workers/graph.js index 5b1489197a..76c0731891 100644 --- a/dist/workers/graph.js +++ b/dist/workers/graph.js @@ -15,6 +15,7 @@ const util = new Util(); // CONSTANTS const transformThreshold = 1.5; +const delayFocus = 1000; const ObjectLayout = { Human: 1, @@ -70,7 +71,9 @@ let edgeMap = new Map(); let hoverAlpha = 0.2; let fontFamily = 'Helvetica, san-serif'; let timeoutHover = 0; +let rootId = ''; let root = null; +let paused = false; addEventListener('message', ({ data }) => { if (this[data.id]) { @@ -82,7 +85,7 @@ init = (param) => { data = param; canvas = data.canvas; settings = data.settings; - + rootId = data.rootId; ctx = canvas.getContext('2d'); util.ctx = ctx; @@ -99,25 +102,10 @@ init = (param) => { initForces(); - simulation.on('tick', () => { redraw(); }); + simulation.on('tick', () => redraw()); simulation.tick(100); - // Center initially on root node - setTimeout(() => { - root = getNodeById(data.rootId); - - let x = width / 2; - let y = height / 2; - - if (root) { - x = root.x; - y = root.y; - }; - - transform = Object.assign(transform, getCenter(x, y)); - send('onTransform', { ...transform }); - redraw(); - }, 100); + setTimeout(() => this.setRootId({ rootId }), 100); }; initTheme = (theme) => { @@ -202,7 +190,7 @@ initForces = () => { }; updateForces = () => { - let old = getNodeMap(); + const old = getNodeMap(); edges = util.objectCopy(data.edges); nodes = util.objectCopy(data.nodes); @@ -217,6 +205,14 @@ updateForces = () => { edges = edges.filter(d => d.type != EdgeType.Relation); }; + // Filte local only edges + if (settings.local) { + edges = edges.filter(d => (d.source == rootId) || (d.target == rootId)); + + const nodeIds = util.arrayUnique([ rootId ].concat(edges.map(d => d.source)).concat(edges.map(d => d.target))); + nodes = nodes.filter(d => nodeIds.includes(d.id)); + }; + let map = getNodeMap(); edges = edges.filter(d => map.get(d.source) && map.get(d.target)); @@ -237,7 +233,9 @@ updateForces = () => { edges = edges.filter(d => map.get(d.source) && map.get(d.target)); // Shallow copy to disable mutations - nodes = nodes.map(d => Object.assign(old.get(d.id) || {}, d)); + nodes = nodes.map(d => { + return Object.assign(old.get(d.id) || { x: width / 2, y: width / 2 }, d); + }); edges = edges.map(d => Object.assign({}, d)); simulation.nodes(nodes); @@ -258,16 +256,28 @@ updateForces = () => { }; updateSettings = (param) => { - const needUpdate = (param.link != settings.link) || - (param.relation != settings.relation) || - (param.orphan != settings.orphan); + const updateKeys = [ 'link', 'relation', 'orphan', 'local' ]; + + let needUpdate = false; + let needFocus = false; + + for (let key of updateKeys) { + if (param[key] != settings[key]) { + needUpdate = true; + + if (key == 'local') { + needFocus = true; + }; + + break; + }; + }; settings = Object.assign(settings, param); + needUpdate ? updateForces() : redraw(); - if (needUpdate) { - updateForces(); - } else { - redraw(); + if (needFocus) { + setTimeout(() => this.setRootId({ rootId }), delayFocus); }; }; @@ -289,7 +299,7 @@ draw = (t) => { ctx.font = getFont(); edges.forEach(d => { - drawLine(d, radius, radius * 1.3, settings.marker && d.isDouble, settings.marker); + drawEdge(d, radius, radius * 1.3, settings.marker && d.isDouble, settings.marker); }); nodes.forEach(d => { @@ -303,10 +313,12 @@ draw = (t) => { redraw = () => { cancelAnimationFrame(frame); - frame = requestAnimationFrame(draw); + if (!paused) { + frame = requestAnimationFrame(draw); + }; }; -drawLine = (d, arrowWidth, arrowHeight, arrowStart, arrowEnd) => { +drawEdge = (d, arrowWidth, arrowHeight, arrowStart, arrowEnd) => { const x1 = d.source.x; const y1 = d.source.y; const r1 = getRadius(d.source); @@ -577,6 +589,22 @@ onSelect = ({ x, y, selectRelated }) => { }; }; +onSetRootId = ({ x, y }) => { + const d = getNodeByCoords(x, y); + if (d) { + this.setRootId({ rootId: d.id }); + }; +}; + +onSetEdges = (param) => { + data.edges = param.edges; + updateForces(); +}; + +onSetSelected = ({ ids }) => { + selected = ids; +}; + onMouseMove = ({ x, y }) => { const active = nodes.find(d => d.isOver); const d = getNodeByCoords(x, y); @@ -618,12 +646,11 @@ onContextMenu = ({ x, y }) => { const d = getNodeByCoords(x, y); if (!d) { send('onContextSpaceClick', { x, y }); - return; + } else { + send('onContextMenu', { node: d, x, y }); + d.isOver = true; + redraw(); }; - - d.isOver = true; - send('onContextMenu', { node: d, x, y }); - redraw(); }; onAddNode = ({ target, sourceId }) => { @@ -667,21 +694,12 @@ onRemoveNode = ({ ids }) => { data.edges = data.edges.filter(d => !ids.includes(d.source.id) && !ids.includes(d.target.id)); updateForces(); - redraw(); }; -onSetEdges = (param) => { - data.edges = param.edges; +setRootId = (param) => { + rootId = param.rootId; + root = getNodeById(rootId); - updateForces(); -}; - -onSetSelected = ({ ids }) => { - selected = ids; -}; - -onSetRootId = ({ rootId }) => { - root = nodes.find(d => d.id == rootId); if (!root) { return; }; @@ -696,12 +714,14 @@ onSetRootId = ({ rootId }) => { transform = Object.assign(transform, coords); redraw(); }) - .onComplete(() => { - send('onTransform', { ...transform }); - }) + .onComplete(() => send('onTransform', { ...transform })) .start(); - redraw(); + if (settings.local) { + updateForces(); + } else { + redraw(); + }; }; restart = (alpha) => { diff --git a/dist/workers/lib/util.js b/dist/workers/lib/util.js index 6ccf9a0417..9d2cca2928 100644 --- a/dist/workers/lib/util.js +++ b/dist/workers/lib/util.js @@ -78,4 +78,8 @@ class Util { this.ctx.restore(); }; + arrayUnique (a) { + return [ ...new Set(a) ]; + }; + }; \ No newline at end of file diff --git a/src/json/text.json b/src/json/text.json index 2ee2113e01..65f93a2ade 100644 --- a/src/json/text.json +++ b/src/json/text.json @@ -1173,6 +1173,7 @@ "menuGraphSettingsLinks": "Links", "menuGraphSettingsRelations": "Relations", "menuGraphSettingsUnlinkedObjects": "Unlinked Objects", + "menuGraphSettingsLocal": "Local graph", "menuHelpWhatsNew": "What's New", "menuHelpShortcut": "Keyboard Shortcuts", diff --git a/src/ts/component/menu/graph/settings.tsx b/src/ts/component/menu/graph/settings.tsx index 14efe0f4c7..47a9bd66cc 100644 --- a/src/ts/component/menu/graph/settings.tsx +++ b/src/ts/component/menu/graph/settings.tsx @@ -99,6 +99,7 @@ const MenuGraphSettings = observer(class MenuGraphSettings extends React.Compone { id: 'link', name: translate('menuGraphSettingsLinks') }, { id: 'relation', name: translate('menuGraphSettingsRelations') }, { id: 'orphan', name: translate('menuGraphSettingsUnlinkedObjects') }, + { id: 'local', name: translate('menuGraphSettingsLocal') }, ] } ]; diff --git a/src/ts/component/util/graph.tsx b/src/ts/component/util/graph.tsx index c8bf33bc0e..b45c9ec6c7 100644 --- a/src/ts/component/util/graph.tsx +++ b/src/ts/component/util/graph.tsx @@ -83,7 +83,7 @@ const Graph = observer(class Graph extends React.Component { this.unbind(); win.on('updateGraphSettings.graph', () => { this.updateSettings(); }); - win.on('updateGraphRoot.graph', (e: any, data: any) => { this.setRootId(data.id); }); + win.on('updateGraphRoot.graph', (e: any, data: any) => this.setRootId(data.id)); win.on('updateTheme.graph', () => { this.send('updateTheme', { theme: commonStore.getThemeClass() }); }); }; @@ -138,8 +138,14 @@ const Graph = observer(class Graph extends React.Component { .call(this.zoom) .call(this.zoom.transform, d3.zoomIdentity.translate(0, 0).scale(1.5)) .on('click', (e: any) => { + const { local } = commonStore.graph; const [ x, y ] = d3.pointer(e); - this.send(e.shiftKey ? 'onSelect' : 'onClick', { x, y }); + + if (local) { + this.send('onSetRootId', { x, y }); + } else { + this.send(e.shiftKey ? 'onSelect' : 'onClick', { x, y }); + }; }) .on('dblclick', (e: any) => { if (e.shiftKey) { @@ -464,7 +470,7 @@ const Graph = observer(class Graph extends React.Component { }; setRootId (id: string) { - this.send('onSetRootId', { rootId: id }); + this.send('setRootId', { rootId: id }); }; send (id: string, param: any, transfer?: any[]) { diff --git a/src/ts/store/common.ts b/src/ts/store/common.ts index 9abcbac13d..093ad9906c 100644 --- a/src/ts/store/common.ts +++ b/src/ts/store/common.ts @@ -16,6 +16,7 @@ interface Graph { label: boolean; relation: boolean; link: boolean; + local: boolean; filter: string; }; @@ -64,6 +65,7 @@ class CommonStore { label: true, relation: true, link: true, + local: false, filter: '', };