diff --git a/dist/index.html b/dist/index.html
index df879d3..fbfed9f 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -1,78 +1,10 @@
Layoutr
+
-
-
-
+
diff --git a/package-lock.json b/package-lock.json
index 2f69f9e..dd04e61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -33,6 +33,64 @@
"integrity": "sha512-Uy0PN4R5vgBUXFoJrKryf5aTk3kJ8Rv3PdlHjl6UaX+Cqp1QE0yPQ68MPXGrZOfG7gZVNDIJZYyot0B9ubXUrQ==",
"dev": true
},
+ "@vue/component-compiler-utils": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-2.6.0.tgz",
+ "integrity": "sha512-IHjxt7LsOFYc0DkTncB7OXJL7UzwOLPPQCfEUNyxL2qt+tF12THV+EO33O1G2Uk4feMSWua3iD39Itszx0f0bw==",
+ "dev": true,
+ "requires": {
+ "consolidate": "^0.15.1",
+ "hash-sum": "^1.0.2",
+ "lru-cache": "^4.1.2",
+ "merge-source-map": "^1.1.0",
+ "postcss": "^7.0.14",
+ "postcss-selector-parser": "^5.0.0",
+ "prettier": "1.16.3",
+ "source-map": "~0.6.1",
+ "vue-template-es2015-compiler": "^1.9.0"
+ },
+ "dependencies": {
+ "cssesc": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+ "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^2.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ }
+ }
+ },
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@@ -987,6 +1045,15 @@
"date-now": "^0.1.4"
}
},
+ "consolidate": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
+ "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.1.1"
+ }
+ },
"constants-browserify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@@ -1435,6 +1502,12 @@
"integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
"dev": true
},
+ "de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=",
+ "dev": true
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -2990,6 +3063,12 @@
"safe-buffer": "^5.0.1"
}
},
+ "hash-sum": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
+ "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=",
+ "dev": true
+ },
"hash.js": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
@@ -3000,6 +3079,12 @@
"minimalistic-assert": "^1.0.1"
}
},
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -3626,11 +3711,6 @@
"object-visit": "^1.0.0"
}
},
- "materialize-css": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/materialize-css/-/materialize-css-1.0.0.tgz",
- "integrity": "sha512-4/oecXl8y/1i8RDZvyvwAICyqwNoKU4or5uf8uoAd74k76KzZ0Llym4zhJ5lLNUskcqjO0AuMcvNyDkpz8Z6zw=="
- },
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -3675,6 +3755,23 @@
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
"dev": true
},
+ "merge-source-map": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
+ "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@@ -4409,6 +4506,12 @@
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
+ "prettier": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.3.tgz",
+ "integrity": "sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==",
+ "dev": true
+ },
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@@ -4452,6 +4555,12 @@
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
"public-encrypt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
@@ -5867,6 +5976,61 @@
}
}
},
+ "vue": {
+ "version": "2.6.10",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
+ "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
+ },
+ "vue-hot-reload-api": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz",
+ "integrity": "sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==",
+ "dev": true
+ },
+ "vue-loader": {
+ "version": "15.7.0",
+ "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.7.0.tgz",
+ "integrity": "sha512-x+NZ4RIthQOxcFclEcs8sXGEWqnZHodL2J9Vq+hUz+TDZzBaDIh1j3d9M2IUlTjtrHTZy4uMuRdTi8BGws7jLA==",
+ "dev": true,
+ "requires": {
+ "@vue/component-compiler-utils": "^2.5.1",
+ "hash-sum": "^1.0.2",
+ "loader-utils": "^1.1.0",
+ "vue-hot-reload-api": "^2.3.0",
+ "vue-style-loader": "^4.1.0"
+ }
+ },
+ "vue-style-loader": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
+ "integrity": "sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ==",
+ "dev": true,
+ "requires": {
+ "hash-sum": "^1.0.2",
+ "loader-utils": "^1.0.2"
+ }
+ },
+ "vue-template-compiler": {
+ "version": "2.6.10",
+ "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz",
+ "integrity": "sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==",
+ "dev": true,
+ "requires": {
+ "de-indent": "^1.0.2",
+ "he": "^1.1.0"
+ }
+ },
+ "vue-template-es2015-compiler": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
+ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
+ "dev": true
+ },
+ "vuetify": {
+ "version": "1.5.16",
+ "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-1.5.16.tgz",
+ "integrity": "sha512-yBgOsfurKQkeS+l+rrTQZ2bFk0D9ezjHhkuVM5A/yVzcg62sY2nfYaq/H++uezBWC9WYFrp/5OmSocJQcWn9Qw=="
+ },
"watchpack": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
diff --git a/package.json b/package.json
index 5a03ad4..95a4e26 100644
--- a/package.json
+++ b/package.json
@@ -20,12 +20,15 @@
"dependencies": {
"d3": "^5.9.7",
"geojs": "^0.19.4",
- "materialize-css": "^1.0.0"
+ "vue": "^2.6.10",
+ "vuetify": "^1.5.16"
},
"devDependencies": {
"css-loader": "^3.0.0",
"gh-pages": "^2.0.1",
"style-loader": "^0.23.1",
+ "vue-loader": "^15.7.0",
+ "vue-template-compiler": "^2.6.10",
"webpack": "^4.35.2",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2",
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..6ad1a51
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,346 @@
+
+
+
+ Upload CSV or JSON
+
+
+
+
+ {{ layoutRunning ? 'Stop' : 'Start' }} layout
+
+
+
+
+
+
+
+
+
+ Download JSON
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index 032c53a..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,41 +0,0 @@
-html,body,#map{
- width: 100%;
- height: 100%;
- padding: 0;
- margin: 0;
- overflow: hidden;
-}
-#tooltip {
- margin-left: 0px;
- margin-top: -20px;
- height: 16px;
- line-height: 16px;
- padding: 2px 5px;
- background: rgba(255, 255, 255, 0.75);
- border-radius: 10px;
- border-bottom-left-radius: 0;
- border: 1px solid rgba(0, 0, 0, 0.75);
- font-size: 12px;
- color: black;
-}
-#tooltip.hidden {
- display: none;
-}
-.checkbox-wrapper {
- padding: 24px 24px 0px 24px;
- line-height: 1.6;
-}
-.slider-wrapper {
- padding: 0px 24px;
- /* Need to reset line-height since we're in a side nav */
- line-height: 1.6;
-}
-.slider-wrapper .slider-label {
- line-height: 3;
-}
-.noUi-horizontal .noUi-handle, .noUi-vertical .noUi-handle {
- background-color: #2196f3;
-}
-.noUi-target.noUi-horizontal .noUi-tooltip {
- background-color: #2196f3;
-}
diff --git a/src/index.js b/src/index.js
index e3a24ab..3b7cd8c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,343 +1,14 @@
-import 'materialize-css/dist/css/materialize.min.css';
-import 'materialize-css/dist/js/materialize.min.js';
-import 'materialize-css/extras/noUiSlider/nouislider.css';
-import noUiSlider from 'materialize-css/extras/noUiSlider/nouislider.min.js';
-import geo from 'geojs/geo.js';
+import 'vuetify/dist/vuetify.min.css';
-import LayoutWorker from 'worker-loader!./worker.js';
-import './index.css';
+import Vue from 'vue';
+import Vuetify from 'vuetify';
+import App from './App';
-let b = 20000;
-let bounds = {
- minx: -b,
- maxx: b,
- miny: -b,
- maxy: b,
-};
-let params = geo.util.pixelCoordinateParams(
- '#map', bounds.maxx - bounds.minx, bounds.maxy - bounds.miny);
+Vue.use(Vuetify);
+Vue.config.productionTip = false;
-// the utility function assumes top left is 0, 0. Move it to minx, miny.
-params.map.maxBounds.left += bounds.minx;
-params.map.maxBounds.top += bounds.miny;
-params.map.maxBounds.right += bounds.minx;
-params.map.maxBounds.bottom += bounds.miny;
-params.map.center.x += bounds.minx;
-params.map.center.y += bounds.miny;
-
-// inflate the bounds to add a border
-const maxwh = Math.max(bounds.maxx - bounds.minx, bounds.maxy - bounds.miny);
-params.map.maxBounds.left -= maxwh * 0.1;
-params.map.maxBounds.top -= maxwh * 0.1;
-params.map.maxBounds.right += maxwh * 0.1;
-params.map.maxBounds.bottom += maxwh * 0.1;
-
-// allow zoomming in until 1 unit of space is 2^(value) bigger.
-params.map.max += 3;
-const map = geo.map(params.map);
-let layer = map.createLayer('feature', {features: ['point', 'line']});
-
-const uiLayer = map.createLayer('ui', {zIndex: 2});
-const tooltip = uiLayer.createWidget('dom', {position: {x: 0, y: 0}});
-const tooltipElem = tooltip.canvas();
-tooltipElem.setAttribute('id', 'tooltip');
-tooltipElem.classList.toggle('hidden', true);
-tooltipElem.style['pointer-events'] = 'none';
-
-map.draw();
-
-let points;
-let lines;
-let graph;
-let positions;
-let nodeMap;
-let radiusFactor = 2;
-let edgeVisibility = false;
-let edgeOpacity = 0.5;
-
-var layoutWorker = new LayoutWorker();
-layoutWorker.onmessage = function(e) {
- if (e.data.type === 'graph') {
- graph = e.data.graph;
-
- map.deleteLayer(layer);
- layer = map.createLayer('feature', {features: ['point', 'line']});
-
- nodeMap = {};
- graph.nodes.forEach((n, i) => nodeMap[n.id] = i);
- lines = layer.createFeature('line').data(graph.edges.map(e => [nodeMap[e.source], nodeMap[e.target]])).style({
- position: nodeid => graph.nodes[nodeid],
- width: 1,
- strokeColor: 'black',
- strokeOpacity: edgeOpacity,
- });
- lines.visible(edgeVisibility);
- map.draw();
-
- points = layer.createFeature('point', {
- primitiveShape: 'triangle',
- style: {
- strokeColor: 'black',
- fillColor: 'grey',
- fillOpacity: 0.5,
- strokeOpacity: 0.5,
- radius: nodeid => Math.max(1, Math.pow(2, map.zoom()) * Math.sqrt(graph.nodes[nodeid].degree) * radiusFactor)
- },
- position: nodeid => graph.nodes[nodeid]
- }).data(Object.keys(graph.nodes));
-
- map.geoOn(geo.event.zoom, () => {
- points.modified().draw();
- });
-
- map.draw();
-
- points
- .geoOn(geo.event.feature.mouseon, function (evt) {
- const nodeid = evt.data, node = graph.nodes[nodeid];
- let text = node.id;
- if (text) {
- tooltip.position(evt.mouse.geo);
- tooltipElem.innerText = text;
- }
- tooltipElem.classList.toggle('hidden', !text);
- })
- .geoOn(geo.event.feature.mousemove, function (evt) {
- tooltip.position(evt.mouse.geo);
- })
- .geoOn(geo.event.feature.mouseoff, function (evt) {
- tooltipElem.classList.toggle('hidden', true);
- });
- }
- else if (e.data.type === 'positions') {
- positions = e.data.nodes;
- points.position(nodeid => positions[nodeid]);
- map.draw();
- }
- else if (e.data.type === 'alpha') {
- alphaFromWorker = true;
- alpha.noUiSlider.set(e.data.value);
- alphaFromWorker = false;
- }
-}
-
-document.getElementById('toggle-start').onclick = () => {
- let mode = document.getElementById('toggle-start').innerText.toLowerCase().split(' ')[0];
- layoutWorker.postMessage({type: mode});
- if (mode === 'start') {
- alpha.setAttribute('disabled', true);
- // Don't draw edges while performing layout for performance reasons
- lines.visible(false);
- } else {
- alpha.removeAttribute('disabled');
- // Reenable edge drawing
- if (edgeVisibility) {
- lines.visible(true);
- lines.position(nodeid => positions[nodeid]);
- map.draw();
- }
- }
- document.getElementById('toggle-start').innerText = (mode === 'start' ? 'Stop Layout': 'Start Layout');
-}
-
-document.getElementById('save').onclick = () => {
- const nodesWithPositions = graph.nodes.map((n, i) => ({
- ...n,
- ...positions[i],
- }));
- const saveGraph = {
- nodes: nodesWithPositions,
- edges: graph.edges,
- }
- var blob = new Blob([JSON.stringify(saveGraph, null, 2)], {
- type : "data:text/json;charset=utf-8;"
- });
- const dl = document.getElementById('download');
- dl.setAttribute("href", URL.createObjectURL(blob));
- dl.setAttribute("download", "scene.json");
- dl.click();
-}
-
-document.getElementById('init-upload-edge-list').onclick = () => {
- const upload = document.getElementById('upload-edge-list');
- upload.click();
-}
-
-document.getElementById('upload-edge-list').onchange = () => {
- var file = document.getElementById('upload-edge-list').files[0];
- if (file) {
- var reader = new FileReader();
- reader.readAsText(file, 'UTF-8');
- reader.onload = function (evt) {
- layoutWorker.postMessage({type: 'loadEdgeList', text: evt.target.result});
- }
- reader.onerror = function (evt) {
- console.log('Error: ', evt);
- }
- }
-}
-
-document.getElementById('init-upload-json').onclick = () => {
- const upload = document.getElementById('upload-json');
- upload.click();
-}
-
-document.getElementById('upload-json').onchange = () => {
- var file = document.getElementById('upload-json').files[0];
- if (file) {
- var reader = new FileReader();
- reader.readAsText(file, 'UTF-8');
- reader.onload = function (evt) {
- layoutWorker.postMessage({type: 'loadJSON', text: evt.target.result});
- }
- reader.onerror = function (evt) {
- console.log('Error: ', evt);
- }
- }
-}
-
-function fixedFormat(n) {
- return {
- to: function (value) {
- return value.toFixed(n);
- },
- from: function (value) {
- return Number(value);
- },
- };
-}
-
-let theta = document.getElementById('theta');
-noUiSlider.create(theta, {
- start: 1.5,
- step: 0.1,
- range: {min: 0.5, max: 3.0},
- format: fixedFormat(1),
-});
-theta.noUiSlider.on('update', () => {
- layoutWorker.postMessage({
- type: 'theta',
- value: theta.noUiSlider.get(),
- });
-});
-
-let alpha = document.getElementById('alpha');
-let alphaFromWorker = false;
-noUiSlider.create(alpha, {
- start: 1.0,
- step: 0.01,
- range: {min: 0.0, max: 1.0},
- format: fixedFormat(2),
-});
-alpha.noUiSlider.on('update', () => {
- if (!alphaFromWorker) {
- layoutWorker.postMessage({
- type: 'alpha',
- value: alpha.noUiSlider.get(),
- });
- }
-});
-
-let radiusFactorSlider = document.getElementById('radius-factor');
-noUiSlider.create(radiusFactorSlider, {
- start: 2.0,
- step: 0.1,
- range: {min: 0.1, max: 10.0},
- format: fixedFormat(1),
-});
-radiusFactorSlider.noUiSlider.on('update', () => {
- radiusFactor = radiusFactorSlider.noUiSlider.get();
- if (points) {
- points.modified();
- map.draw();
- }
- layoutWorker.postMessage({
- type: 'radiusFactor',
- value: radiusFactorSlider.noUiSlider.get(),
- });
-});
-
-let linkStrengthSlider = document.getElementById('link-strength');
-noUiSlider.create(linkStrengthSlider, {
- start: 1.0,
- step: 0.01,
- range: {min: 0.0, max: 1.0},
- format: fixedFormat(2),
-});
-linkStrengthSlider.noUiSlider.on('update', () => {
- layoutWorker.postMessage({
- type: 'linkStrength',
- value: linkStrengthSlider.noUiSlider.get(),
- });
-});
-
-let chargeStrengthSlider = document.getElementById('charge-strength');
-noUiSlider.create(chargeStrengthSlider, {
- start: -30,
- step: 1,
- range: {min: -50, max: 50},
- format: fixedFormat(0),
-});
-chargeStrengthSlider.noUiSlider.on('update', () => {
- layoutWorker.postMessage({
- type: 'chargeStrength',
- value: chargeStrengthSlider.noUiSlider.get(),
- });
-});
-
-let collideStrengthSlider = document.getElementById('collide-strength');
-noUiSlider.create(collideStrengthSlider, {
- start: 0.7,
- step: 0.01,
- range: {min: 0.0, max: 1.0},
- format: fixedFormat(2),
-});
-collideStrengthSlider.noUiSlider.on('update', () => {
- layoutWorker.postMessage({
- type: 'collideStrength',
- value: collideStrengthSlider.noUiSlider.get(),
- });
-});
-
-document.getElementById('charge').onchange = () => {
- layoutWorker.postMessage({type: 'charge', value: !!document.getElementById('charge').checked});
-}
-
-document.getElementById('link').onchange = () => {
- layoutWorker.postMessage({type: 'link', value: !!document.getElementById('link').checked});
-}
-
-document.getElementById('collide').onchange = () => {
- layoutWorker.postMessage({type: 'collide', value: !!document.getElementById('collide').checked});
-}
-
-document.getElementById('center').onchange = () => {
- layoutWorker.postMessage({type: 'center', value: !!document.getElementById('center').checked});
-}
-
-document.getElementById('show-edges').onchange = () => {
- edgeVisibility = !!document.getElementById('show-edges').checked;
- if (lines) {
- lines.visible(edgeVisibility);
- if (edgeVisibility) {
- lines.position(nodeid => positions[nodeid]);
- }
- map.draw();
- }
-}
-
-let edgeOpacitySlider = document.getElementById('edge-opacity');
-noUiSlider.create(edgeOpacitySlider, {
- start: 0.5,
- step: 0.01,
- range: {min: 0.01, max: 1.0},
- format: fixedFormat(2),
-});
-edgeOpacitySlider.noUiSlider.on('update', () => {
- edgeOpacity = edgeOpacitySlider.noUiSlider.get();
- if (lines) {
- lines.style('strokeOpacity', edgeOpacity);
- lines.modified();
- map.draw();
- }
+new Vue({
+ el: '#app',
+ components: { App },
+ template: '',
});
diff --git a/src/worker.js b/src/worker.js
index 2bd5f8a..126b36a 100644
--- a/src/worker.js
+++ b/src/worker.js
@@ -1,7 +1,7 @@
let d3 = require('d3/dist/d3.js');
-let radiusFactor = 2;
-let linkStrength = 0.1;
+let radius = 2;
+let linkStrength = 1;
let linkStrengthFunctions = {
inverseMinDegree: link => linkStrength / Math.min(link.source.degree, link.target.degree),
@@ -10,12 +10,12 @@ let linkStrengthFunctions = {
};
let linkDistanceFunctions = {
- sumSqrtDegree: link => (Math.sqrt(link.source.degree) + Math.sqrt(link.target.degree)) * radiusFactor,
+ sumSqrtDegree: link => (Math.sqrt(link.source.degree) + Math.sqrt(link.target.degree)) * radius,
};
let link = d3.forceLink().id(d => d.id).distance(linkDistanceFunctions.sumSqrtDegree).strength(linkStrengthFunctions.inverseMinDegree);
let charge = d3.forceManyBody();
-let collide = d3.forceCollide().radius(d => Math.sqrt(d.degree) * radiusFactor);
+let collide = d3.forceCollide().radius(d => Math.sqrt(d.degree) * radius);
let center = d3.forceCenter();
// let radial = d3.forceX(d => ((d.discovery ? d.discovery : 2020) - 1900) * 150).strength(1);
let simulation = d3.forceSimulation()
@@ -67,11 +67,12 @@ loadGraph = function(graph) {
}
onmessage = function(e) {
- if (e.data.type === 'stop') {
- simulation.stop();
- }
- else if (e.data.type === 'start') {
- simulation.restart();
+ if (e.data.type === 'layout') {
+ if (e.data.value) {
+ simulation.restart();
+ } else {
+ simulation.stop();
+ }
}
else if (e.data.type === 'loadEdgeList') {
loadGraph({edges: d3.csvParse(e.data.text)});
@@ -85,10 +86,10 @@ onmessage = function(e) {
else if (e.data.type === 'alpha') {
simulation.alpha(e.data.value);
}
- else if (e.data.type === 'radiusFactor') {
- radiusFactor = e.data.value;
+ else if (e.data.type === 'radius') {
+ radius = e.data.value;
link.strength(linkStrengthFunctions.inverseMinDegree);
- collide.radius(d => Math.sqrt(d.degree) * radiusFactor);
+ collide.radius(d => Math.sqrt(d.degree) * radius);
}
else if (e.data.type === 'linkStrength') {
linkStrength = e.data.value;
diff --git a/webpack.config.js b/webpack.config.js
index c703b71..a23891e 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,4 +1,5 @@
const path = require('path');
+const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
mode: 'development',
@@ -8,14 +9,27 @@ module.exports = {
},
module: {
rules: [
+ {
+ test: /\.vue$/,
+ loader: 'vue-loader',
+ },
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
+ resolve: {
+ extensions: ['.js', '.vue'],
+ alias: {
+ 'vue$': 'vue/dist/vue.js',
+ }
+ },
output: {
path: path.resolve(__dirname, 'dist'),
- filename: 'bundle.js'
+ filename: 'bundle.js',
},
+ plugins: [
+ new VueLoaderPlugin(),
+ ],
};