diff --git a/.eslintrc b/.eslintrc index 1905a4d..f0e14f8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,15 +1,41 @@ parserOptions: - sourceType: module + sourceType: module extends: - "eslint:recommended" - + "standard" rules: - no-cond-assign: 0 - no-console: 0 - + no-cond-assign: 0 + no-console: 0 + semi: + - error + - always +/* + no-extra-parens: 1 + curly: 0 + block-scoped-var: 1 + no-tabs: 1 + no-negated-condition: 1 + block-spacing: 1 + brace-style: 1 + comma-spacing: 1 + comma-style: 1 + eol-last: 1 + func-call-spacing: 1 + indent: + - error + - 2 + key-spacing: 1 + linebreak-style: 1 + no-lonely-if: 1 +*/ env: es6: true globals: console: false +/* +plugins: + - import + - node + - promise +*/ diff --git a/index.js b/index.js index ddb7046..a383b74 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,17 @@ export {default as forceNeighbourSampling} - from "./src/neighbourSampling"; + from './src/neighbourSampling'; export { default as forceBarnesHut} - from "./src/barnesHut"; + from './src/barnesHut'; export { default as tSNE} - from "./src/t-sne"; + from './src/t-sne'; export { default as forceLinkCompleteGraph} - from "./src/link"; + from './src/link'; export { default as hybridSimulation} - from "./src/hybridSimulation"; + from './src/hybridSimulation'; export { getStress as calculateStress } - from "./src/stress"; + from './src/stress'; diff --git a/package.json b/package.json index 65c51c2..789da4c 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,11 @@ }, "devDependencies": { "eslint": "4", + "eslint-config-standard": "^11.0.0", + "eslint-plugin-import": "^2.9.0", + "eslint-plugin-node": "^6.0.1", + "eslint-plugin-promise": "^3.7.0", + "eslint-plugin-standard": "^3.0.1", "rollup": "0.36", "uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony" }, diff --git a/src/barnesHut.js b/src/barnesHut.js index 24f3a5b..0192c53 100644 --- a/src/barnesHut.js +++ b/src/barnesHut.js @@ -1,7 +1,7 @@ -import constant from "./constant"; -import jiggle from "./jiggle"; -import {x, y} from "./xy"; -import {quadtree} from "d3-quadtree"; +import constant from './constant'; +import jiggle from './jiggle'; +import {x, y} from './xy'; +import {quadtree} from 'd3-quadtree'; /** * The refinement of the existing Barnes-Hut implementation in D3 @@ -12,7 +12,7 @@ import {quadtree} from "d3-quadtree"; * The check to see if the nodes are far away was also changed to the one described in original Barnes-Hut paper. * @return {force} calculated forces. */ -export default function() { +export default function () { var nodes, node, alpha, @@ -25,7 +25,7 @@ export default function() { * @param {number} _ - controls the stopping of the * particle simulations. */ - function force(_) { + function force (_) { var i, n = nodes.length, tree = quadtree(nodes, x, y).visitAfter(accumulate); for (alpha = _, i = 0; i < n; ++i) { node = nodes[i], tree.visit(apply); @@ -38,7 +38,7 @@ export default function() { * nodes accumulate forces from coincident quadrants. * @param {quadrant} quad - node representing the quadrant in quadtree. */ - function accumulate(quad) { + function accumulate (quad) { var q, d, children = []; // For internal nodes, accumulate forces from child quadrants. @@ -69,8 +69,7 @@ export default function() { * @param {number} x2 - upper x bound of the node. * @return {boolean} - true if the approximation was applied. */ - function apply(quad, x1, _, x2) { - + function apply (quad, x1, _, x2) { var x = quad.data.x + quad.data.vx - node.x - node.vx, y = quad.data.y + quad.data.vy - node.y - node.vy, w = x2 - x1, @@ -98,13 +97,15 @@ export default function() { if (y === 0) y = jiggle(), l += y * y; } - do if (quad.data !== node) { - l = (l - +distance(node, quad.data)) / l * alpha; - x *= l, y *= l; - quad.data.vx -= x; - quad.data.vy -= y; - node.vx += x; - node.vy += y; + do { + if (quad.data !== node) { + l = (l - +distance(node, quad.data)) / l * alpha; + x *= l, y *= l; + quad.data.vx -= x; + quad.data.vy -= y; + node.vx += x; + node.vy += y; + } } while (quad = quad.next); } @@ -114,37 +115,37 @@ export default function() { * the better layout. * @return {number} - stress of the layout. */ - function getStress() { + function getStress () { var totalDiffSq = 0, totalHighDistSq = 0; for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) { for (var j = 0; j < nodes.length; j++) { if (i !== j) { source = nodes[i], target = nodes[j]; - realDist = Math.hypot(target.x-source.x, target.y-source.y); + realDist = Math.hypot(target.x - source.x, target.y - source.y); highDist = +distance(nodes[i], nodes[j]); - totalDiffSq += Math.pow(realDist-highDist, 2); + totalDiffSq += Math.pow(realDist - highDist, 2); totalHighDistSq += highDist * highDist; } } } - return Math.sqrt(totalDiffSq/totalHighDistSq); + return Math.sqrt(totalDiffSq / totalHighDistSq); } // API for initializing the algorithm, setting parameters and querying // metrics. - force.initialize = function(_) { + force.initialize = function (_) { nodes = _; }; - force.distance = function(_) { - return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance; + force.distance = function (_) { + return arguments.length ? (distance = typeof _ === 'function' ? _ : constant(+_), force) : distance; }; - force.theta = function(_) { + force.theta = function (_) { return arguments.length ? (theta = _, force) : theta; }; - force.stress = function() { + force.stress = function () { return getStress(); }; diff --git a/src/constant.js b/src/constant.js index 5f5f1ea..cad0019 100644 --- a/src/constant.js +++ b/src/constant.js @@ -1,8 +1,8 @@ /** * @return a constant defined by x. */ -export default function(x) { - return function() { +export default function (x) { + return function () { return x; }; } diff --git a/src/interpolation/helpers.js b/src/interpolation/helpers.js index 6e2f5b2..d0ae6d8 100644 --- a/src/interpolation/helpers.js +++ b/src/interpolation/helpers.js @@ -8,7 +8,7 @@ sample is the list of selected objects while remainder is the list of those unselected. */ -export function takeSampleFrom(sourceList, amount) { +export function takeSampleFrom (sourceList, amount) { let randElements = [], max = sourceList.length, swap = false; @@ -18,7 +18,7 @@ export function takeSampleFrom(sourceList, amount) { } // If picking more than half of the entire set, random to pick the remainder instead - if (amount > Math.ceil(max/2)){ + if (amount > Math.ceil(max / 2)) { amount = max - amount; swap = true; } @@ -35,7 +35,7 @@ export function takeSampleFrom(sourceList, amount) { return !randElements.includes(obj); }); - if(swap) { + if (swap) { return { sample: remainder, remainder: randElements @@ -57,14 +57,14 @@ export function takeSampleFrom(sourceList, amount) { * @param {number} r * @return {object} - coordinate {x: number, y: number} of the point */ -export function pointOnCircle(h, k, angle, r) { +export function pointOnCircle (h, k, angle, r) { return { - x: h + r*Math.cos(toRadians(angle)), - y: k + r*Math.sin(toRadians(angle)) + x: h + r * Math.cos(toRadians(angle)), + y: k + r * Math.sin(toRadians(angle)) }; } -function toRadians(degrees) { +function toRadians (degrees) { return degrees * (Math.PI / 180); } @@ -79,7 +79,7 @@ function toRadians(degrees) { that of samples. * @return {number} - Sum of distances differences */ -export function sumDistError(node, samples, realDistances) { +export function sumDistError (node, samples, realDistances) { let total = 0.0; for (let i = 0; i < samples.length; i++) { let sample = samples[i]; diff --git a/src/jiggle.js b/src/jiggle.js index 4450848..ffd90ba 100644 --- a/src/jiggle.js +++ b/src/jiggle.js @@ -1,7 +1,7 @@ /** * @return {number} a very small non-zero random number. */ -export default function() { +export default function () { let rand; do { rand = (Math.random() - 0.5) * 1e-6; diff --git a/src/link.js b/src/link.js index 3f046e8..80c7af8 100644 --- a/src/link.js +++ b/src/link.js @@ -1,5 +1,5 @@ -import constant from "./constant"; -import jiggle from "./jiggle"; +import constant from './constant'; +import jiggle from './jiggle'; /** * Modified link force algorithm @@ -8,7 +8,7 @@ import jiggle from "./jiggle"; * - removed other unused functions * Alpha should be constant 1 for accurate simulation */ -export default function() { +export default function () { var dataSizeFactor, distance = constant(30), distances = [], @@ -18,60 +18,62 @@ export default function() { latestVelocityDiff = 0, iterations = 1; - function force(alpha) { + function force (alpha) { let n = nodes.length; // Cache old velocity for comparison later - if (stableVeloHandler!==null && stableVelocity>=0) { - for (let i = n-1, node; i>=0; i--) { + if (stableVeloHandler !== null && stableVelocity >= 0) { + for (let i = n - 1, node; i >= 0; i--) { node = nodes[i]; node.oldvx = node.vx; node.oldvy = node.vy; } } - + // Each iteration in a tick for (var k = 0, source, target, i, j, x, y, l; k < iterations; ++k) { // For each link - for (i = 1; i < n; i++) for (j = 0; j < i; j++) { + for (i = 1; i < n; i++) { + for (j = 0; j < i; j++) { // jiggle so l won't be zero and divide by zero error after this - source = nodes[i]; - target = nodes[j]; - x = target.x + target.vx - source.x - source.vx || jiggle(); - y = target.y + target.vy - source.y - source.vy || jiggle(); - l = Math.sqrt(x * x + y * y); - l = (l - distances[i*(i-1)/2+j]) / l * dataSizeFactor * alpha; - x *= l, y *= l; - target.vx -= x; - target.vy -= y; - source.vx += x; - source.vy += y; + source = nodes[i]; + target = nodes[j]; + x = target.x + target.vx - source.x - source.vx || jiggle(); + y = target.y + target.vy - source.y - source.vy || jiggle(); + l = Math.sqrt(x * x + y * y); + l = (l - distances[i * (i - 1) / 2 + j]) / l * dataSizeFactor * alpha; + x *= l, y *= l; + target.vx -= x; + target.vy -= y; + source.vx += x; + source.vy += y; + } } } // Calculate velocity changes, aka force applied. - if (stableVeloHandler!==null && stableVelocity>=0) { + if (stableVeloHandler !== null && stableVelocity >= 0) { let velocityDiff = 0; - for (let i = n-1, node; i>=0; i--) { + for (let i = n - 1, node; i >= 0; i--) { node = nodes[i]; - velocityDiff += Math.abs(Math.hypot(node.vx-node.oldvx, node.vy-node.oldvy)); + velocityDiff += Math.abs(Math.hypot(node.vx - node.oldvx, node.vy - node.oldvy)); } velocityDiff /= n; latestVelocityDiff = velocityDiff; - if(velocityDiff=0) { - for (let i = n-1, node; i>=0; i--) { + if (stableVeloHandler !== null && stableVelocity >= 0) { + for (let i = n - 1, node; i >= 0; i--) { node = nodes[i]; node.oldvx = node.vx; node.oldvy = node.vy; } } - for (let i = n-1, node, samples; i>=0; i--) { + for (let i = n - 1, node, samples; i >= 0; i--) { node = nodes[i]; samples = createRandomSamples(i); @@ -52,16 +52,16 @@ export default function () { } // Calculate velocity changes, aka force applied. - if (stableVeloHandler!==null && stableVelocity>=0) { + if (stableVeloHandler !== null && stableVelocity >= 0) { let velocityDiff = 0; - for (let i = n-1, node; i>=0; i--) { + for (let i = n - 1, node; i >= 0; i--) { node = nodes[i]; - velocityDiff += Math.abs(Math.hypot(node.vx-node.oldvx, node.vy-node.oldvy)); + velocityDiff += Math.abs(Math.hypot(node.vx - node.oldvx, node.vy - node.oldvy)); } velocityDiff /= n; latestVelocityDiff = velocityDiff; - if(velocityDiff=0; i--) { + for (let i = nodes.length - 1; i >= 0; i--) { let neighbs = pickRandomNodesFor(i, [i], neighbourSize); // Sort the neighbour set by the distances. neighbours[i] = new Map(neighbs.sort(sortDistances)); @@ -103,8 +103,8 @@ export default function () { initDataSizeFactor(); } - function initDataSizeFactor(){ - dataSizeFactor = 0.5/(neighbourSize+sampleSize); + function initDataSizeFactor () { + dataSizeFactor = 0.5 / (neighbourSize + sampleSize); } /** @@ -116,7 +116,7 @@ export default function () { * @param {number} size - max number of elements in the map to return. * @return {array} */ - function pickRandomNodesFor(index, exclude, size) { + function pickRandomNodesFor (index, exclude, size) { let randElements = []; let max = nodes.length; @@ -133,7 +133,7 @@ export default function () { } randElements.push(rand); } - for(let i=randElements.length-1, rand; i>=0; i--){ + for (let i = randElements.length - 1, rand; i >= 0; i--) { rand = randElements[i]; randElements[i] = [rand, distance(nodes[index], nodes[rand])]; } @@ -146,7 +146,7 @@ export default function () { * @param {number} index - index of the node to generate sample for * @return {map} */ - function createRandomSamples(index) { + function createRandomSamples (index) { // Ignore the current neighbours of the node and itself. let exclude = [index]; exclude = exclude.concat(Array.from(neighbours[index].keys())); @@ -160,13 +160,12 @@ export default function () { * @param {map} samples - map of samples * @return {map} - new map of neighbours */ - function findNewNeighbours(neighbours, samples) { + function findNewNeighbours (neighbours, samples) { let combined = [...neighbours.entries()].concat([...samples.entries()]); combined = combined.sort(sortDistances); return new Map(combined.slice(0, neighbourSize)); } - // API for initializing the algorithm and setting parameters force.initialize = function (_) { nodes = _; @@ -186,7 +185,7 @@ export default function () { }; force.distance = function (_) { - return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance; + return arguments.length ? (distance = typeof _ === 'function' ? _ : constant(+_), force) : distance; }; force.latestAccel = function () { diff --git a/src/stress.js b/src/stress.js index 6d75880..df54fec 100644 --- a/src/stress.js +++ b/src/stress.js @@ -4,10 +4,10 @@ * to the better layout. * @return {number} - stress of the layout. */ -export function getStress(nodes, distance) { - let sumDiffSq = 0 +export function getStress (nodes, distance) { + let sumDiffSq = 0; let sumLowDDistSq = 0; - for (let j = nodes.length-1; j >= 1; j--) { + for (let j = nodes.length - 1; j >= 1; j--) { for (let i = 0; i < j; i++) { let source = nodes[i], target = nodes[j]; let lowDDist = Math.hypot(target.x - source.x, target.y - source.y); diff --git a/src/t-sne.js b/src/t-sne.js index 9314735..d27ab32 100644 --- a/src/t-sne.js +++ b/src/t-sne.js @@ -1,5 +1,5 @@ /* eslint-disable block-scoped-var */ -import constant from "./constant"; +import constant from './constant'; /** * Set the node id accessor to the specified i. @@ -7,7 +7,7 @@ import constant from "./constant"; * @param {accessor} i - id accessor. * @return {accessor} - node id accessor. */ -function index(d, i) { +function index (d, i) { return i; } @@ -15,7 +15,7 @@ function index(d, i) { * t-SNE implementation in D3 by using the code existing in tsnejs * (https://github.com/karpathy/tsnejs) to compute the solution. */ -export default function() { +export default function () { var id = index, distance = constant(300), nodes, @@ -33,7 +33,7 @@ export default function() { * Make a step in t-SNE algorithm and set the velocities for the nodes * to accumulate the values from solution. */ - function force() { + function force () { // Make a step at each iteration. step(); var solution = getSolution(); @@ -49,7 +49,7 @@ export default function() { * Calculates the random number from Gaussian distribution. * @return {number} random number. */ - function gaussRandom() { + function gaussRandom () { let u = 2 * Math.random() - 1; let v = 2 * Math.random() - 1; let r = u * u + v * v; @@ -63,11 +63,11 @@ export default function() { * Return the normalized number. * @return {number} normalized random number from Gaussian distribution. */ - function randomN() { + function randomN () { return gaussRandom() * 1e-4; } - function sign(x) { + function sign (x) { return x > 0 ? 1 : x < 0 ? -1 : 0; } @@ -76,7 +76,7 @@ export default function() { * @param {number} n - length of array. * @return {Float64Array} - array of zeros with length n. */ - function zeros(n) { + function zeros (n) { if (typeof n === 'undefined' || isNaN(n)) { return []; } @@ -90,7 +90,7 @@ export default function() { * @param {number} d - columns. * @return {array} - 2d array */ - function random2d(n, d) { + function random2d (n, d) { var x = []; for (var i = 0; i < n; i++) { var y = []; @@ -109,7 +109,7 @@ export default function() { * @param {number} tol - limit for entropy difference. * @return {2d array} - 2d matrix containing probabilities. */ - function d2p(data, perplexity, tol) { + function d2p (data, perplexity, tol) { N = Math.floor(data.length); var Htarget = Math.log(perplexity); // target entropy of distribution. var P1 = zeros(N * N); // temporary probability matrix. @@ -161,7 +161,6 @@ export default function() { } else { beta = (beta + betamax) / 2; } - } else { // Converse case. Make distrubtion less peaky. betamax = beta; @@ -183,7 +182,6 @@ export default function() { for (j = 0; j < N; j++) { P1[i * N + j] = prow[j]; } - } // Symmetrize P and normalize it to sum to 1 over all ij @@ -200,7 +198,7 @@ export default function() { /** * Initialize a starting (random) solution. */ - function initSolution() { + function initSolution () { Y = random2d(N, dim); // Step gains to accelerate progress in unchanging directions. gains = random2d(N, dim, 1.0); @@ -212,7 +210,7 @@ export default function() { /** * @return {2d array} the solution. */ - function getSolution() { + function getSolution () { return Y; } @@ -220,7 +218,7 @@ export default function() { * Do a single step (iteration) for the layout. * @return {number} the current cost. */ - function step() { + function step () { iteration += 1; var cg = costGrad(Y); // Evaluate gradient. @@ -269,8 +267,7 @@ export default function() { * @param {2d array} Y - the current solution to evaluate. * @return {object} that contains a cost and a gradient. */ - function costGrad(Y) { - + function costGrad (Y) { var pmul = iteration < 100 ? 4 : 1; // Compute current Q distribution, unnormalized first. @@ -326,7 +323,7 @@ export default function() { * the better layout. * @return {number} - stress of the layout. */ - function getStress() { + function getStress () { var totalDiffSq = 0, totalHighDistSq = 0; for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) { @@ -345,7 +342,7 @@ export default function() { // API for initializing the algorithm, setting parameters and querying // metrics. - force.initialize = function(_) { + force.initialize = function (_) { nodes = _; N = nodes.length; // Initialize the probability matrix. @@ -353,23 +350,23 @@ export default function() { initSolution(); }; - force.id = function(_) { + force.id = function (_) { return arguments.length ? (id = _, force) : id; }; - force.distance = function(_) { - return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance; + force.distance = function (_) { + return arguments.length ? (distance = typeof _ === 'function' ? _ : constant(+_), force) : distance; }; - force.stress = function() { + force.stress = function () { return getStress(); }; - force.learningRate = function(_) { + force.learningRate = function (_) { return arguments.length ? (learningRate = +_, force) : learningRate; }; - force.perplexity = function(_) { + force.perplexity = function (_) { return arguments.length ? (perplexity = +_, force) : perplexity; }; diff --git a/src/xy.js b/src/xy.js index 647cc76..161144d 100644 --- a/src/xy.js +++ b/src/xy.js @@ -1,13 +1,13 @@ /** * @return x value of a node */ -export function x(d) { +export function x (d) { return d.x; } /** * @return y value of a node */ -export function y(d) { +export function y (d) { return d.y; }