1(function () { 2 'use strict'; 3 4 var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 5 6 function createCommonjsModule(fn, module) { 7 return module = { exports: {} }, fn(module, module.exports), module.exports; 8 } 9 10 var underscore = createCommonjsModule(function (module, exports) { 11 // Underscore.js 1.9.1 12 // http://underscorejs.org 13 // (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 14 // Underscore may be freely distributed under the MIT license. 15 16 (function() { 17 18 // Baseline setup 19 // -------------- 20 21 // Establish the root object, `window` (`self`) in the browser, `global` 22 // on the server, or `this` in some virtual machines. We use `self` 23 // instead of `window` for `WebWorker` support. 24 var root = typeof self == 'object' && self.self === self && self || 25 typeof commonjsGlobal == 'object' && commonjsGlobal.global === commonjsGlobal && commonjsGlobal || 26 this || 27 {}; 28 29 // Save the previous value of the `_` variable. 30 var previousUnderscore = root._; 31 32 // Save bytes in the minified (but not gzipped) version: 33 var ArrayProto = Array.prototype, ObjProto = Object.prototype; 34 var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; 35 36 // Create quick reference variables for speed access to core prototypes. 37 var push = ArrayProto.push, 38 slice = ArrayProto.slice, 39 toString = ObjProto.toString, 40 hasOwnProperty = ObjProto.hasOwnProperty; 41 42 // All **ECMAScript 5** native function implementations that we hope to use 43 // are declared here. 44 var nativeIsArray = Array.isArray, 45 nativeKeys = Object.keys, 46 nativeCreate = Object.create; 47 48 // Naked function reference for surrogate-prototype-swapping. 49 var Ctor = function(){}; 50 51 // Create a safe reference to the Underscore object for use below. 52 var _ = function(obj) { 53 if (obj instanceof _) return obj; 54 if (!(this instanceof _)) return new _(obj); 55 this._wrapped = obj; 56 }; 57 58 // Export the Underscore object for **Node.js**, with 59 // backwards-compatibility for their old module API. If we're in 60 // the browser, add `_` as a global object. 61 // (`nodeType` is checked to ensure that `module` 62 // and `exports` are not HTML elements.) 63 if (!exports.nodeType) { 64 if (!module.nodeType && module.exports) { 65 exports = module.exports = _; 66 } 67 exports._ = _; 68 } else { 69 root._ = _; 70 } 71 72 // Current version. 73 _.VERSION = '1.9.1'; 74 75 // Internal function that returns an efficient (for current engines) version 76 // of the passed-in callback, to be repeatedly applied in other Underscore 77 // functions. 78 var optimizeCb = function(func, context, argCount) { 79 if (context === void 0) return func; 80 switch (argCount == null ? 3 : argCount) { 81 case 1: return function(value) { 82 return func.call(context, value); 83 }; 84 // The 2-argument case is omitted because weâre not using it. 85 case 3: return function(value, index, collection) { 86 return func.call(context, value, index, collection); 87 }; 88 case 4: return function(accumulator, value, index, collection) { 89 return func.call(context, accumulator, value, index, collection); 90 }; 91 } 92 return function() { 93 return func.apply(context, arguments); 94 }; 95 }; 96 97 var builtinIteratee; 98 99 // An internal function to generate callbacks that can be applied to each 100 // element in a collection, returning the desired result â either `identity`, 101 // an arbitrary callback, a property matcher, or a property accessor. 102 var cb = function(value, context, argCount) { 103 if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); 104 if (value == null) return _.identity; 105 if (_.isFunction(value)) return optimizeCb(value, context, argCount); 106 if (_.isObject(value) && !_.isArray(value)) return _.matcher(value); 107 return _.property(value); 108 }; 109 110 // External wrapper for our callback generator. Users may customize 111 // `_.iteratee` if they want additional predicate/iteratee shorthand styles. 112 // This abstraction hides the internal-only argCount argument. 113 _.iteratee = builtinIteratee = function(value, context) { 114 return cb(value, context, Infinity); 115 }; 116 117 // Some functions take a variable number of arguments, or a few expected 118 // arguments at the beginning and then a variable number of values to operate 119 // on. This helper accumulates all remaining arguments past the functionâs 120 // argument length (or an explicit `startIndex`), into an array that becomes 121 // the last argument. Similar to ES6âs "rest parameter". 122 var restArguments = function(func, startIndex) { 123 startIndex = startIndex == null ? func.length - 1 : +startIndex; 124 return function() { 125 var length = Math.max(arguments.length - startIndex, 0), 126 rest = Array(length), 127 index = 0; 128 for (; index < length; index++) { 129 rest[index] = arguments[index + startIndex]; 130 } 131 switch (startIndex) { 132 case 0: return func.call(this, rest); 133 case 1: return func.call(this, arguments[0], rest); 134 case 2: return func.call(this, arguments[0], arguments[1], rest); 135 } 136 var args = Array(startIndex + 1); 137 for (index = 0; index < startIndex; index++) { 138 args[index] = arguments[index]; 139 } 140 args[startIndex] = rest; 141 return func.apply(this, args); 142 }; 143 }; 144 145 // An internal function for creating a new object that inherits from another. 146 var baseCreate = function(prototype) { 147 if (!_.isObject(prototype)) return {}; 148 if (nativeCreate) return nativeCreate(prototype); 149 Ctor.prototype = prototype; 150 var result = new Ctor; 151 Ctor.prototype = null; 152 return result; 153 }; 154 155 var shallowProperty = function(key) { 156 return function(obj) { 157 return obj == null ? void 0 : obj[key]; 158 }; 159 }; 160 161 var has = function(obj, path) { 162 return obj != null && hasOwnProperty.call(obj, path); 163 }; 164 165 var deepGet = function(obj, path) { 166 var length = path.length; 167 for (var i = 0; i < length; i++) { 168 if (obj == null) return void 0; 169 obj = obj[path[i]]; 170 } 171 return length ? obj : void 0; 172 }; 173 174 // Helper for collection methods to determine whether a collection 175 // should be iterated as an array or as an object. 176 // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength 177 // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 178 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; 179 var getLength = shallowProperty('length'); 180 var isArrayLike = function(collection) { 181 var length = getLength(collection); 182 return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; 183 }; 184 185 // Collection Functions 186 // -------------------- 187 188 // The cornerstone, an `each` implementation, aka `forEach`. 189 // Handles raw objects in addition to array-likes. Treats all 190 // sparse array-likes as if they were dense. 191 _.each = _.forEach = function(obj, iteratee, context) { 192 iteratee = optimizeCb(iteratee, context); 193 var i, length; 194 if (isArrayLike(obj)) { 195 for (i = 0, length = obj.length; i < length; i++) { 196 iteratee(obj[i], i, obj); 197 } 198 } else { 199 var keys = _.keys(obj); 200 for (i = 0, length = keys.length; i < length; i++) { 201 iteratee(obj[keys[i]], keys[i], obj); 202 } 203 } 204 return obj; 205 }; 206 207 // Return the results of applying the iteratee to each element. 208 _.map = _.collect = function(obj, iteratee, context) { 209 iteratee = cb(iteratee, context); 210 var keys = !isArrayLike(obj) && _.keys(obj), 211 length = (keys || obj).length, 212 results = Array(length); 213 for (var index = 0; index < length; index++) { 214 var currentKey = keys ? keys[index] : index; 215 results[index] = iteratee(obj[currentKey], currentKey, obj); 216 } 217 return results; 218 }; 219 220 // Create a reducing function iterating left or right. 221 var createReduce = function(dir) { 222 // Wrap code that reassigns argument variables in a separate function than 223 // the one that accesses `arguments.length` to avoid a perf hit. (#1991) 224 var reducer = function(obj, iteratee, memo, initial) { 225 var keys = !isArrayLike(obj) && _.keys(obj), 226 length = (keys || obj).length, 227 index = dir > 0 ? 0 : length - 1; 228 if (!initial) { 229 memo = obj[keys ? keys[index] : index]; 230 index += dir; 231 } 232 for (; index >= 0 && index < length; index += dir) { 233 var currentKey = keys ? keys[index] : index; 234 memo = iteratee(memo, obj[currentKey], currentKey, obj); 235 } 236 return memo; 237 }; 238 239 return function(obj, iteratee, memo, context) { 240 var initial = arguments.length >= 3; 241 return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial); 242 }; 243 }; 244 245 // **Reduce** builds up a single result from a list of values, aka `inject`, 246 // or `foldl`. 247 _.reduce = _.foldl = _.inject = createReduce(1); 248 249 // The right-associative version of reduce, also known as `foldr`. 250 _.reduceRight = _.foldr = createReduce(-1); 251 252 // Return the first value which passes a truth test. Aliased as `detect`. 253 _.find = _.detect = function(obj, predicate, context) { 254 var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey; 255 var key = keyFinder(obj, predicate, context); 256 if (key !== void 0 && key !== -1) return obj[key]; 257 }; 258 259 // Return all the elements that pass a truth test. 260 // Aliased as `select`. 261 _.filter = _.select = function(obj, predicate, context) { 262 var results = []; 263 predicate = cb(predicate, context); 264 _.each(obj, function(value, index, list) { 265 if (predicate(value, index, list)) results.push(value); 266 }); 267 return results; 268 }; 269 270 // Return all the elements for which a truth test fails. 271 _.reject = function(obj, predicate, context) { 272 return _.filter(obj, _.negate(cb(predicate)), context); 273 }; 274 275 // Determine whether all of the elements match a truth test. 276 // Aliased as `all`. 277 _.every = _.all = function(obj, predicate, context) { 278 predicate = cb(predicate, context); 279 var keys = !isArrayLike(obj) && _.keys(obj), 280 length = (keys || obj).length; 281 for (var index = 0; index < length; index++) { 282 var currentKey = keys ? keys[index] : index; 283 if (!predicate(obj[currentKey], currentKey, obj)) return false;
284 } 285 return true; 286 }; 287 288 // Determine if at least one element in the object matches a truth test. 289 // Aliased as `any`. 290 _.some = _.any = function(obj, predicate, context) { 291 predicate = cb(predicate, context); 292 var keys = !isArrayLike(obj) && _.keys(obj), 293 length = (keys || obj).length; 294 for (var index = 0; index < length; index++) { 295 var currentKey = keys ? keys[index] : index; 296 if (predicate(obj[currentKey], currentKey, obj)) return true; 297 } 298 return false; 299 }; 300 301 // Determine if the array or object contains a given item (using `===`). 302 // Aliased as `includes` and `include`. 303 _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { 304 if (!isArrayLike(obj)) obj = _.values(obj); 305 if (typeof fromIndex != 'number' || guard) fromIndex = 0; 306 return _.indexOf(obj, item, fromIndex) >= 0; 307 }; 308 309 // Invoke a method (with arguments) on every item in a collection. 310 _.invoke = restArguments(function(obj, path, args) { 311 var contextPath, func; 312 if (_.isFunction(path)) { 313 func = path; 314 } else if (_.isArray(path)) { 315 contextPath = path.slice(0, -1); 316 path = path[path.length - 1]; 317 } 318 return _.map(obj, function(context) { 319 var method = func; 320 if (!method) { 321 if (contextPath && contextPath.length) { 322 context = deepGet(context, contextPath); 323 } 324 if (context == null) return void 0; 325 method = context[path]; 326 } 327 return method == null ? method : method.apply(context, args); 328 }); 329 }); 330 331 // Convenience version of a common use case of `map`: fetching a property. 332 _.pluck = function(obj, key) { 333 return _.map(obj, _.property(key)); 334 }; 335 336 // Convenience version of a common use case of `filter`: selecting only objects 337 // containing specific `key:value` pairs. 338 _.where = function(obj, attrs) { 339 return _.filter(obj, _.matcher(attrs)); 340 }; 341 342 // Convenience version of a common use case of `find`: getting the first object 343 // containing specific `key:value` pairs. 344 _.findWhere = function(obj, attrs) { 345 return _.find(obj, _.matcher(attrs)); 346 }; 347 348 // Return the maximum element (or element-based computation). 349 _.max = function(obj, iteratee, context) { 350 var result = -Infinity, lastComputed = -Infinity, 351 value, computed; 352 if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) { 353 obj = isArrayLike(obj) ? obj : _.values(obj); 354 for (var i = 0, length = obj.length; i < length; i++) { 355 value = obj[i]; 356 if (value != null && value > result) { 357 result = value; 358 } 359 } 360 } else { 361 iteratee = cb(iteratee, context); 362 _.each(obj, function(v, index, list) { 363 computed = iteratee(v, index, list); 364 if (computed > lastComputed || computed === -Infinity && result === -Infinity) { 365 result = v; 366 lastComputed = computed; 367 } 368 }); 369 } 370 return result; 371 }; 372 373 // Return the minimum element (or element-based computation). 374 _.min = function(obj, iteratee, context) { 375 var result = Infinity, lastComputed = Infinity, 376 value, computed; 377 if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) { 378 obj = isArrayLike(obj) ? obj : _.values(obj); 379 for (var i = 0, length = obj.length; i < length; i++) { 380 value = obj[i]; 381 if (value != null && value < result) { 382 result = value; 383 } 384 } 385 } else { 386 iteratee = cb(iteratee, context); 387 _.each(obj, function(v, index, list) { 388 computed = iteratee(v, index, list); 389 if (computed < lastComputed || computed === Infinity && result === Infinity) { 390 result = v; 391 lastComputed = computed; 392 } 393 }); 394 } 395 return result; 396 }; 397 398 // Shuffle a collection. 399 _.shuffle = function(obj) { 400 return _.sample(obj, Infinity); 401 }; 402 403 // Sample **n** random values from a collection using the modern version of the 404 // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherâYates_shuffle). 405 // If **n** is not specified, returns a single random element. 406 // The internal `guard` argument allows it to work with `map`. 407 _.sample = function(obj, n, guard) { 408 if (n == null || guard) { 409 if (!isArrayLike(obj)) obj = _.values(obj); 410 return obj[_.random(obj.length - 1)]; 411 } 412 var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj); 413 var length = getLength(sample); 414 n = Math.max(Math.min(n, length), 0); 415 var last = length - 1; 416 for (var index = 0; index < n; index++) { 417 var rand = _.random(index, last); 418 var temp = sample[index]; 419 sample[index] = sample[rand]; 420 sample[rand] = temp; 421 } 422 return sample.slice(0, n); 423 }; 424 425 // Sort the object's values by a criterion produced by an iteratee. 426 _.sortBy = function(obj, iteratee, context) { 427 var index = 0; 428 iteratee = cb(iteratee, context); 429 return _.pluck(_.map(obj, function(value, key, list) { 430 return { 431 value: value, 432 index: index++, 433 criteria: iteratee(value, key, list) 434 }; 435 }).sort(function(left, right) { 436 var a = left.criteria; 437 var b = right.criteria; 438 if (a !== b) { 439 if (a > b || a === void 0) return 1; 440 if (a < b || b === void 0) return -1; 441 } 442 return left.index - right.index; 443 }), 'value'); 444 }; 445 446 // An internal function used for aggregate "group by" operations. 447 var group = function(behavior, partition) { 448 return function(obj, iteratee, context) { 449 var result = partition ? [[], []] : {}; 450 iteratee = cb(iteratee, context); 451 _.each(obj, function(value, index) { 452 var key = iteratee(value, index, obj); 453 behavior(result, value, key); 454 }); 455 return result; 456 }; 457 }; 458 459 // Groups the object's values by a criterion. Pass either a string attribute 460 // to group by, or a function that returns the criterion. 461 _.groupBy = group(function(result, value, key) { 462 if (has(result, key)) result[key].push(value); else result[key] = [value]; 463 }); 464 465 // Indexes the object's values by a criterion, similar to `groupBy`, but for 466 // when you know that your index values will be unique. 467 _.indexBy = group(function(result, value, key) { 468 result[key] = value; 469 }); 470 471 // Counts instances of an object that group by a certain criterion. Pass 472 // either a string attribute to count by, or a function that returns the 473 // criterion. 474 _.countBy = group(function(result, value, key) { 475 if (has(result, key)) result[key]++; else result[key] = 1; 476 }); 477 478 var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g; 479 // Safely create a real, live array from anything iterable. 480 _.toArray = function(obj) { 481 if (!obj) return []; 482 if (_.isArray(obj)) return slice.call(obj); 483 if (_.isString(obj)) { 484 // Keep surrogate pair characters together 485 return obj.match(reStrSymbol); 486 } 487 if (isArrayLike(obj)) return _.map(obj, _.identity); 488 return _.values(obj); 489 }; 490 491 // Return the number of elements in an object. 492 _.size = function(obj) { 493 if (obj == null) return 0; 494 return isArrayLike(obj) ? obj.length : _.keys(obj).length; 495 }; 496 497 // Split a collection into two arrays: one whose elements all satisfy the given 498 // predicate, and one whose elements all do not satisfy the predicate. 499 _.partition = group(function(result, value, pass) { 500 result[pass ? 0 : 1].push(value); 501 }, true); 502 503 // Array Functions 504 // --------------- 505 506 // Get the first element of an array. Passing **n** will return the first N 507 // values in the array. Aliased as `head` and `take`. The **guard** check 508 // allows it to work with `_.map`. 509 _.first = _.head = _.take = function(array, n, guard) { 510 if (array == null || array.length < 1) return n == null ? void 0 : []; 511 if (n == null || guard) return array[0]; 512 return _.initial(array, array.length - n); 513 }; 514 515 // Returns everything but the last entry of the array. Especially useful on 516 // the arguments object. Passing **n** will return all the values in 517 // the array, excluding the last N. 518 _.initial = function(array, n, guard) { 519 return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); 520 }; 521 522 // Get the last element of an array. Passing **n** will return the last N 523 // values in the array. 524 _.last = function(array, n, guard) { 525 if (array == null || array.length < 1) return n == null ? void 0 : []; 526 if (n == null || guard) return array[array.length - 1]; 527 return _.rest(array, Math.max(0, array.length - n)); 528 }; 529 530 // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 531 // Especially useful on the arguments object. Passing an **n** will return 532 // the rest N values in the array. 533 _.rest = _.tail = _.drop = function(array, n, guard) { 534 return slice.call(array, n == null || guard ? 1 : n); 535 }; 536 537 // Trim out all falsy values from an array.
538 _.compact = function(array) { 539 return _.filter(array, Boolean); 540 }; 541 542 // Internal implementation of a recursive `flatten` function. 543 var flatten = function(input, shallow, strict, output) { 544 output = output || []; 545 var idx = output.length; 546 for (var i = 0, length = getLength(input); i < length; i++) { 547 var value = input[i]; 548 if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { 549 // Flatten current level of array or arguments object. 550 if (shallow) { 551 var j = 0, len = value.length; 552 while (j < len) output[idx++] = value[j++]; 553 } else { 554 flatten(value, shallow, strict, output); 555 idx = output.length; 556 } 557 } else if (!strict) { 558 output[idx++] = value; 559 } 560 } 561 return output; 562 }; 563 564 // Flatten out an array, either recursively (by default), or just one level. 565 _.flatten = function(array, shallow) { 566 return flatten(array, shallow, false); 567 }; 568 569 // Return a version of the array that does not contain the specified value(s). 570 _.without = restArguments(function(array, otherArrays) { 571 return _.difference(array, otherArrays); 572 }); 573 574 // Produce a duplicate-free version of the array. If the array has already 575 // been sorted, you have the option of using a faster algorithm. 576 // The faster algorithm will not work with an iteratee if the iteratee 577 // is not a one-to-one function, so providing an iteratee will disable 578 // the faster algorithm. 579 // Aliased as `unique`. 580 _.uniq = _.unique = function(array, isSorted, iteratee, context) { 581 if (!_.isBoolean(isSorted)) { 582 context = iteratee; 583 iteratee = isSorted; 584 isSorted = false; 585 } 586 if (iteratee != null) iteratee = cb(iteratee, context); 587 var result = []; 588 var seen = []; 589 for (var i = 0, length = getLength(array); i < length; i++) { 590 var value = array[i], 591 computed = iteratee ? iteratee(value, i, array) : value; 592 if (isSorted && !iteratee) { 593 if (!i || seen !== computed) result.push(value); 594 seen = computed; 595 } else if (iteratee) { 596 if (!_.contains(seen, computed)) { 597 seen.push(computed); 598 result.push(value); 599 } 600 } else if (!_.contains(result, value)) { 601 result.push(value); 602 } 603 } 604 return result; 605 }; 606 607 // Produce an array that contains the union: each distinct element from all of 608 // the passed-in arrays. 609 _.union = restArguments(function(arrays) { 610 return _.uniq(flatten(arrays, true, true)); 611 }); 612 613 // Produce an array that contains every item shared between all the 614 // passed-in arrays. 615 _.intersection = function(array) { 616 var result = []; 617 var argsLength = arguments.length; 618 for (var i = 0, length = getLength(array); i < length; i++) { 619 var item = array[i]; 620 if (_.contains(result, item)) continue; 621 var j; 622 for (j = 1; j < argsLength; j++) { 623 if (!_.contains(arguments[j], item)) break; 624 } 625 if (j === argsLength) result.push(item); 626 } 627 return result; 628 }; 629 630 // Take the difference between one array and a number of other arrays. 631 // Only the elements present in just the first array will remain. 632 _.difference = restArguments(function(array, rest) { 633 rest = flatten(rest, true, true); 634 return _.filter(array, function(value){ 635 return !_.contains(rest, value); 636 }); 637 }); 638 639 // Complement of _.zip. Unzip accepts an array of arrays and groups 640 // each array's elements on shared indices. 641 _.unzip = function(array) { 642 var length = array && _.max(array, getLength).length || 0; 643 var result = Array(length); 644 645 for (var index = 0; index < length; index++) { 646 result[index] = _.pluck(array, index); 647 } 648 return result; 649 }; 650 651 // Zip together multiple lists into a single array -- elements that share 652 // an index go together. 653 _.zip = restArguments(_.unzip); 654 655 // Converts lists into objects. Pass either a single array of `[key, value]` 656 // pairs, or two parallel arrays of the same length -- one of keys, and one of 657 // the corresponding values. Passing by pairs is the reverse of _.pairs. 658 _.object = function(list, values) { 659 var result = {}; 660 for (var i = 0, length = getLength(list); i < length; i++) { 661 if (values) { 662 result[list[i]] = values[i]; 663 } else { 664 result[list[i][0]] = list[i][1]; 665 } 666 } 667 return result; 668 }; 669 670 // Generator function to create the findIndex and findLastIndex functions. 671 var createPredicateIndexFinder = function(dir) { 672 return function(array, predicate, context) { 673 predicate = cb(predicate, context); 674 var length = getLength(array); 675 var index = dir > 0 ? 0 : length - 1; 676 for (; index >= 0 && index < length; index += dir) { 677 if (predicate(array[index], index, array)) return index; 678 } 679 return -1; 680 }; 681 }; 682 683 // Returns the first index on an array-like that passes a predicate test. 684 _.findIndex = createPredicateIndexFinder(1); 685 _.findLastIndex = createPredicateIndexFinder(-1); 686 687 // Use a comparator function to figure out the smallest index at which 688 // an object should be inserted so as to maintain order. Uses binary search. 689 _.sortedIndex = function(array, obj, iteratee, context) { 690 iteratee = cb(iteratee, context, 1); 691 var value = iteratee(obj); 692 var low = 0, high = getLength(array); 693 while (low < high) { 694 var mid = Math.floor((low + high) / 2); 695 if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; 696 } 697 return low; 698 }; 699 700 // Generator function to create the indexOf and lastIndexOf functions. 701 var createIndexFinder = function(dir, predicateFind, sortedIndex) { 702 return function(array, item, idx) { 703 var i = 0, length = getLength(array); 704 if (typeof idx == 'number') { 705 if (dir > 0) { 706 i = idx >= 0 ? idx : Math.max(idx + length, i); 707 } else { 708 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; 709 } 710 } else if (sortedIndex && idx && length) { 711 idx = sortedIndex(array, item); 712 return array[idx] === item ? idx : -1; 713 } 714 if (item !== item) { 715 idx = predicateFind(slice.call(array, i, length), _.isNaN); 716 return idx >= 0 ? idx + i : -1; 717 } 718 for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { 719 if (array[idx] === item) return idx; 720 } 721 return -1; 722 }; 723 }; 724 725 // Return the position of the first occurrence of an item in an array, 726 // or -1 if the item is not included in the array. 727 // If the array is large and already in sort order, pass `true` 728 // for **isSorted** to use binary search. 729 _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); 730 _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); 731 732 // Generate an integer Array containing an arithmetic progression. A port of 733 // the native Python `range()` function. See 734 // [the Python documentation](http://docs.python.org/library/functions.html#range). 735 _.range = function(start, stop, step) { 736 if (stop == null) { 737 stop = start || 0; 738 start = 0; 739 } 740 if (!step) { 741 step = stop < start ? -1 : 1; 742 } 743 744 var length = Math.max(Math.ceil((stop - start) / step), 0); 745 var range = Array(length); 746 747 for (var idx = 0; idx < length; idx++, start += step) { 748 range[idx] = start; 749 } 750 751 return range; 752 }; 753 754 // Chunk a single array into multiple arrays, each containing `count` or fewer 755 // items. 756 _.chunk = function(array, count) { 757 if (count == null || count < 1) return []; 758 var result = []; 759 var i = 0, length = array.length; 760 while (i < length) { 761 result.push(slice.call(array, i, i += count)); 762 } 763 return result; 764 }; 765 766 // Function (ahem) Functions 767 // ------------------ 768 769 // Determines whether to execute a function as a constructor 770 // or a normal function with the provided arguments. 771 var executeBound = function(sourceFunc, boundFunc, context, callingContext, args
771) { 772 if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); 773 var self = baseCreate(sourceFunc.prototype); 774 var result = sourceFunc.apply(self, args); 775 if (_.isObject(result)) return result; 776 return self; 777 }; 778 779 // Create a function bound to a given object (assigning `this`, and arguments, 780 // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 781 // available. 782 _.bind = restArguments(function(func, context, args) { 783 if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); 784 var bound = restArguments(function(callArgs) { 785 return executeBound(func, bound, context, this, args.concat(callArgs)); 786 }); 787 return bound; 788 }); 789 790 // Partially apply a function by creating a version that has had some of its 791 // arguments pre-filled, without changing its dynamic `this` context. _ acts 792 // as a placeholder by default, allowing any combination of arguments to be 793 // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument. 794 _.partial = restArguments(function(func, boundArgs) { 795 var placeholder = _.partial.placeholder; 796 var bound = function() { 797 var position = 0, length = boundArgs.length; 798 var args = Array(length); 799 for (var i = 0; i < length; i++) { 800 args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i]; 801 } 802 while (position < arguments.length) args.push(arguments[position++]); 803 return executeBound(func, bound, this, this, args); 804 }; 805 return bound; 806 }); 807 808 _.partial.placeholder = _; 809 810 // Bind a number of an object's methods to that object. Remaining arguments 811 // are the method names to be bound. Useful for ensuring that all callbacks 812 // defined on an object belong to it. 813 _.bindAll = restArguments(function(obj, keys) { 814 keys = flatten(keys, false, false); 815 var index = keys.length; 816 if (index < 1) throw new Error('bindAll must be passed function names'); 817 while (index--) { 818 var key = keys[index]; 819 obj[key] = _.bind(obj[key], obj); 820 } 821 }); 822 823 // Memoize an expensive function by storing its results. 824 _.memoize = function(func, hasher) { 825 var memoize = function(key) { 826 var cache = memoize.cache; 827 var address = '' + (hasher ? hasher.apply(this, arguments) : key); 828 if (!has(cache, address)) cache[address] = func.apply(this, arguments); 829 return cache[address]; 830 }; 831 memoize.cache = {}; 832 return memoize; 833 }; 834 835 // Delays a function for the given number of milliseconds, and then calls 836 // it with the arguments supplied.
837 _.delay = restArguments(function(func, wait, args) { 838 return setTimeout(function() { 839 return func.apply(null, args); 840 }, wait); 841 }); 842 843 // Defers a function, scheduling it to run after the current call stack has 844 // cleared. 845 _.defer = _.partial(_.delay, _, 1); 846 847 // Returns a function, that, when invoked, will only be triggered at most once 848 // during a given window of time. Normally, the throttled function will run 849 // as much as it can, without ever going more than once per `wait` duration; 850 // but if you'd like to disable the execution on the leading edge, pass 851 // `{leading: false}`. To disable execution on the trailing edge, ditto. 852 _.throttle = function(func, wait, options) { 853 var timeout, context, args, result; 854 var previous = 0; 855 if (!options) options = {}; 856 857 var later = function() { 858 previous = options.leading === false ? 0 : _.now(); 859 timeout = null; 860 result = func.apply(context, args); 861 if (!timeout) context = args = null; 862 }; 863 864 var throttled = function() { 865 var now = _.now(); 866 if (!previous && options.leading === false) previous = now; 867 var remaining = wait - (now - previous); 868 context = this; 869 args = arguments; 870 if (remaining <= 0 || remaining > wait) { 871 if (timeout) { 872 clearTimeout(timeout); 873 timeout = null; 874 } 875 previous = now; 876 result = func.apply(context, args); 877 if (!timeout) context = args = null; 878 } else if (!timeout && options.trailing !== false) { 879 timeout = setTimeout(later, remaining); 880 } 881 return result; 882 }; 883 884 throttled.cancel = function() { 885 clearTimeout(timeout); 886 previous = 0; 887 timeout = context = args = null; 888 }; 889 890 return throttled; 891 }; 892 893 // Returns a function, that, as long as it continues to be invoked, will not 894 // be triggered. The function will be called after it stops being called for 895 // N milliseconds. If `immediate` is passed, trigger the function on the 896 // leading edge, instead of the trailing. 897 _.debounce = function(func, wait, immediate) { 898 var timeout, result; 899 900 var later = function(context, args) { 901 timeout = null; 902 if (args) result = func.apply(context, args); 903 }; 904 905 var debounced = restArguments(function(args) { 906 if (timeout) clearTimeout(timeout); 907 if (immediate) { 908 var callNow = !timeout; 909 timeout = setTimeout(later, wait); 910 if (callNow) result = func.apply(this, args); 911 } else { 912 timeout = _.delay(later, wait, this, args); 913 } 914 915 return result; 916 }); 917 918 debounced.cancel = function() { 919 clearTimeout(timeout); 920 timeout = null; 921 }; 922 923 return debounced; 924 }; 925 926 // Returns the first function passed as an argument to the second, 927 // allowing you to adjust arguments, run code before and after, and 928 // conditionally execute the original function. 929 _.wrap = function(func, wrapper) { 930 return _.partial(wrapper, func); 931 }; 932
933 // Returns a negated version of the passed-in predicate. 934 _.negate = function(predicate) { 935 return function() { 936 return !predicate.apply(this, arguments); 937 }; 938 }; 939 940 // Returns a function that is the composition of a list of functions, each 941 // consuming the return value of the function that follows. 942 _.compose = function() { 943 var args = arguments; 944 var start = args.length - 1; 945 return function() { 946 var i = start; 947 var result = args[start].apply(this, arguments); 948 while (i--) result = args[i].call(this, result); 949 return result; 950 }; 951 }; 952 953 // Returns a function that will only be executed on and after the Nth call. 954 _.after = function(times, func) { 955 return function() { 956 if (--times < 1) { 957 return func.apply(this, arguments); 958 } 959 }; 960 }; 961 962 // Returns a function that will only be executed up to (but not including) the Nth call. 963 _.before = function(times, func) { 964 var memo; 965 return function() { 966 if (--times > 0) { 967 memo = func.apply(this, arguments); 968 } 969 if (times <= 1) func = null; 970 return memo; 971 }; 972 }; 973 974 // Returns a function that will be executed at most one time, no matter how 975 // often you call it. Useful for lazy initialization. 976 _.once = _.partial(_.before, 2); 977 978 _.restArguments = restArguments; 979 980 // Object Functions 981 // ---------------- 982 983 // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. 984 var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); 985 var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 986 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; 987 988 var collectNonEnumProps = function(obj, keys) { 989 var nonEnumIdx = nonEnumerableProps.length; 990 var constructor = obj.constructor; 991 var proto = _.isFunction(constructor) && constructor.prototype || ObjProto; 992 993 // Constructor is a special case. 994 var prop = 'constructor';
995 if (has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); 996 997 while (nonEnumIdx--) { 998 prop = nonEnumerableProps[nonEnumIdx]; 999 if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { 1000 keys.push(prop); 1001 } 1002 } 1003 }; 1004 1005 // Retrieve the names of an object's own properties. 1006 // Delegates to **ECMAScript 5**'s native `Object.keys`. 1007 _.keys = function(obj) { 1008 if (!_.isObject(obj)) return []; 1009 if (nativeKeys) return nativeKeys(obj); 1010 var keys = []; 1011 for (var key in obj) if (has(obj, key)) keys.push(key); 1012 // Ahem, IE < 9. 1013 if (hasEnumBug) collectNonEnumProps(obj, keys); 1014 return keys; 1015 }; 1016 1017 // Retrieve all the property names of an object. 1018 _.allKeys = function(obj) { 1019 if (!_.isObject(obj)) return []; 1020 var keys = []; 1021 for (var key in obj) keys.push(key); 1022 // Ahem, IE < 9. 1023 if (hasEnumBug) collectNonEnumProps(obj, keys); 1024 return keys; 1025 }; 1026 1027 // Retrieve the values of an object's properties. 1028 _.values = function(obj) { 1029 var keys = _.keys(obj); 1030 var length = keys.length; 1031 var values = Array(length); 1032 for (var i = 0; i < length; i++) { 1033 values[i] = obj[keys[i]]; 1034 } 1035 return values; 1036 }; 1037 1038 // Returns the results of applying the iteratee to each element of the object. 1039 // In contrast to _.map it returns an object. 1040 _.mapObject = function(obj, iteratee, context) { 1041 iteratee = cb(iteratee, context); 1042 var keys = _.keys(obj), 1043 length = keys.length, 1044 results = {}; 1045 for (var index = 0; index < length; index++) { 1046 var currentKey = keys[index]; 1047 results[currentKey] = iteratee(obj[currentKey], currentKey, obj); 1048 } 1049 return results; 1050 }; 1051 1052 // Convert an object into a list of `[key, value]` pairs. 1053 // The opposite of _.object. 1054 _.pairs = function(obj) { 1055 var keys = _.keys(obj); 1056 var length = keys.length; 1057 var pairs = Array(length); 1058 for (var i = 0; i < length; i++) { 1059 pairs[i] = [keys[i], obj[keys[i]]]; 1060 } 1061 return pairs; 1062 }; 1063 1064 // Invert the keys and values of an object. The values must be serializable. 1065 _.invert = function(obj) { 1066 var result = {}; 1067 var keys = _.keys(obj); 1068 for (var i = 0, length = keys.length; i < length; i++) { 1069 result[obj[keys[i]]] = keys[i]; 1070 } 1071 return result; 1072 }; 1073 1074 // Return a sorted list of the function names available on the object. 1075 // Aliased as `methods`. 1076 _.functions = _.methods = function(obj) { 1077 var names = []; 1078 for (var key in obj) { 1079 if (_.isFunction(obj[key])) names.push(key); 1080 } 1081 return names.sort(); 1082 }; 1083 1084 // An internal function for creating assigner functions. 1085 var createAssigner = function(keysFunc, defaults) { 1086 return function(obj) { 1087 var length = arguments.length; 1088 if (defaults) obj = Object(obj); 1089 if (length < 2 || obj == null) return obj; 1090 for (var index = 1; index < length; index++) { 1091 var source = arguments[index], 1092 keys = keysFunc(source), 1093 l = keys.length; 1094 for (var i = 0; i < l; i++) { 1095 var key = keys[i]; 1096 if (!defaults || obj[key] === void 0) obj[key] = source[key]; 1097 } 1098 } 1099 return obj; 1100 }; 1101 }; 1102 1103 // Extend a given object with all the properties in passed-in object(s). 1104 _.extend = createAssigner(_.allKeys); 1105 1106 // Assigns a given object with all the own properties in the passed-in object(s). 1107 // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 1108 _.extendOwn = _.assign = createAssigner(_.keys); 1109 1110 // Returns the first key on an object that passes a predicate test. 1111 _.findKey = function(obj, predicate, context) { 1112 predicate = cb(predicate, context); 1113 var keys = _.keys(obj), key; 1114 for (var i = 0, length = keys.length; i < length; i++) { 1115 key = keys[i]; 1116 if (predicate(obj[key], key, obj)) return key; 1117 } 1118 }; 1119 1120 // Internal pick helper function to determine if `obj` has key `key`. 1121 var keyInObj = function(value, key, obj) { 1122 return key in obj; 1123 }; 1124 1125 // Return a copy of the object only containing the whitelisted properties. 1126 _.pick = restArguments(function(obj, keys) { 1127 var result = {}
1127, iteratee = keys[0]; 1128 if (obj == null) return result; 1129 if (_.isFunction(iteratee)) { 1130 if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]); 1131 keys = _.allKeys(obj); 1132 } else { 1133 iteratee = keyInObj; 1134 keys = flatten(keys, false, false); 1135 obj = Object(obj); 1136 } 1137 for (var i = 0, length = keys.length; i < length; i++) { 1138 var key = keys[i]; 1139 var value = obj[key]; 1140 if (iteratee(value, key, obj)) result[key] = value; 1141 } 1142 return result; 1143 }); 1144 1145 // Return a copy of the object without the blacklisted properties. 1146 _.omit = restArguments(function(obj, keys) { 1147 var iteratee = keys[0], context; 1148 if (_.isFunction(iteratee)) { 1149 iteratee = _.negate(iteratee); 1150 if (keys.length > 1) context = keys[1]; 1151 } else { 1152 keys = _.map(flatten(keys, false, false), String); 1153 iteratee = function(value, key) { 1154 return !_.contains(keys, key); 1155 }; 1156 } 1157 return _.pick(obj, iteratee, context); 1158 }); 1159 1160 // Fill in a given object with default properties. 1161 _.defaults = createAssigner(_.allKeys, true); 1162 1163 // Creates an object that inherits from the given prototype object. 1164 // If additional properties are provided then they will be added to the 1165 // created object. 1166 _.create = function(prototype, props) { 1167 var result = baseCreate(prototype); 1168 if (props) _.extendOwn(result, props); 1169 return result; 1170 }; 1171 1172 // Create a (shallow-cloned) duplicate of an object. 1173 _.clone = function(obj) { 1174 if (!_.isObject(obj)) return obj; 1175 return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 1176 }; 1177 1178 // Invokes interceptor with the obj, and then returns obj. 1179 // The primary purpose of this method is to "tap into" a method chain, in 1180 // order to perform operations on intermediate results within the chain. 1181 _.tap = function(obj, interceptor) { 1182 interceptor(obj); 1183 return obj; 1184 }; 1185 1186 // Returns whether an object has a given set of `key:value` pairs. 1187 _.isMatch = function(object, attrs) { 1188 var keys = _.keys(attrs), length = keys.length; 1189 if (object == null) return !length; 1190 var obj = Object(object); 1191 for (var i = 0; i < length; i++) { 1192 var key = keys[i]; 1193 if (attrs[key] !== obj[key] || !(key in obj)) return false; 1194 } 1195 return true; 1196 }; 1197 1198 1199 // Internal recursive comparison function for `isEqual`. 1200 var eq, deepEq; 1201 eq = function(a, b, aStack, bStack) { 1202 // Identical objects are equal. `0 === -0`, but they aren't identical. 1203 // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 1204 if (a === b) return a !== 0 || 1 / a === 1 / b; 1205 // `null` or `undefined` only equal to itself (strict comparison). 1206 if (a == null || b == null) return false; 1207 // `NaN`s are equivalent, but non-reflexive. 1208 if (a !== a) return b !== b; 1209 // Exhaust primitive checks 1210 var type = typeof a; 1211 if (type !== 'function' && type !== 'object' && typeof b != 'object') return false; 1212 return deepEq(a, b, aStack, bStack); 1213 }; 1214 1215 // Internal recursive comparison function for `isEqual`. 1216 deepEq = function(a, b, aStack, bStack) { 1217 // Unwrap any wrapped objects. 1218 if (a instanceof _) a = a._wrapped; 1219 if (b instanceof _) b = b._wrapped; 1220 // Compare `[[Class]]` names. 1221 var className = toString.call(a); 1222 if (className !== toString.call(b)) return false; 1223 switch (className) { 1224 // Strings, numbers, regular expressions, dates, and booleans are compared by value. 1225 case '[object RegExp]': 1226 // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') 1227 case '[object String]': 1228 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 1229 // equivalent to `new String("5")`. 1230 return '' + a === '' + b; 1231 case '[object Number]': 1232 // `NaN`s are equivalent, but non-reflexive. 1233 // Object(NaN) is equivalent to NaN. 1234 if (+a !== +a) return +b !== +b; 1235 // An `egal` comparison is performed for other numeric values. 1236 return +a === 0 ? 1 / +a === 1 / b : +a === +b; 1237 case '[object Date]': 1238 case '[object Boolean]': 1239 // Coerce dates and booleans to numeric primitive values. Dates are compared by their 1240 // millisecond representations. Note that invalid dates with millisecond representations 1241 // of `NaN` are not equivalent. 1242 return +a === +b; 1243 case '[object Symbol]': 1244 return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b); 1245 } 1246 1247 var areArrays = className === '[object Array]'; 1248 if (!areArrays) { 1249 if (typeof a != 'object' || typeof b != 'object') return false;
vendor: 5,169 bytes, lines 1250-1394
1250 1251 // Objects with different constructors are not equivalent, but `Object`s or `Array`s 1252 // from different frames are. 1253 var aCtor = a.constructor, bCtor = b.constructor; 1254 if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && 1255 _.isFunction(bCtor) && bCtor instanceof bCtor) 1256 && ('constructor' in a && 'constructor' in b)) { 1257 return false; 1258 } 1259 } 1260 // Assume equality for cyclic structures. The algorithm for detecting cyclic 1261 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 1262 1263 // Initializing stack of traversed objects. 1264 // It's done here since we only need them for objects and arrays comparison. 1265 aStack = aStack || []; 1266 bStack = bStack || []; 1267 var length = aStack.length; 1268 while (length--) { 1269 // Linear search. Performance is inversely proportional to the number of 1270 // unique nested structures. 1271 if (aStack[length] === a) return bStack[length] === b; 1272 } 1273 1274 // Add the first object to the stack of traversed objects. 1275 aStack.push(a); 1276 bStack.push(b); 1277 1278 // Recursively compare objects and arrays. 1279 if (areArrays) { 1280 // Compare array lengths to determine if a deep comparison is necessary. 1281 length = a.length; 1282 if (length !== b.length) return false; 1283 // Deep compare the contents, ignoring non-numeric properties. 1284 while (length--) { 1285 if (!eq(a[length], b[length], aStack, bStack)) return false; 1286 } 1287 } else { 1288 // Deep compare objects. 1289 var keys = _.keys(a), key; 1290 length = keys.length; 1291 // Ensure that both objects contain the same number of properties before comparing deep equality. 1292 if (_.keys(b).length !== length) return false; 1293 while (length--) { 1294 // Deep compare each member 1295 key = keys[length]; 1296 if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; 1297 } 1298 } 1299 // Remove the first object from the stack of traversed objects. 1300 aStack.pop(); 1301 bStack.pop(); 1302 return true; 1303 }; 1304 1305 // Perform a deep comparison to check if two objects are equal. 1306 _.isEqual = function(a, b) { 1307 return eq(a, b); 1308 }; 1309 1310 // Is a given array, string, or object empty? 1311 // An "empty" object has no enumerable own-properties. 1312 _.isEmpty = function(obj) { 1313 if (obj == null) return true; 1314 if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; 1315 return _.keys(obj).length === 0; 1316 }; 1317 1318 // Is a given value a DOM element? 1319 _.isElement = function(obj) { 1320 return !!(obj && obj.nodeType === 1); 1321 }; 1322 1323 // Is a given value an array? 1324 // Delegates to ECMA5's native Array.isArray 1325 _.isArray = nativeIsArray || function(obj) { 1326 return toString.call(obj) === '[object Array]'; 1327 }; 1328 1329 // Is a given variable an object? 1330 _.isObject = function(obj) { 1331 var type = typeof obj; 1332 return type === 'function' || type === 'object' && !!obj; 1333 }; 1334 1335 // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet. 1336 _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) { 1337 _['is' + name] = function(obj) { 1338 return toString.call(obj) === '[object ' + name + ']'; 1339 }; 1340 }); 1341 1342 // Define a fallback version of the method in browsers (ahem, IE < 9), where 1343 // there isn't any inspectable "Arguments" type. 1344 if (!_.isArguments(arguments)) { 1345 _.isArguments = function(obj) { 1346 return has(obj, 'callee'); 1347 }; 1348 } 1349 1350 // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, 1351 // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236). 1352 var nodelist = root.document && root.document.childNodes; 1353 if (typeof Int8Array != 'object' && typeof nodelist != 'function') { 1354 _.isFunction = function(obj) { 1355 return typeof obj == 'function' || false; 1356 }; 1357 } 1358 1359 // Is a given object a finite number? 1360 _.isFinite = function(obj) { 1361 return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj)); 1362 }; 1363 1364 // Is the given value `NaN`? 1365 _.isNaN = function(obj) { 1366 return _.isNumber(obj) && isNaN(obj); 1367 }; 1368 1369 // Is a given value a boolean? 1370 _.isBoolean = function(obj) { 1371 return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; 1372 }; 1373 1374 // Is a given value equal to null? 1375 _.isNull = function(obj) { 1376 return obj === null; 1377 }; 1378 1379 // Is a given variable undefined? 1380 _.isUndefined = function(obj) { 1381 return obj === void 0; 1382 }; 1383 1384 // Shortcut function for checking if an object has a given property directly 1385 // on itself (in other words, not on a prototype). 1386 _.has = function(obj, path) { 1387 if (!_.isArray(path)) { 1388 return has(obj, path); 1389 } 1390 var length = path.length; 1391 for (var i = 0; i < length; i++) { 1392 var key = path[i]; 1393 if (obj == null || !hasOwnProperty.call(obj, key)) { 1394 return false;
1395 } 1396 obj = obj[key]; 1397 } 1398 return !!length; 1399 }; 1400 1401 // Utility Functions 1402 // ----------------- 1403 1404 // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1405 // previous owner. Returns a reference to the Underscore object. 1406 _.noConflict = function() { 1407 root._ = previousUnderscore; 1408 return this; 1409 }; 1410 1411 // Keep the identity function around for default iteratees. 1412 _.identity = function(value) { 1413 return value; 1414 }; 1415 1416 // Predicate-generating functions. Often useful outside of Underscore. 1417 _.constant = function(value) { 1418 return function() { 1419 return value; 1420 }; 1421 }; 1422 1423 _.noop = function(){}; 1424 1425 // Creates a function that, when passed an object, will traverse that objectâs 1426 // properties down the given `path`, specified as an array of keys or indexes. 1427 _.property = function(path) { 1428 if (!_.isArray(path)) { 1429 return shallowProperty(path); 1430 } 1431 return function(obj) { 1432 return deepGet(obj, path); 1433 }; 1434 }; 1435 1436 // Generates a function for a given object that returns a given property. 1437 _.propertyOf = function(obj) { 1438 if (obj == null) { 1439 return function(){}; 1440 } 1441 return function(path) { 1442 return !_.isArray(path) ? obj[path] : deepGet(obj, path); 1443 }; 1444 }; 1445 1446 // Returns a predicate for checking whether an object has a given set of 1447 // `key:value` pairs. 1448 _.matcher = _.matches = function(attrs) { 1449 attrs = _.extendOwn({}, attrs); 1450 return function(obj) { 1451 return _.isMatch(obj, attrs); 1452 }; 1453 }; 1454 1455 // Run a function **n** times. 1456 _.times = function(n, iteratee, context) { 1457 var accum = Array(Math.max(0, n)); 1458 iteratee = optimizeCb(iteratee, context, 1); 1459 for (var i = 0; i < n; i++) accum[i] = iteratee(i); 1460 return accum; 1461 }; 1462 1463 // Return a random integer between min and max (inclusive). 1464 _.random = function(min, max) { 1465 if (max == null) { 1466 max = min; 1467 min = 0; 1468 } 1469 return min + Math.floor(Math.random() * (max - min + 1)); 1470 }; 1471 1472 // A (possibly faster) way to get the current timestamp as an integer. 1473 _.now = Date.now || function() { 1474 return new Date().getTime(); 1475 }; 1476 1477 // List of HTML entities for escaping. 1478 var escapeMap = { 1479 '&': '&', 1480 '<': '<', 1481 '>': '>', 1482 '"': '"', 1483 "'": ''', 1484 '`': '`' 1485 }; 1486 var unescapeMap = _.invert(escapeMap); 1487 1488 // Functions for escaping and unescaping strings to/from HTML interpolation. 1489 var createEscaper = function(map) { 1490 var escaper = function(match) { 1491 return map[match]; 1492 }; 1493 // Regexes for identifying a key that needs to be escaped. 1494 var source = '(?:' + _.keys(map).join('|') + ')'; 1495 var testRegexp = RegExp(source); 1496 var replaceRegexp = RegExp(source, 'g'); 1497 return function(string) { 1498 string = string == null ? '' : '' + string; 1499 return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; 1500 }; 1501 }; 1502 _.escape = createEscaper(escapeMap); 1503 _.unescape = createEscaper(unescapeMap); 1504 1505 // Traverses the children of `obj` along `path`. If a child is a function, it 1506 // is invoked with its parent as context. Returns the value of the final 1507 // child, or `fallback` if any child is undefined. 1508 _.result = function(obj, path, fallback) { 1509 if (!_.isArray(path)) path = [path]; 1510 var length = path.length; 1511 if (!length) { 1512 return _.isFunction(fallback) ? fallback.call(obj) : fallback; 1513 } 1514 for (var i = 0; i < length; i++) { 1515 var prop = obj == null ? void 0 : obj[path[i]]; 1516 if (prop === void 0) { 1517 prop = fallback; 1518 i = length; // Ensure we don't continue iterating. 1519 } 1520 obj = _.isFunction(prop) ? prop.call(obj) : prop; 1521 } 1522 return obj; 1523 }; 1524 1525 // Generate a unique integer id (unique within the entire client session). 1526 // Useful for temporary DOM ids. 1527 var idCounter = 0; 1528 _.uniqueId = function(prefix) { 1529 var id = ++idCounter + ''; 1530 return prefix ? prefix + id : id; 1531 }; 1532 1533 // By default, Underscore uses ERB-style template delimiters, change the 1534 // following template settings to use alternative delimiters. 1535 _.templateSettings = { 1536 evaluate: /<%([\s\S]+?)%>/g, 1537 interpolate: /<%=([\s\S]+?)%>/g, 1538 escape: /<%-([\s\S]+?)%>/g 1539 }; 1540 1541 // When customizing `templateSettings`, if you don't want to define an 1542 // interpolation, evaluation or escaping regex, we need one that is 1543 // guaranteed not to match. 1544 var noMatch = /(.)^/; 1545 1546 // Certain characters need to be escaped so that they can be put into a 1547 // string literal. 1548 var escapes = { 1549 "'": "'", 1550 '\\': '\\', 1551 '\r': 'r', 1552 '\n': 'n', 1553 '\u2028': 'u2028', 1554 '\u2029': 'u2029' 1555 }; 1556 1557 var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g; 1558 1559 var escapeChar = function(match) { 1560 return '\\' + escapes[match]; 1561 }; 1562 1563 // JavaScript micro-templating, similar to John Resig's implementation. 1564 // Underscore templating handles arbitrary delimiters, preserves whitespace, 1565 // and correctly escapes quotes within interpolated code. 1566 // NB: `oldSettings` only exists for backwards compatibility. 1567 _.template = function(text, settings, oldSettings) { 1568 if (!settings && oldSettings) settings = oldSettings; 1569 settings = _.defaults({}, settings, _.templateSettings); 1570 1571 // Combine delimiters into one regular expression via alternation. 1572 var matcher = RegExp([
1573 (settings.escape || noMatch).source, 1574 (settings.interpolate || noMatch).source, 1575 (settings.evaluate || noMatch).source 1576 ].join('|') + '|$', 'g'); 1577 1578 // Compile the template source, escaping string literals appropriately. 1579 var index = 0; 1580 var source = "__p+='"; 1581 text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1582 source += text.slice(index, offset).replace(escapeRegExp, escapeChar); 1583 index = offset + match.length; 1584 1585 if (escape) { 1586 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1587 } else if (interpolate) { 1588 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1589 } else if (evaluate) { 1590 source += "';\n" + evaluate + "\n__p+='"; 1591 } 1592 1593 // Adobe VMs need the match returned to produce the correct offset. 1594 return match; 1595 }); 1596 source += "';\n"; 1597 1598 // If a variable is not specified, place data values in local scope. 1599 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1600 1601 source = "var __t,__p='',__j=Array.prototype.join," + 1602 "print=function(){__p+=__j.call(arguments,'');};\n" + 1603 source + 'return __p;\n'; 1604 1605 var render; 1606 try { 1607 render = new Function(settings.variable || 'obj', '_', source); 1608 } catch (e) { 1609 e.source = source; 1610 throw e; 1611 } 1612 1613 var template = function(data) { 1614 return render.call(this, data, _); 1615 }; 1616 1617 // Provide the compiled source as a convenience for precompilation. 1618 var argument = settings.variable || 'obj'; 1619 template.source = 'function(' + argument + '){\n' + source + '}'; 1620 1621 return template; 1622 }; 1623 1624 // Add a "chain" function. Start chaining a wrapped Underscore object. 1625 _.chain = function(obj) { 1626 var instance = _(obj); 1627 instance._chain = true; 1628 return instance; 1629 }; 1630 1631 // OOP 1632 // --------------- 1633 // If Underscore is called as a function, it returns a wrapped object that 1634 // can be used OO-style. This wrapper holds altered versions of all the 1635 // underscore functions. Wrapped objects may be chained. 1636 1637 // Helper function to continue chaining intermediate results. 1638 var chainResult = function(instance, obj) { 1639 return instance._chain ? _(obj).chain() : obj; 1640 }; 1641 1642 // Add your own custom functions to the Underscore object. 1643 _.mixin = function(obj) { 1644 _.each(_.functions(obj), function(name) { 1645 var func = _[name] = obj[name]; 1646 _.prototype[name] = function() { 1647 var args = [this._wrapped]; 1648 push.apply(args, arguments); 1649 return chainResult(this, func.apply(_, args)); 1650 }; 1651 }); 1652 return _; 1653 }; 1654 1655 // Add all of the Underscore functions to the wrapper object. 1656 _.mixin(_); 1657 1658 // Add all mutator Array functions to the wrapper. 1659 _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1660 var method = ArrayProto[name]; 1661 _.prototype[name] = function() { 1662 var obj = this._wrapped; 1663 method.apply(obj, arguments); 1664 if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; 1665 return chainResult(this, obj); 1666 }; 1667 }); 1668 1669 // Add all accessor Array functions to the wrapper. 1670 _.each(['concat', 'join', 'slice'], function(name) { 1671 var method = ArrayProto[name]; 1672 _.prototype[name] = function() { 1673 return chainResult(this, method.apply(this._wrapped, arguments)); 1674 }; 1675 }); 1676 1677 // Extracts the result from a wrapped and chained object. 1678 _.prototype.value = function() { 1679 return this._wrapped; 1680 }; 1681 1682 // Provide unwrapping proxy for some methods used in engine operations 1683 // such as arithmetic and JSON stringification. 1684 _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; 1685 1686 _.prototype.toString = function() { 1687 return String(this._wrapped); 1688 }; 1689 }()); 1690 }); 1691 var underscore_1 = underscore._; 1692 1693 // https://gist.github.com/bmershon/25a74f7b1c7cbd07e7456af1d2c07da1 1694 1695 /** 1696 * This function create the pairs between node and its links. 1697 * 1698 * @param links 1699 * @returns [["nodeX,nodeY", Array(numberOfLinksRelatedToTheNodes)]] 1700 */ 1701 function pairNodeLinks(links) { 1702 let nestedByNodes = {}; 1703 links.forEach(l => { 1704 let sourceKey = l.source.join(','); 1705 if (!nestedByNodes[sourceKey]) { 1706 nestedByNodes[sourceKey] = []; 1707 } 1708 nestedByNodes[sourceKey].push(l); 1709 let targetKey = l.target.join(','); 1710 if (!nestedByNodes[targetKey]) { 1711 nestedByNodes[targetKey] = []; 1712 } 1713 nestedByNodes[targetKey].push(l); 1714 }); 1715 //Pair the results
1716 let pairedResults = underscore.pairs(nestedByNodes); 1717 return pairedResults; 1718 } 1719 1720 /** 1721 * This function returns corners (three vertices) of vertices of degree two in the for mat of 1722 * point1, point2, point3 => point1 is the the vertex with degree two (two edges connected to it are [point1, point2] and [point1, point3] (order of the points in each edge is not important)). 1723 * @param tree 1724 * @returns {*} 1725 */ 1726 function getAllV2CornersFromTree(tree) { 1727 let pairedResults = pairNodeLinks(tree.links); 1728 //Get all pairs with length = 2 (V2) 1729 let allV2 = pairedResults.filter(p => p[1].length == 2); 1730 1731 let allCorners = allV2.map(v2 => { 1732 let corner = []; 1733 //First point is the common vertice 1734 corner.push(v2[0].split(',').map(d => +d));//map(d=>+d) is to convert the strings into digits 1735 //Push the source or target if they are not the common vertices of the two edges 1736 v2[1].forEach(link => { 1737 if (link.source.join(',') != v2[0]) { 1738 corner.push(link.source); 1739 } else { 1740 corner.push(link.target); 1741 } 1742 }); 1743 return corner; 1744 }); 1745 return allCorners; 1746 } 1747 1748 /** 1749 * This function returns all single degree vertices from a tree 1750 * @param tree 1751 */ 1752 function getAllV1sFromTree(tree){ 1753 let pairedResults = pairNodeLinks(tree.links); 1754 //Get all pairs with length = 2 (V2) 1755 let allV1 = pairedResults.filter(p => p[1].length == 1); 1756 return allV1.map(v1=>v1[0].split(',').map(Number)); 1757 } 1758 /** 1759 * Create a graph from mesh 1760 * @param triangles is inform of set of triangles as the result from delaunay triangulations 1761 */ 1762 function createGraph (triangles) { 1763 1764 function makeLink(sourceId, targetId, weight) { 1765 return {"source": sourceId, "target": targetId, "weight": weight}; 1766 } 1767 1768 let graph = {}; 1769 graph.nodes = []; 1770 graph.links = []; 1771 //Creating nodes 1772 triangles.forEach(t => { 1773 for (let i = 0; i < 3; i++) { 1774 let id = t[i]; 1775 if (!idExists(graph.nodes, id)) { 1776 graph.nodes.push(makeNode(id)); 1777 } 1778 } 1779 }); 1780 1781 //Creating links 1782 triangles.forEach(t => { 1783 for (let i = 0; i < 3; i++) { 1784 let p1 = t[i]; 1785 let p2 = t[(i + 1) % 3]; 1786 let id1 = p1; 1787 let id2 = p2; 1788 let dist = distance(p1, p2); 1789 let link = makeLink(id1, id2, dist); 1790 if (!linkExists(graph.links, link)) { 1791 graph.links.push(link); 1792 } 1793 } 1794 }); 1795 1796 1797 //TODO: may sort the id alphabetically => when creating => so we can just check 1 condition only. 1798 function linkExists(links, link) { 1799 let length = links.length; 1800 for (let i = length-1; i >= 0; --i) { 1801 if (equalLinks(link, links[i])) { 1802 return true; 1803 } 1804 } 1805 return false; 1806 } 1807 1808 return graph; 1809 } 1810 1811 function distance(a, b) { 1812 let dx = a[0] - b[0], 1813 dy = a[1] - b[1]; 1814 //For computer storage issue, some coordinates of the same distance may return different distances if we use long floating point 1815 //So take only 10 digits after the floating points=> this is precise enough and still have the same values for two different lines of the same distance 1816 return Math.round(Math.sqrt((dx * dx) + (dy * dy))*Math.pow(10, 10))/Math.pow(10, 10); 1817 } 1818 1819 function equalPoints(id1, id2) { 1820 return (id1[0] === id2[0] && id1[1] === id2[1]); 1821 } 1822 function equalLinks(l1, l2) { 1823 return (equalPoints(l1.source, l2.source) && equalPoints(l1.target, l2.target)) || 1824 (equalPoints(l1.source, l2.target) && equalPoints(l1.target, l2.source)); 1825 } 1826 function idExists(nodes, id) { 1827 let length = nodes.length; 1828 for (let i = length-1; i >= 0; --i) { 1829 let node = nodes[i]; 1830 if (equalPoints(node.id, id)) { 1831 return true; 1832 } 1833 } 1834 return false; 1835 } 1836 function makeNode(id) { 1837 return {"id": id}; 1838 } 1839 /** 1840 * create the mst 1841 * @param graph: in form of nodes and links 1842 * @returns {{nodes: (selection_nodes|nodes), links: Array}} 1843 */ 1844 function mst (graph) { 1845 let vertices = graph.nodes, 1846 edges = graph.links.slice(0), 1847 selectedEdges = [], 1848 forest = new DisjointSet(); 1849 1850 // Each vertex begins "disconnected" and isolated from all the others.
1851 vertices.forEach((vertex) => { 1852 forest.makeSet(vertex.id); 1853 }); 1854 1855 // Sort edges in descending order of weight. We will pop edges beginning 1856 // from the end of the array. 1857 edges.sort((a, b) => { 1858 return -(a.weight - b.weight); 1859 }); 1860 1861 while (edges.length && forest.size() > 1) { 1862 let edge = edges.pop(); 1863 1864 if (forest.find(edge.source) !== forest.find(edge.target)) { 1865 forest.union(edge.source, edge.target); 1866 selectedEdges.push(edge); 1867 } 1868 } 1869 1870 return { 1871 nodes: vertices, 1872 links: selectedEdges 1873 } 1874 } 1875 1876 1877 function DisjointSet() { 1878 this.index_ = {}; 1879 } 1880 1881 function Node(id) { 1882 this.id_ = id; 1883 this.parent_ = this; 1884 this.rank_ = 0; 1885 } 1886 1887 DisjointSet.prototype.makeSet = function (id) { 1888 if (!this.index_[id]) { 1889 let created = new Node(id); 1890 this.index_[id] = created; 1891 } 1892 }; 1893 1894 // Returns the id of the representative element of this set that (id) 1895 // belongs to. 1896 DisjointSet.prototype.find = function (id) { 1897 if (this.index_[id] === undefined) { 1898 return undefined; 1899 } 1900 1901 let current = this.index_[id].parent_; 1902 while (current !== current.parent_) { 1903 current = current.parent_; 1904 } 1905 return current.id_; 1906 }; 1907 1908 DisjointSet.prototype.union = function (x, y) { 1909 let xRoot = this.index_[this.find(x)]; 1910 let yRoot = this.index_[this.find(y)]; 1911 1912 if (xRoot === undefined || yRoot === undefined || xRoot === yRoot) { 1913 // x and y already belong to the same set. 1914 return; 1915 } 1916 1917 if (xRoot.rank < yRoot.rank) { // Move x into the set y is a member of. 1918 xRoot.parent_ = yRoot; 1919 } else if (yRoot.rank_ < xRoot.rank_) { // Move y into the set x is a member of. 1920 yRoot.parent_ = xRoot; 1921 } else { // Arbitrarily choose to move y into the set x is a member of. 1922 yRoot.parent_ = xRoot; 1923 xRoot.rank_++; 1924 } 1925 }; 1926 1927 // Returns the current number of disjoint sets. 1928 DisjointSet.prototype.size = function () { 1929 let uniqueIndices = {}; 1930 Object.keys(this.index_).forEach((id) => { 1931 uniqueIndices[id] = true; 1932 }); 1933 return Object.keys(uniqueIndices).length; 1934 }; 1935 1936 /* @flow */ 1937 1938 /* @flow */ 1939 1940 /** 1941 * This computes the maximum number in an array. 1942 * 1943 * This runs on `O(n)`, linear time in respect to the array 1944 * 1945 * @param {Array<number>} x sample of one or more data points 1946 * @returns {number} maximum value 1947 * @throws {Error} if the the length of x is less than one 1948 * @example 1949 * max([1, 2, 3, 4]); 1950 * // => 4 1951 */ 1952 function max(x /*: Array<number> */) /*:number*/ { 1953 1954 if (x.length === 0) { 1955 throw new Error('max requires at least one data point'); 1956 } 1957 1958 var value = x[0]; 1959 for (var i = 1; i < x.length; i++) { 1960 // On the first iteration of this loop, max is 1961 // undefined and is thus made the maximum element in the array 1962 if (x[i] > value) { 1963 value = x[i]; 1964 } 1965 } 1966 return value; 1967 } 1968 1969 /* @flow */ 1970 1971 /** 1972 * This is the internal implementation of quantiles: when you know 1973 * that the order is sorted, you don't need to re-sort it, and the computations 1974 * are faster. 1975 * 1976 * @param {Array<number>} x sample of one or more data points 1977 * @param {number} p desired quantile: a number between 0 to 1, inclusive 1978 * @returns {number} quantile value 1979 * @throws {Error} if p ix outside of the range from 0 to 1 1980 * @throws {Error} if x is empty 1981 * @example 1982 * quantileSorted([3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 0.5); // => 9 1983 */ 1984 function quantileSorted(x /*: Array<number> */, p /*: number */)/*: number */ { 1985 var idx = x.length * p; 1986 if (x.length === 0) { 1987 throw new Error('quantile requires at least one data point.'); 1988 } else if (p < 0 || p > 1) { 1989 throw new Error('quantiles must be between 0 and 1'); 1990 } else if (p === 1) { 1991 // If p is 1, directly return the last element 1992 return x[x.length - 1]; 1993 } else if (p === 0) { 1994 // If p is 0, directly return the first element 1995 return x[0]; 1996 } else if (idx % 1 !== 0) { 1997 // If p is not integer, return the next element in array 1998 return x[Math.ceil(idx) - 1]; 1999 } else if (x.length % 2 === 0) { 2000 // If the list has even-length, we'll take the average of this number 2001 // and the next value, if there is one 2002 return (x[idx - 1] + x[idx]) / 2; 2003 } else { 2004 // Finally, in the simple case of an integer value 2005 // with an odd-length list, return the x value at the index. 2006 return x[idx]; 2007 } 2008 } 2009 2010 /* @flow */ 2011 2012 /** 2013 * Rearrange items in `arr` so that all items in `[left, k]` range are the smallest. 2014 * The `k`-th element will have the `(k - left + 1)`-th smallest value in `[left, right]`. 2015 * 2016 * Implements Floyd-Rivest selection algorithm https://en.wikipedia.org/wiki/Floyd-Rivest_algorithm 2017 * 2018 * @param {Array<number>} arr input array 2019 * @param {number} k pivot index 2020 * @param {number} [left] left index 2021 * @param {number} [right] right index 2022 * @returns {void} mutates input array 2023 * @example 2024 * var arr = [65, 28, 59, 33, 21, 56, 22, 95, 50, 12, 90, 53, 28, 77, 39]; 2025 * quickselect(arr, 8); 2026 * // = [39, 28, 28, 33, 21, 12, 22, 50, 53, 56, 59, 65, 90, 77, 95] 2027 */ 2028 function quickselect(arr/*: Array<number> */, k/*: number */, left/*: ?number */, right/*: ?number */)/*: void */ { 2029 left = left || 0; 2030 right = right || (arr.length - 1); 2031 2032 while (right > left) { 2033 // 600 and 0.5 are arbitrary constants chosen in the original paper to minimize execution time 2034 if (right - left > 600) { 2035 var n = right - left + 1; 2036 var m = k - left + 1; 2037 var z = Math.log(n); 2038 var s = 0.5 * Math.exp(2 * z / 3); 2039 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n); 2040 if (m - n / 2 < 0) sd *= -1; 2041 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); 2042 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); 2043 quickselect(arr, k, newLeft, newRight); 2044 } 2045 2046 var t = arr[k]; 2047 var i = left; 2048 var j = right; 2049 2050 swap(arr, left, k); 2051 if (arr[right] > t) swap(arr, left, right); 2052 2053 while (i < j) { 2054 swap(arr, i, j); 2055 i++; 2056 j--; 2057 while (arr[i] < t) i++; 2058 while (arr[j] > t) j--; 2059 } 2060 2061 if (arr[left] === t) swap(arr, left, j); 2062 else { 2063 j++; 2064 swap(arr, j, right); 2065 } 2066 2067 if (j <= k) left = j + 1; 2068 if (k <= j) right = j - 1; 2069 } 2070 } 2071 2072 function swap(arr, i, j) { 2073 var tmp = arr[i]; 2074 arr[i] = arr[j]; 2075 arr[j] = tmp; 2076 } 2077 2078 /* @flow */ 2079 2080 /** 2081 * The [quantile](https://en.wikipedia.org/wiki/Quantile): 2082 * this is a population quantile, since we assume to know the entire 2083 * dataset in this library. This is an implementation of the 2084 * [Quantiles of a Population](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population) 2085 * algorithm from wikipedia. 2086 * 2087 * Sample is a one-dimensional array of numbers, 2088 * and p is either a decimal number from 0 to 1 or an array of decimal 2089 * numbers from 0 to 1. 2090 * In terms of a k/q quantile, p = k/q - it's just dealing with fractions or dealing 2091 * with decimal values. 2092 * When p is an array, the result of the function is also an array containing the appropriate 2093 * quantiles in input order 2094 * 2095 * @param {Array<number>} x sample of one or more numbers 2096 * @param {Array<number> | number} p the desired quantile, as a number between 0 and 1 2097 * @returns {number} quantile 2098 * @example 2099 * quantile([3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 0.5); // => 9 2100 */ 2101 function quantile(x/*: Array<number> */, p/*: Array<number> | number */)/*: Array<number> | number */ { 2102 var copy = x.slice(); 2103 2104 if (Array.isArray(p)) { 2105 // rearrange elements so that each element corresponding to a requested 2106 // quantile is on a place it would be if the array was fully sorted 2107 multiQuantileSelect(copy, p); 2108 // Initialize the result array 2109 var results = []; 2110 // For each requested quantile 2111 for (var i = 0; i < p.length; i++) { 2112 results[i] = quantileSorted(copy, p[i]); 2113 } 2114 return results; 2115 } else { 2116 var idx = quantileIndex(copy.length, p); 2117 quantileSelect(copy, idx, 0, copy.length - 1); 2118 return quantileSorted(copy, p); 2119 } 2120 } 2121 2122 function quantileSelect(arr, k, left, right) { 2123 if (k % 1 === 0) { 2124 quickselect(arr, k, left, right); 2125 } else { 2126 k = Math.floor(k); 2127 quickselect(arr, k, left, right); 2128 quickselect(arr, k + 1, k + 1, right); 2129 } 2130 } 2131 2132 function multiQuantileSelect(arr, p) { 2133 var indices = [0]; 2134 for (var i = 0; i < p.length; i++) { 2135 indices.push(quantileIndex(arr.length, p[i])); 2136 } 2137 indices.push(arr.length - 1); 2138 indices.sort(compare); 2139 2140 var stack = [0, indices.length - 1]; 2141 2142 while (stack.length) {
2143 var r = Math.ceil(stack.pop()); 2144 var l = Math.floor(stack.pop()); 2145 if (r - l <= 1) continue; 2146 2147 var m = Math.floor((l + r) / 2); 2148 quantileSelect(arr, indices[m], indices[l], indices[r]); 2149 2150 stack.push(l, m, m, r); 2151 } 2152 } 2153 2154 function compare(a, b) { 2155 return a - b; 2156 } 2157 2158 function quantileIndex(len /*: number */, p /*: number */)/*:number*/ { 2159 var idx = len * p; 2160 if (p === 1) { 2161 // If p is 1, directly return the last index 2162 return len - 1; 2163 } else if (p === 0) { 2164 // If p is 0, directly return the first index 2165 return 0; 2166 } else if (idx % 1 !== 0) { 2167 // If index is not integer, return the next index in array 2168 return Math.ceil(idx) - 1; 2169 } else if (len % 2 === 0) { 2170 // If the list has even-length, we'll return the middle of two indices 2171 // around quantile to indicate that we need an average value of the two 2172 return idx - 0.5; 2173 } else { 2174 // Finally, in the simple case of an integer index 2175 // with an odd-length list, return the index 2176 return idx; 2177 } 2178 } 2179 var LOGSQRT2PI = Math.log(Math.sqrt(2 * Math.PI)); 2180 2181 /* @flow */ 2182 2183 var SQRT_2PI = Math.sqrt(2 * Math.PI); 2184 2185 /* @flow */ 2186 2187 var SQRT_2PI$1 = Math.sqrt(2 * Math.PI); 2188 2189 class Clumpy { 2190 constructor(tree) { 2191 //Clone the tree to avoid modifying it 2192 this.tree = JSON.parse(JSON.stringify(tree)); 2193 } 2194 2195 /** 2196 * Returns clumpy score 2197 * @returns {number} 2198 */ 2199 score() { 2200 let allRuntRatios = []; 2201 this.tree.links.forEach(link =>{ 2202 let rg = this.runtGraph(link); 2203 if(rg.length>0){ 2204 allRuntRatios.push(this.maxLength(rg)/link.weight); 2205 } 2206 }); 2207 if(allRuntRatios.length>0){ 2208 //Only if there are some runt graphs 2209 return max(allRuntRatios.map(rr=>1-rr)); 2210 }else{ 2211 //In case all lengths are equal => then the score is 0 2212 return 0; 2213 } 2214 } 2215 // score() { 2216 // return max(this.tree.links.map(link=>1-this.maxLength(this.runtGraph(link))/link.weight)); 2217 // } 2218 // runtGraph(link){ 2219 // let greaterOrEqualLinks = this.tree.links.filter(l=>l.weight >= link.weight); 2220 // //Remove the currently checking link. 2221 // greaterOrEqualLinks = greaterOrEqualLinks.filter(l=>!equalLinks(l, link)); 2222 // let pairedResults = pairNodeLinks(greaterOrEqualLinks); 2223 // 2224 // //Process the source side. 2225 // let sourceConnectedNodes = [link.source]; 2226 // let sourceConnectedLinks = this.getConnectedLinks(sourceConnectedNodes, pairedResults); 2227 // 2228 // let targetConnectedNodes = [link.target]; 2229 // let targetConnectedLinks = this.getConnectedLinks(targetConnectedNodes, pairedResults); 2230 // 2231 // return sourceConnectedLinks.length < targetConnectedLinks.length?sourceConnectedLinks:targetConnectedLinks; 2232 // } 2233 runtGraph(link){ 2234 let greaterOrEqualLinks = this.tree.links.filter(l=>l.weight < link.weight); 2235 //Remove the currently checking link. 2236 greaterOrEqualLinks = greaterOrEqualLinks.filter(l=>!equalLinks(l, link)); 2237 let pairedResults = pairNodeLinks(greaterOrEqualLinks); 2238 2239 //Process the source side. 2240 let sourceConnectedNodes = [link.source]; 2241 let sourceConnectedLinks = this.getConnectedLinks(sourceConnectedNodes, pairedResults); 2242 2243 let targetConnectedNodes = [link.target]; 2244 let targetConnectedLinks = this.getConnectedLinks(targetConnectedNodes, pairedResults); 2245 2246 return sourceConnectedLinks.length < targetConnectedLinks.length?sourceConnectedLinks:targetConnectedLinks; 2247 } 2248 2249 2250 getConnectedLinks(connectedNodes, pairedResults) { 2251 let processedNodes = []; 2252 let connectedLinks = []; 2253 while (connectedNodes.length > 0) { 2254 //Can stop earlier if this is having more than half of the links in the whole tree. 2255 if(connectedLinks.length > this.tree.links.length + 1){ 2256 break; 2257 } 2258 let firstNode = underscore.first(connectedNodes); 2259 //Removed the processed nodes 2260 connectedNodes = underscore.without(connectedNodes, firstNode); 2261 //Add it to the processed node 2262 processedNodes.push(firstNode); 2263 //Find the edges connected to that node. 2264 let result = pairedResults.find(p => p[0] === firstNode.join(",")); 2265 let links = result?result[1]:[]; 2266 connectedLinks = connectedLinks.concat(links); 2267 //Add new nodes to be processed 2268 links.forEach(l => { 2269 //If the node in the connected link is not processed => then add it to be processed (to expand later on). 2270 if (!pointExists(processedNodes, l.source)) { 2271 connectedNodes.push(l.source); 2272 } 2273 if(!pointExists(processedNodes, l.target)) { 2274 connectedNodes.push(l.target); 2275 } 2276 }); 2277 } 2278 return connectedLinks; 2279 } 2280 2281 maxLength(runtGraph){ 2282 if(runtGraph.length===0){ 2283 return 0; 2284 }
2285 return max(runtGraph.map(l=>l.weight)); 2286 } 2287 } 2288 function pointExists(points, point){ 2289 for (let i = 0; i < points.length; i++) { 2290 let point1 = points[i]; 2291 if (equalPoints(point1, point)) { 2292 return true; 2293 } 2294 } 2295 return false; 2296 } 2297 2298 const EPSILON = Math.pow(2, -52); 2299 2300 class Delaunator { 2301 2302 static from(points, getX, getY) { 2303 if (!getX) getX = defaultGetX; 2304 if (!getY) getY = defaultGetY; 2305 2306 const n = points.length; 2307 const coords = new Float64Array(n * 2); 2308 2309 for (let i = 0; i < n; i++) { 2310 const p = points[i]; 2311 coords[2 * i] = getX(p); 2312 coords[2 * i + 1] = getY(p); 2313 } 2314 2315 return new Delaunator(coords); 2316 } 2317 2318 constructor(coords) { 2319 let minX = Infinity; 2320 let minY = Infinity; 2321 let maxX = -Infinity; 2322 let maxY = -Infinity; 2323 2324 const n = coords.length >> 1; 2325 const ids = this.ids = new Uint32Array(n); 2326 2327 if (n > 0 && typeof coords[0] !== 'number') throw new Error('Expected coords to contain numbers.'); 2328 2329 this.coords = coords; 2330 2331 for (let i = 0; i < n; i++) { 2332 const x = coords[2 * i]; 2333 const y = coords[2 * i + 1]; 2334 if (x < minX) minX = x; 2335 if (y < minY) minY = y; 2336 if (x > maxX) maxX = x; 2337 if (y > maxY) maxY = y; 2338 ids[i] = i; 2339 } 2340 2341 const cx = (minX + maxX) / 2; 2342 const cy = (minY + maxY) / 2; 2343 2344 let minDist = Infinity; 2345 let i0, i1, i2; 2346 2347 // pick a seed point close to the centroid 2348 for (let i = 0; i < n; i++) { 2349 const d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]); 2350 if (d < minDist) { 2351 i0 = i; 2352 minDist = d; 2353 } 2354 } 2355 const i0x = coords[2 * i0]; 2356 const i0y = coords[2 * i0 + 1]; 2357 2358 minDist = Infinity; 2359 2360 // find the point closest to the seed 2361 for (let i = 0; i < n; i++) { 2362 if (i === i0) continue; 2363 const d = dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]); 2364 if (d < minDist && d > 0) { 2365 i1 = i; 2366 minDist = d; 2367 } 2368 } 2369 let i1x = coords[2 * i1]; 2370 let i1y = coords[2 * i1 + 1]; 2371 2372 let minRadius = Infinity; 2373 2374 // find the third point which forms the smallest circumcircle with the first two 2375 for (let i = 0; i < n; i++) { 2376 if (i === i0 || i === i1) continue; 2377 const r = circumradius(i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]); 2378 if (r < minRadius) { 2379 i2 = i; 2380 minRadius = r; 2381 } 2382 } 2383 let i2x = coords[2 * i2]; 2384 let i2y = coords[2 * i2 + 1]; 2385 2386 if (minRadius === Infinity) { 2387 throw new Error('No Delaunay triangulation exists for this input.'); 2388 } 2389 2390 // swap the order of the seed points for counter-clockwise orientation 2391 if (orient(i0x, i0y, i1x, i1y, i2x, i2y)) { 2392 const i = i1; 2393 const x = i1x; 2394 const y = i1y; 2395 i1 = i2; 2396 i1x = i2x; 2397 i1y = i2y; 2398 i2 = i; 2399 i2x = x; 2400 i2y = y; 2401 } 2402 2403 const center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y); 2404 this._cx = center.x; 2405 this._cy = center.y; 2406 2407 // sort the points by distance from the seed triangle circumcenter 2408 quicksort(ids, coords, 0, ids.length - 1, center.x, center.y); 2409 2410 // initialize a hash table for storing edges of the advancing convex hull 2411 this._hashSize = Math.ceil(Math.sqrt(n)); 2412 this._hash = new Array(this._hashSize); 2413 2414 // initialize a circular doubly-linked list that will hold an advancing convex hull 2415 let e = this.hull = insertNode(coords, i0); 2416 this._hashEdge(e); 2417 e.t = 0; 2418 e = insertNode(coords, i1, e); 2419 this._hashEdge(e); 2420 e.t = 1;
2421 e = insertNode(coords, i2, e); 2422 this._hashEdge(e); 2423 e.t = 2; 2424 2425 const maxTriangles = 2 * n - 5; 2426 const triangles = this.triangles = new Uint32Array(maxTriangles * 3); 2427 const halfedges = this.halfedges = new Int32Array(maxTriangles * 3); 2428 2429 this.trianglesLen = 0; 2430 2431 this._addTriangle(i0, i1, i2, -1, -1, -1); 2432 2433 for (let k = 0, xp, yp; k < ids.length; k++) { 2434 const i = ids[k]; 2435 const x = coords[2 * i]; 2436 const y = coords[2 * i + 1]; 2437 2438 // skip near-duplicate points 2439 if (k > 0 && Math.abs(x - xp) <= EPSILON && Math.abs(y - yp) <= EPSILON) continue; 2440 xp = x; 2441 yp = y; 2442 2443 // skip seed triangle points 2444 if (i === i0 || i === i1 || i === i2) continue; 2445 2446 // find a visible edge on the convex hull using edge hash 2447 const startKey = this._hashKey(x, y); 2448 let key = startKey; 2449 let start; 2450 do { 2451 start = this._hash[key]; 2452 key = (key + 1) % this._hashSize; 2453 } while ((!start || start.removed) && key !== startKey); 2454 2455 start = start.prev; 2456 e = start; 2457 while (!orient(x, y, e.x, e.y, e.next.x, e.next.y)) { 2458 e = e.next; 2459 if (e === start) { 2460 e = null; 2461 break; 2462 } 2463 } 2464 // likely a near-duplicate point; skip it 2465 if (!e) continue; 2466 2467 const walkBack = e === start; 2468 2469 // add the first triangle from the point 2470 let t = this._addTriangle(e.i, i, e.next.i, -1, -1, e.t); 2471 2472 e.t = t; // keep track of boundary triangles on the hull 2473 e = insertNode(coords, i, e); 2474 2475 // recursively flip triangles from the point until they satisfy the Delaunay condition 2476 e.t = this._legalize(t + 2); 2477 2478 // walk forward through the hull, adding more triangles and flipping recursively 2479 let q = e.next; 2480 while (orient(x, y, q.x, q.y, q.next.x, q.next.y)) { 2481 t = this._addTriangle(q.i, i, q.next.i, q.prev.t, -1, q.t); 2482 q.prev.t = this._legalize(t + 2); 2483 this.hull = removeNode(q); 2484 q = q.next; 2485 } 2486 2487 if (walkBack) { 2488 // walk backward from the other side, adding more triangles and flipping 2489 q = e.prev; 2490 while (orient(x, y, q.prev.x, q.prev.y, q.x, q.y)) { 2491 t = this._addTriangle(q.prev.i, i, q.i, -1, q.t, q.prev.t); 2492 this._legalize(t + 2); 2493 q.prev.t = t; 2494 this.hull = removeNode(q); 2495 q = q.prev; 2496 } 2497 } 2498 2499 // save the two new edges in the hash table 2500 this._hashEdge(e); 2501 this._hashEdge(e.prev); 2502 } 2503 2504 // trim typed triangle mesh arrays 2505 this.triangles = triangles.subarray(0, this.trianglesLen); 2506 this.halfedges = halfedges.subarray(0, this.trianglesLen); 2507 } 2508 2509 _hashEdge(e) { 2510 this._hash[this._hashKey(e.x, e.y)] = e; 2511 } 2512 2513 _hashKey(x, y) { 2514 return Math.floor(pseudoAngle(x - this._cx, y - this._cy) * this._hashSize) % this._hashSize; 2515 } 2516 2517 _legalize(a) { 2518 const {triangles, coords, halfedges} = this; 2519 2520 const b = halfedges[a]; 2521 2522 /* if the pair of triangles doesn't satisfy the Delaunay condition 2523 * (p1 is inside the circumcircle of [p0, pl, pr]), flip them, 2524 * then do the same check/flip recursively for the new pair of triangles 2525 * 2526 * pl pl 2527 * /||\ / \ 2528 * al/ || \bl al/ \a 2529 * / || \ / \ 2530 * / a||b \ flip /___ar___\ 2531 * p0\ || /p1 => p0\---bl---/p1 2532 * \ || / \ / 2533 * ar\ || /br b\ /br 2534 * \||/ \ / 2535 * pr pr 2536 */ 2537 const a0 = a - a % 3; 2538 const b0 = b - b % 3; 2539 2540 const al = a0 + (a + 1) % 3; 2541 const ar = a0 + (a + 2) % 3; 2542 const bl = b0 + (b + 2) % 3; 2543 2544 if (b === -1) return ar; 2545 2546 const p0 = triangles[ar]; 2547 const pr = triangles[a]; 2548 const pl = triangles[al]; 2549 const p1 = triangles[bl]; 2550 2551 const illegal = inCircle( 2552 coords[2 * p0], coords[2 * p0 + 1], 2553 coords[2 * pr], coords[2 * pr + 1], 2554 coords[2 * pl], coords[2 * pl + 1], 2555 coords[2 * p1], coords[2 * p1 + 1]); 2556 2557 if (illegal) { 2558 triangles[a] = p1; 2559 triangles[b] = p0; 2560 2561 const hbl = halfedges[bl]; 2562 2563 // edge swapped on the other side of the hull (rare); fix the halfedge reference 2564 if (hbl === -1) { 2565 let e = this.hull; 2566 do { 2567 if (e.t === bl) { 2568 e.t = a; 2569 break; 2570 } 2571 e = e.next; 2572 } while (e !== this.hull); 2573 } 2574 this._link(a, hbl); 2575 this._link(b, halfedges[ar]); 2576 this._link(ar, bl); 2577 2578 const br = b0 + (b + 1) % 3; 2579 2580 this._legalize(a); 2581 return this._legalize(br); 2582 } 2583 2584 return ar; 2585 } 2586 2587 _link(a, b) { 2588 this.halfedges[a] = b; 2589 if (b !== -1) this.halfedges[b] = a; 2590 } 2591 2592 // add a new triangle given vertex indices and adjacent half-edge ids 2593 _addTriangle(i0, i1, i2, a, b, c) { 2594 const t = this.trianglesLen; 2595 2596 this.triangles[t] = i0; 2597 this.triangles[t + 1] = i1; 2598 this.triangles[t + 2] = i2; 2599 2600 this._link(t, a); 2601 this._link(t + 1, b); 2602 this._link(t + 2, c); 2603 2604 this.trianglesLen += 3; 2605 2606 return t; 2607 } 2608 } 2609 2610 // monotonically increases with real angle, but doesn't need expensive trigonometry 2611 function pseudoAngle(dx, dy) { 2612 const p = dx / (Math.abs(dx) + Math.abs(dy)); 2613 return (dy > 0 ? 3 - p : 1 + p) / 4; // [0..1] 2614 } 2615 2616 function dist(ax, ay, bx, by) { 2617 const dx = ax - bx; 2618 const dy = ay - by; 2619 return dx * dx + dy * dy; 2620 } 2621 2622 function orient(px, py, qx, qy, rx, ry) { 2623 return (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0; 2624 } 2625 2626 function inCircle(ax, ay, bx, by, cx, cy, px, py) { 2627 const dx = ax - px; 2628 const dy = ay - py; 2629 const ex = bx - px; 2630 const ey = by - py; 2631 const fx = cx - px; 2632 const fy = cy - py; 2633 2634 const ap = dx * dx + dy * dy; 2635 const bp = ex * ex + ey * ey; 2636 const cp = fx * fx + fy * fy; 2637 2638 return dx * (ey * cp - bp * fy) - 2639 dy * (ex * cp - bp * fx) + 2640 ap * (ex * fy - ey * fx) < 0; 2641 } 2642 2643 function circumradius(ax, ay, bx, by, cx, cy) { 2644 const dx = bx - ax; 2645 const dy = by - ay; 2646 const ex = cx - ax; 2647 const ey = cy - ay; 2648 2649 const bl = dx * dx + dy * dy; 2650 const cl = ex * ex + ey * ey; 2651 const d = dx * ey - dy * ex; 2652 2653 const x = (ey * bl - dy * cl) * 0.5 / d; 2654 const y = (dx * cl - ex * bl) * 0.5 / d; 2655 2656 return bl && cl && d && (x * x + y * y) || Infinity; 2657 } 2658 2659 function circumcenter(ax, ay, bx, by, cx, cy) { 2660 const dx = bx - ax; 2661 const dy = by - ay; 2662 const ex = cx - ax; 2663 const ey = cy - ay; 2664 2665 const bl = dx * dx + dy * dy; 2666 const cl = ex * ex + ey * ey; 2667 const d = dx * ey - dy * ex; 2668 2669 const x = ax + (ey * bl - dy * cl) * 0.5 / d; 2670 const y = ay + (dx * cl - ex * bl) * 0.5 / d; 2671 2672 return {x, y}; 2673 } 2674 2675 // create a new node in a doubly linked list
2676 function insertNode(coords, i, prev) { 2677 const node = { 2678 i, 2679 x: coords[2 * i], 2680 y: coords[2 * i + 1], 2681 t: 0, 2682 prev: null, 2683 next: null, 2684 removed: false 2685 }; 2686 2687 if (!prev) { 2688 node.prev = node; 2689 node.next = node; 2690 2691 } else { 2692 node.next = prev.next; 2693 node.prev = prev; 2694 prev.next.prev = node; 2695 prev.next = node; 2696 } 2697 return node; 2698 } 2699 2700 function removeNode(node) { 2701 node.prev.next = node.next; 2702 node.next.prev = node.prev; 2703 node.removed = true; 2704 return node.prev; 2705 } 2706 2707 function quicksort(ids, coords, left, right, cx, cy) { 2708 let i, j, temp; 2709 2710 if (right - left <= 20) { 2711 for (i = left + 1; i <= right; i++) { 2712 temp = ids[i]; 2713 j = i - 1; 2714 while (j >= left && compare$1(coords, ids[j], temp, cx, cy) > 0) ids[j + 1] = ids[j--]; 2715 ids[j + 1] = temp; 2716 } 2717 } else { 2718 const median = (left + right) >> 1; 2719 i = left + 1; 2720 j = right; 2721 swap$1(ids, median, i); 2722 if (compare$1(coords, ids[left], ids[right], cx, cy) > 0) swap$1(ids, left, right); 2723 if (compare$1(coords, ids[i], ids[right], cx, cy) > 0) swap$1(ids, i, right); 2724 if (compare$1(coords, ids[left], ids[i], cx, cy) > 0) swap$1(ids, left, i); 2725 2726 temp = ids[i]; 2727 while (true) { 2728 do i++; while (compare$1(coords, ids[i], temp, cx, cy) < 0); 2729 do j--; while (compare$1(coords, ids[j], temp, cx, cy) > 0); 2730 if (j < i) break; 2731 swap$1(ids, i, j); 2732 } 2733 ids[left + 1] = ids[j]; 2734 ids[j] = temp; 2735 2736 if (right - i + 1 >= j - left) { 2737 quicksort(ids, coords, i, right, cx, cy); 2738 quicksort(ids, coords, left, j - 1, cx, cy); 2739 } else { 2740 quicksort(ids, coords, left, j - 1, cx, cy); 2741 quicksort(ids, coords, i, right, cx, cy); 2742 } 2743 } 2744 } 2745 2746 function compare$1(coords, i, j, cx, cy) { 2747 const d1 = dist(coords[2 * i], coords[2 * i + 1], cx, cy); 2748 const d2 = dist(coords[2 * j], coords[2 * j + 1], cx, cy); 2749 return (d1 - d2) || (coords[2 * i] - coords[2 * j]) || (coords[2 * i + 1] - coords[2 * j + 1]); 2750 } 2751 2752 function swap$1(arr, i, j) { 2753 const tmp = arr[i]; 2754 arr[i] = arr[j]; 2755 arr[j] = tmp; 2756 } 2757 2758 function defaultGetX(p) { 2759 return p[0]; 2760 } 2761 function defaultGetY(p) { 2762 return p[1]; 2763 } 2764 2765 const epsilon$1 = 1e-6; 2766 2767 class Path { 2768 constructor() { 2769 this._x0 = this._y0 = // start of current subpath 2770 this._x1 = this._y1 = null; // end of current subpath 2771 this._ = ""; 2772 } 2773 moveTo(x, y) { 2774 this._ += `M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}`; 2775 } 2776 closePath() { 2777 if (this._x1 !== null) { 2778 this._x1 = this._x0, this._y1 = this._y0; 2779 this._ += "Z"; 2780 } 2781 } 2782 lineTo(x, y) { 2783 this._ += `L${this._x1 = +x},${this._y1 = +y}`; 2784 } 2785 arc(x, y, r) { 2786 x = +x, y = +y, r = +r; 2787 const x0 = x + r; 2788 const y0 = y; 2789 if (r < 0) throw new Error("negative radius"); 2790 if (this._x1 === null) this._ += `M${x0},${y0}`; 2791 else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) this._ += "L" + x0 + "," + y0; 2792 if (!r) return; 2793 this._ += `A${r},${r},0,1,1,${x - r},${y}A${r},${r},0,1,1,${this._x1 = x0},${this._y1 = y0}`; 2794 } 2795 rect(x, y, w, h) { 2796 this._ += `M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}h${+w}v${+h}h${-w}Z`; 2797 } 2798 value() { 2799 return this._ || null; 2800 } 2801 } 2802 2803 class Polygon { 2804 constructor() { 2805 this._ = []; 2806 } 2807 moveTo(x, y) { 2808 this._.push([x, y]); 2809 } 2810 closePath() { 2811 this._.push(this._[0].slice()); 2812 } 2813 lineTo(x, y) { 2814 this._.push([x, y]); 2815 } 2816 value() { 2817 return this._.length ? this._ : null; 2818 } 2819 } 2820 2821 class Voronoi { 2822 constructor(delaunay, [xmin, ymin, xmax, ymax] = [0, 0, 960, 500]) { 2823 if (!((xmax = +xmax) >= (xmin = +xmin)) || !((ymax = +ymax) >= (ymin = +ymin))) throw new Error("invalid bounds"); 2824 const {points, hull, triangles} = this.delaunay = delaunay; 2825 const circumcenters = this.circumcenters = new Float64Array(triangles.length / 3 * 2); 2826 const vectors = this.vectors = new Float64Array(points.length * 2);
vendor: 6,957 bytes, lines 2827-3014
2827 this.xmax = xmax, this.xmin = xmin; 2828 this.ymax = ymax, this.ymin = ymin; 2829 2830 // Compute circumcenters. 2831 for (let i = 0, j = 0, n = triangles.length; i < n; i += 3, j += 2) { 2832 const t1 = triangles[i] * 2; 2833 const t2 = triangles[i + 1] * 2; 2834 const t3 = triangles[i + 2] * 2; 2835 const x1 = points[t1]; 2836 const y1 = points[t1 + 1]; 2837 const x2 = points[t2]; 2838 const y2 = points[t2 + 1]; 2839 const x3 = points[t3]; 2840 const y3 = points[t3 + 1]; 2841 const a2 = x1 - x2; 2842 const a3 = x1 - x3; 2843 const b2 = y1 - y2; 2844 const b3 = y1 - y3; 2845 const d1 = x1 * x1 + y1 * y1; 2846 const d2 = d1 - x2 * x2 - y2 * y2; 2847 const d3 = d1 - x3 * x3 - y3 * y3; 2848 const ab = (a3 * b2 - a2 * b3) * 2; 2849 circumcenters[j] = (b2 * d3 - b3 * d2) / ab; 2850 circumcenters[j + 1] = (a3 * d2 - a2 * d3) / ab; 2851 } 2852 2853 // Compute exterior cell rays. 2854 let node = hull; 2855 let p0, p1 = node.i * 4; 2856 let x0, x1 = node.x; 2857 let y0, y1 = node.y; 2858 do { 2859 node = node.next, p0 = p1, x0 = x1, y0 = y1, p1 = node.i * 4, x1 = node.x, y1 = node.y; 2860 vectors[p0 + 2] = vectors[p1] = y0 - y1; 2861 vectors[p0 + 3] = vectors[p1 + 1] = x1 - x0; 2862 } while (node !== hull); 2863 } 2864 render(context) { 2865 const buffer = context == null ? context = new Path : undefined; 2866 const {delaunay: {halfedges, hull}, circumcenters, vectors} = this; 2867 for (let i = 0, n = halfedges.length; i < n; ++i) { 2868 const j = halfedges[i]; 2869 if (j < i) continue; 2870 const ti = Math.floor(i / 3) * 2; 2871 const tj = Math.floor(j / 3) * 2; 2872 const xi = circumcenters[ti]; 2873 const yi = circumcenters[ti + 1]; 2874 const xj = circumcenters[tj]; 2875 const yj = circumcenters[tj + 1]; 2876 this._renderSegment(xi, yi, xj, yj, context); 2877 } 2878 let node = hull; 2879 do { 2880 node = node.next; 2881 const t = Math.floor(node.t / 3) * 2; 2882 const x = circumcenters[t]; 2883 const y = circumcenters[t + 1]; 2884 const v = node.i * 4; 2885 const p = this._project(x, y, vectors[v + 2], vectors[v + 3]); 2886 if (p) this._renderSegment(x, y, p[0], p[1], context); 2887 } while (node !== hull); 2888 return buffer && buffer.value(); 2889 } 2890 renderBounds(context) { 2891 const buffer = context == null ? context = new Path : undefined; 2892 context.rect(this.xmin, this.ymin, this.xmax - this.xmin, this.ymax - this.ymin); 2893 return buffer && buffer.value(); 2894 } 2895 renderCell(i, context) { 2896 const buffer = context == null ? context = new Path : undefined; 2897 const points = this._clip(i); 2898 if (points === null) return; 2899 context.moveTo(points[0], points[1]); 2900 for (let i = 2, n = points.length; i < n; i += 2) { 2901 context.lineTo(points[i], points[i + 1]); 2902 } 2903 context.closePath(); 2904 return buffer && buffer.value(); 2905 } 2906 *cellPolygons() { 2907 const {delaunay: {points}} = this; 2908 for (let i = 0, n = points.length / 2; i < n; ++i) { 2909 const cell = this.cellPolygon(i); 2910 if (cell) yield cell; 2911 } 2912 } 2913 cellPolygon(i) { 2914 const polygon = new Polygon; 2915 this.renderCell(i, polygon); 2916 return polygon.value(); 2917 } 2918 _renderSegment(x0, y0, x1, y1, context) { 2919 let S; 2920 const c0 = this._regioncode(x0, y0); 2921 const c1 = this._regioncode(x1, y1); 2922 if (c0 === 0 && c1 === 0) { 2923 context.moveTo(x0, y0); 2924 context.lineTo(x1, y1); 2925 } else if (S = this._clipSegment(x0, y0, x1, y1, c0, c1)) { 2926 context.moveTo(S[0], S[1]); 2927 context.lineTo(S[2], S[3]); 2928 } 2929 } 2930 contains(i, x, y) { 2931 if ((x = +x, x !== x) || (y = +y, y !== y)) return false; 2932 return this.delaunay._step(i, x, y) === i; 2933 } 2934 _cell(i) { 2935 const {circumcenters, delaunay: {inedges, halfedges, triangles}} = this; 2936 const e0 = inedges[i]; 2937 if (e0 === -1) return null; // coincident point 2938 const points = []; 2939 let e = e0; 2940 do { 2941 const t = Math.floor(e / 3); 2942 points.push(circumcenters[t * 2], circumcenters[t * 2 + 1]); 2943 e = e % 3 === 2 ? e - 2 : e + 1; 2944 if (triangles[e] !== i) break; // bad triangulation 2945 e = halfedges[e]; 2946 } while (e !== e0 && e !== -1); 2947 return points; 2948 } 2949 _clip(i) { 2950 const points = this._cell(i); 2951 if (points === null) return null; 2952 const {vectors: V} = this; 2953 const v = i * 4; 2954 return V[v] || V[v + 1] 2955 ? this._clipInfinite(i, points, V[v], V[v + 1], V[v + 2], V[v + 3]) 2956 : this._clipFinite(i, points); 2957 } 2958 _clipFinite(i, points) { 2959 const n = points.length; 2960 let P = null; 2961 let x0, y0, x1 = points[n - 2], y1 = points[n - 1]; 2962 let c0, c1 = this._regioncode(x1, y1); 2963 let e0, e1; 2964 for (let j = 0; j < n; j += 2) { 2965 x0 = x1, y0 = y1, x1 = points[j], y1 = points[j + 1]; 2966 c0 = c1, c1 = this._regioncode(x1, y1); 2967 if (c0 === 0 && c1 === 0) { 2968 e0 = e1, e1 = 0; 2969 if (P) P.push(x1, y1); 2970 else P = [x1, y1]; 2971 } else { 2972 let S, sx0, sy0, sx1, sy1; 2973 if (c0 === 0) { 2974 if ((S = this._clipSegment(x0, y0, x1, y1, c0, c1)) === null) continue; 2975 [sx0, sy0, sx1, sy1] = S; 2976 } else { 2977 if ((S = this._clipSegment(x1, y1, x0, y0, c1, c0)) === null) continue; 2978 [sx1, sy1, sx0, sy0] = S; 2979 e0 = e1, e1 = this._edgecode(sx0, sy0); 2980 if (e0 && e1) this._edge(i, e0, e1, P, P.length); 2981 if (P) P.push(sx0, sy0); 2982 else P = [sx0, sy0]; 2983 } 2984 e0 = e1, e1 = this._edgecode(sx1, sy1); 2985 if (e0 && e1) this._edge(i, e0, e1, P, P.length); 2986 if (P) P.push(sx1, sy1); 2987 else P = [sx1, sy1]; 2988 } 2989 } 2990 if (P) { 2991 e0 = e1, e1 = this._edgecode(P[0], P[1]); 2992 if (e0 && e1) this._edge(i, e0, e1, P, P.length); 2993 } else if (this.contains(i, (this.xmin + this.xmax) / 2, (this.ymin + this.ymax) / 2)) { 2994 return [this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax, this.xmin, this.ymin]; 2995 } 2996 return P; 2997 } 2998 _clipSegment(x0, y0, x1, y1, c0, c1) { 2999 while (true) { 3000 if (c0 === 0 && c1 === 0) return [x0, y0, x1, y1]; 3001 if (c0 & c1) return null; 3002 let x, y, c = c0 || c1; 3003 if (c & 0b1000) x = x0 + (x1 - x0) * (this.ymax - y0) / (y1 - y0), y = this.ymax; 3004 else if (c & 0b0100) x = x0 + (x1 - x0) * (this.ymin - y0) / (y1 - y0), y = this.ymin; 3005 else if (c & 0b0010) y = y0 + (y1 - y0) * (this.xmax - x0) / (x1 - x0), x = this.xmax; 3006 else y = y0 + (y1 - y0) * (this.xmin - x0) / (x1 - x0), x = this.xmin; 3007 if (c0) x0 = x, y0 = y, c0 = this._regioncode(x0, y0); 3008 else x1 = x, y1 = y, c1 = this._regioncode(x1, y1); 3009 } 3010 } 3011 _clipInfinite(i, points, vx0, vy0, vxn, vyn) { 3012 let P = Array.from(points), p; 3013 if (p = this._project(P[0], P[1], vx0, vy0)) P.unshift(p[0], p[1]); 3014 if (p = this._project(P[P.length - 2], P[P.length - 1], vxn, vyn)) P.push(p[
30140], p[1]); 3015 if (P = this._clipFinite(i, P)) { 3016 for (let j = 0, n = P.length, c0, c1 = this._edgecode(P[n - 2], P[n - 1]); j < n; j += 2) { 3017 c0 = c1, c1 = this._edgecode(P[j], P[j + 1]); 3018 if (c0 && c1) j = this._edge(i, c0, c1, P, j), n = P.length; 3019 } 3020 } else if (this.contains(i, (this.xmin + this.xmax) / 2, (this.ymin + this.ymax) / 2)) { 3021 P = [this.xmin, this.ymin, this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax]; 3022 } 3023 return P; 3024 } 3025 _edge(i, e0, e1, P, j) { 3026 while (e0 !== e1) { 3027 let x, y; 3028 switch (e0) { 3029 case 0b0101: e0 = 0b0100; continue; // top-left 3030 case 0b0100: e0 = 0b0110, x = this.xmax, y = this.ymin; break; // top 3031 case 0b0110: e0 = 0b0010; continue; // top-right 3032 case 0b0010: e0 = 0b1010, x = this.xmax, y = this.ymax; break; // right 3033 case 0b1010: e0 = 0b1000; continue; // bottom-right 3034 case 0b1000: e0 = 0b1001, x = this.xmin, y = this.ymax; break; // bottom 3035 case 0b1001: e0 = 0b0001; continue; // bottom-left 3036 case 0b0001: e0 = 0b0101, x = this.xmin, y = this.ymin; break; // left 3037 } 3038 if ((P[j] !== x || P[j + 1] !== y) && this.contains(i, x, y)) { 3039 P.splice(j, 0, x, y), j += 2; 3040 } 3041 } 3042 return j; 3043 } 3044 _project(x0, y0, vx, vy) { 3045 let t = Infinity, c, x, y; 3046 if (vy < 0) { // top 3047 if (y0 <= this.ymin) return null; 3048 if ((c = (this.ymin - y0) / vy) < t) y = this.ymin, x = x0 + (t = c) * vx; 3049 } else if (vy > 0) { // bottom 3050 if (y0 >= this.ymax) return null; 3051 if ((c = (this.ymax - y0) / vy) < t) y = this.ymax, x = x0 + (t = c) * vx; 3052 } 3053 if (vx > 0) { // right 3054 if (x0 >= this.xmax) return null; 3055 if ((c = (this.xmax - x0) / vx) < t) x = this.xmax, y = y0 + (t = c) * vy; 3056 } else if (vx < 0) { // left 3057 if (x0 <= this.xmin) return null; 3058 if ((c = (this.xmin - x0) / vx) < t) x = this.xmin, y = y0 + (t = c) * vy; 3059 } 3060 return [x, y]; 3061 } 3062 _edgecode(x, y) { 3063 return (x === this.xmin ? 0b0001 3064 : x === this.xmax ? 0b0010 : 0b0000) 3065 | (y === this.ymin ? 0b0100 3066 : y === this.ymax ? 0b1000 : 0b0000); 3067 } 3068 _regioncode(x, y) { 3069 return (x < this.xmin ? 0b0001 3070 : x > this.xmax ? 0b0010 : 0b0000) 3071 | (y < this.ymin ? 0b0100 3072 : y > this.ymax ? 0b1000 : 0b0000); 3073 } 3074 } 3075 3076 const tau = 2 * Math.PI; 3077 3078 function pointX(p) { 3079 return p[0]; 3080 } 3081 3082 function pointY(p) { 3083 return p[1]; 3084 } 3085 3086 class Delaunay { 3087 constructor(points) { 3088 const {halfedges, hull, triangles} = new Delaunator(points); 3089 this.points = points; 3090 this.halfedges = halfedges; 3091 this.hull = hull; 3092 this.triangles = triangles; 3093 const inedges = this.inedges = new Int32Array(points.length / 2).fill(-1); 3094 const outedges = this.outedges = new Int32Array(points.length / 2).fill(-1); 3095 3096 // Compute an index from each point to an (arbitrary) incoming halfedge. 3097 for (let e = 0, n = halfedges.length; e < n; ++e) { 3098 inedges[triangles[e % 3 === 2 ? e - 2 : e + 1]] = e; 3099 } 3100 3101 // For points on the hull, index both the incoming and outgoing halfedges. 3102 let node0, node1 = hull; 3103 do { 3104 node0 = node1, node1 = node1.next; 3105 inedges[node1.i] = node0.t; 3106 outedges[node0.i] = node1.t; 3107 } while (node1 !== hull); 3108 } 3109 voronoi(bounds) { 3110 return new Voronoi(this, bounds); 3111 } 3112 *neighbors(i) { 3113 const {inedges, outedges, halfedges, triangles} = this; 3114 const e0 = inedges[i]; 3115 if (e0 === -1) return; // coincident point 3116 let e = e0; 3117 do { 3118 yield triangles[e]; 3119 e = e % 3 === 2 ? e - 2 : e + 1; 3120 if (triangles[e] !== i) return; // bad triangulation 3121 e = halfedges[e]; 3122 if (e === -1) return yield triangles[outedges[i]]; 3123 } while (e !== e0); 3124 } 3125 find(x, y, i = 0) { 3126 if ((x = +x, x !== x) || (y = +y, y !== y)) return -1; 3127 let c; 3128 while ((c = this._step(i, x, y)) >= 0 && c !== i) i = c; 3129 return c; 3130 } 3131 _step(i, x, y) { 3132 const {inedges, points} = this; 3133 if (inedges[i] === -1) return -1; // coincident point 3134 let c = i; 3135 let dc = (x - points[i * 2]) ** 2 + (y - points[i * 2 + 1]) ** 2; 3136 for (const t of this.neighbors(i)) { 3137 const dt = (x - points[t * 2]) ** 2 + (y - points[t * 2 + 1]) ** 2; 3138 if (dt < dc) dc = dt, c = t; 3139 } 3140 return c; 3141 } 3142 render(context) { 3143 const buffer = context == null ? context = new Path : undefined; 3144 const {points, halfedges, triangles} = this; 3145 for (let i = 0, n = halfedges.length; i < n; ++i) { 3146 const j = halfedges[i]; 3147 if (j < i) continue; 3148 const ti = triangles[i] * 2; 3149 const tj = triangles[j] * 2; 3150 context.moveTo(points[ti], points[ti + 1]); 3151 context.lineTo(points[tj], points[tj + 1]); 3152 } 3153 this.renderHull(context); 3154 return buffer && buffer.value(); 3155 } 3156 renderPoints(context, r = 2) { 3157 const buffer = context == null ? context = new Path : undefined; 3158 const {points} = this; 3159 for (let i = 0, n = points.length; i < n; i += 2) { 3160 const x = points[i], y = points[i + 1]; 3161 context.moveTo(x + r, y); 3162 context.arc(x, y, r, 0, tau); 3163 } 3164 return buffer && buffer.value(); 3165 } 3166 renderHull(context) { 3167 const buffer = context == null ? context = new Path : undefined; 3168 const {hull} = this; 3169 let node = hull; 3170 context.moveTo(node.x, node.y); 3171 while (node = node.next, node !== hull) context.lineTo(node.x, node.y); 3172 context.closePath(); 3173 return buffer && buffer.value(); 3174 } 3175 hullPolygon() { 3176 const polygon = new Polygon; 3177 this.renderHull(polygon); 3178 return polygon.value(); 3179 } 3180 renderTriangle(i, context) { 3181 const buffer = context == null ? context = new Path : undefined; 3182 const {points, triangles} = this; 3183 const t0 = triangles[i *= 3] * 2; 3184 const t1 = triangles[i + 1] * 2; 3185 const t2 = triangles[i + 2] * 2; 3186 context.moveTo(points[t0], points[t0 + 1]); 3187 context.lineTo(points[t1], points[t1 + 1]); 3188 context.lineTo(points[t2], points[t2 + 1]); 3189 context.closePath(); 3190 return buffer && buffer.value(); 3191 } 3192 *trianglePolygons() { 3193 const {triangles} = this; 3194 for (let i = 0, n = triangles.length / 3; i < n; ++i) { 3195 yield this.trianglePolygon(i); 3196 } 3197 } 3198 trianglePolygon(i) { 3199 const polygon = new Polygon; 3200 this.renderTriangle(i, polygon); 3201 return polygon.value(); 3202 } 3203 } 3204 3205 Delaunay.from = function(points, fx = pointX, fy = pointY, that) { 3206 return new Delaunay("length" in points 3207 ? flatArray(points, fx, fy, that) 3208 : Float64Array.from(flatIterable(points, fx, fy, that))); 3209 }; 3210 3211 function flatArray(points, fx, fy, that) { 3212 const n = points.length; 3213 const array = new Float64Array(n * 2); 3214 for (let i = 0; i < n; ++i) { 3215 const p = points[i]; 3216 array[i * 2] = fx.call(that, p, i, points); 3217 array[i * 2 + 1] = fy.call(that, p, i, points); 3218 } 3219 return array; 3220 } 3221 3222 function* flatIterable(points, fx, fy, that) { 3223 let i = 0; 3224 for (const p of points) { 3225 yield fx.call(that, p, i, points); 3226 yield fy.call(that, p, i, points); 3227 ++i; 3228 } 3229 } 3230 3231 class Outlying { 3232 constructor(tree, upperBound) { 3233 //Clone the tree to avoid modifying it 3234 this.tree = JSON.parse(JSON.stringify(tree)); 3235 this.upperBound = upperBound; 3236 } 3237 3238 /** 3239 * Returns outlying score 3240 * @returns {number} 3241 */ 3242 score() { 3243 let tree = this.tree, 3244 totalLengths = 0, 3245 totalOutlyingLengths = 0;
3246 let upperBound = this.upperBound; 3247 if(!upperBound){ 3248 let allLengths = tree.links.map(l => l.weight), 3249 q1 = quantile(allLengths, 0.25), 3250 q3 = quantile(allLengths, 0.75), 3251 iqr = q3 - q1; 3252 // upperBound = q3+1.5*iqr; 3253 upperBound = q3+3*iqr; 3254 //Save it for displaying purpose. 3255 this.upperBound = upperBound; 3256 } 3257 tree.links.forEach(l => { 3258 totalLengths += l.weight; 3259 if (l.weight > upperBound) { 3260 totalOutlyingLengths += l.weight; 3261 l.isOutlying = true; 3262 } 3263 }); 3264 return totalOutlyingLengths / totalLengths; 3265 } 3266 3267 /** 3268 * Returns outlying links 3269 */ 3270 links() { 3271 return this.tree.links.filter(l => l.isOutlying); 3272 } 3273 3274 /** 3275 * Remove outlying links and nodes and return a new tree without outlying points/edges 3276 */ 3277 removeOutlying() { 3278 var newTree = JSON.parse(JSON.stringify(this.tree)); 3279 //Remove outlying links 3280 newTree.links = newTree.links.filter(l => !l.isOutlying); 3281 //Remove outlying nodes (nodes are not in any none-outlying links 3282 let allNodesWithLinks = []; 3283 newTree.links.forEach(l => { 3284 allNodesWithLinks.push(l.source); 3285 allNodesWithLinks.push(l.target); 3286 }); 3287 allNodesWithLinks = underscore.uniq(allNodesWithLinks, false, d => d.join(',')); 3288 //TODO: May need to reuse the existing result instead of calculating all again. 3289 //Triangulate again 3290 let delaunay = Delaunay.from(allNodesWithLinks); 3291 delaunay.points = allNodesWithLinks; 3292 delaunay.triangleCoordinates = function(){ 3293 let triangles = this.triangles; 3294 let tc = []; 3295 for (let i = 0; i < triangles.length; i += 3) { 3296 tc.push([ 3297 this.points[triangles[i]], 3298 this.points[triangles[i + 1]], 3299 this.points[triangles[i + 2]] 3300 ]); 3301 } 3302 return tc; 3303 }; 3304 let graph = createGraph(delaunay.triangleCoordinates()); 3305 newTree = mst(graph); 3306 return newTree; 3307 } 3308 3309 /** 3310 * Returns the outlying points (in form of points, not node object). 3311 * @returns {Array} 3312 */ 3313 points(){ 3314 let newTree = this.removeOutlying(); 3315 let newNodes = newTree.nodes; 3316 let oldNodes = this.tree.nodes; 3317 let ops = []; 3318 oldNodes.forEach(n=>{ 3319 //.id since we are accessing to points and the node is in form of {id: thePoint} 3320 if(!pointExists(newNodes.map(n=>n.id), n.id)){ 3321 ops.push(n.id); 3322 } 3323 }); 3324 return ops; 3325 } 3326 } 3327 3328 class Skewed { 3329 constructor(tree) { 3330 //Clone the tree to avoid modifying it 3331 this.tree = JSON.parse(JSON.stringify(tree)); 3332 } 3333 3334 /** 3335 * Returns skewed score 3336 * @returns {number} 3337 */ 3338 score() { 3339 let allLengths = this.tree.links.map(l=>l.weight), 3340 q90 = quantile(allLengths, .9), 3341 q50 = quantile(allLengths, .5), 3342 q10 = quantile(allLengths, .1); 3343 if(q90!=q10){ 3344 return (q90-q50)/(q90-q10); 3345 }else{ 3346 return 0; 3347 } 3348 3349 } 3350 } 3351 3352 class Sparse { 3353 constructor(tree) { 3354 //Clone the tree to avoid modifying it 3355 this.tree = JSON.parse(JSON.stringify(tree)); 3356 } 3357 3358 /** 3359 * Returns sparse score 3360 * @returns {number} 3361 */ 3362 score() { 3363 let allLengths = this.tree.links.map(l=>l.weight), 3364 q90 = quantile(allLengths, .9); 3365 return q90; 3366 } 3367 } 3368 3369 class Striated { 3370 constructor(tree) { 3371 //Clone the tree to avoid modifying it 3372 this.tree = JSON.parse(JSON.stringify(tree)); 3373 } 3374 3375 /** 3376 * Returns striated score 3377 * @returns {number} 3378 */ 3379 score() { 3380 //Loop through the nodes. 3381 let verticesCount = this.tree.nodes.length; 3382 let allObtuseV2CornersCount = this.getAllObtuseV2Corners().length; 3383 return allObtuseV2CornersCount / verticesCount; 3384 } 3385 3386 getAllObtuseV2Corners() { 3387 let allV2Corners = this.getAllV2Corners(); 3388 let allObtuseV2Corners = [];
3389 allV2Corners.forEach(corner => { 3390 let cs = cosine(corner[0], corner[1], corner[2]); 3391 if (cs < -.75) { 3392 allObtuseV2Corners.push(corner); 3393 } 3394 }); 3395 return allObtuseV2Corners; 3396 3397 function cosine(p1, p2, p3) { 3398 let p12 = distance(p1, p2), 3399 p13 = distance(p1, p3), 3400 p23 = distance(p2, p3); 3401 return ((Math.pow(p12, 2)) + (Math.pow(p13, 2)) - (Math.pow(p23, 2))) / (2 * p12 * p13); 3402 } 3403 } 3404 3405 /** 3406 * This function returns corners (three vertices) of vertices of degree two in the for mat of 3407 * point1, point2, point3 => point1 is the the vertex with degree two (two edges connected to it are [point1, point2] and [point1, point3] (order of the points in each edge is not important)). 3408 */ 3409 getAllV2Corners() { 3410 return getAllV2CornersFromTree(this.tree); 3411 } 3412 } 3413 3414 function area(polygon) { 3415 var i = -1, 3416 n = polygon.length, 3417 a, 3418 b = polygon[n - 1], 3419 area = 0; 3420 3421 while (++i < n) { 3422 a = b; 3423 b = polygon[i]; 3424 area += a[1] * b[0] - a[0] * b[1]; 3425 } 3426 3427 return area / 2; 3428 } 3429 3430 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of 3431 3432 function contains(polygon, point) { 3433 var n = polygon.length, 3434 p = polygon[n - 1], 3435 x = point[0], y = point[1], 3436 x0 = p[0], y0 = p[1], 3437 x1, y1, 3438 inside = false;
3439 3440 for (var i = 0; i < n; ++i) { 3441 p = polygon[i], x1 = p[0], y1 = p[1]; 3442 if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside; 3443 x0 = x1, y0 = y1; 3444 } 3445 3446 return inside; 3447 } 3448 3449 function length(polygon) { 3450 var i = -1, 3451 n = polygon.length, 3452 b = polygon[n - 1], 3453 xa, 3454 ya, 3455 xb = b[0], 3456 yb = b[1], 3457 perimeter = 0; 3458 3459 while (++i < n) { 3460 xa = xb; 3461 ya = yb; 3462 b = polygon[i]; 3463 xb = b[0]; 3464 yb = b[1]; 3465 xa -= xb; 3466 ya -= yb; 3467 perimeter += Math.sqrt(xa * xa + ya * ya); 3468 } 3469 3470 return perimeter; 3471 } 3472 3473 class ConcaveHull{ 3474 constructor(distance$$1){ 3475 if(!distance$$1){ 3476 this.calculateDistance = this.stdevDistance; 3477 } 3478 else if(typeof distance$$1 === "function"){ 3479 this.calculateDistance = distance$$1; 3480 }else if(typeof distance$$1 === "number"){ 3481 this.calculateDistance = function () {return distance$$1;}; 3482 } 3483 this.padding = 0; 3484 } 3485 coveringConcaveHull(delaunay){ 3486 let theMst = mst(createGraph(delaunay)); 3487 let allLengths = theMst.links.map(l=>l.weight); 3488 let allPoints = theMst.nodes.map(n=>n.id); 3489 //We will start with q7 3490 let qx = 0.5, 3491 increment = 0.01; 3492 let theHull = null; 3493 do{ 3494 qx = qx + increment; 3495 let longEdge = quantile(allLengths, qx); 3496 theHull = this.concaveHull(delaunay, longEdge); 3497 }while(!coverAllPoints(theHull, allPoints)); 3498 3499 return theHull; 3500 3501 function coverAllPoints(hull$$1, points) { 3502 for (let i = 0; i < points.length; i++) { 3503 let point = points[i]; 3504 let pointIsCovered = false; 3505 for (let j = 0; j < hull$$1.length; j++) { 3506 if(contains(hull$$1[j], point)){ 3507 pointIsCovered = true; 3508 break; 3509 } 3510 } 3511 if(!pointIsCovered){ 3512 return false; 3513 } 3514 } 3515 return true; 3516 } 3517 } 3518 /** 3519 * generate the concave hull. 3520 * @param delaunay is inform of coordinates of triangulated triangles (3 points per triangle) 3521 * @returns {Array} 3522 */ 3523 concaveHull(delaunay, longEdge) { 3524 if(!longEdge){ 3525 longEdge = this.calculateDistance(delaunay); 3526 } 3527 let mesh = delaunay.filter(function (d) { 3528 return distance(d[0], d[1]) < longEdge && distance(d[0], d[2]) < longEdge && distance(d[1], d[2]) < longEdge 3529 }); 3530 3531 var counts = {}, 3532 edges = {}, 3533 r, 3534 result = []; 3535 // Traverse the edges of all triangles and discard any edges that appear twice. 3536 mesh.forEach(function (triangle) { 3537 for (var i = 0; i < 3; i++) { 3538 var edge = [triangle[i], triangle[(i + 1) % 3]].sort(ascendingCoords).map(String); 3539 (edges[edge[0]] = (edges[edge[0]] || [])).push(edge[1]); 3540 (edges[edge[1]] = (edges[edge[1]] || [])).push(edge[0]); 3541 var k = edge.join(":"); 3542 if (counts[k]) delete counts[k]; 3543 else counts[k] = 1; 3544 } 3545 }); 3546 3547 while (1) { 3548 var k = null; 3549 // Pick an arbitrary starting point on a boundary. 3550 for (k in counts) break; 3551 if (k == null) break; 3552 result.push(r = k.split(":").map(function (d) { 3553 return d.split(",").map(Number); 3554 })); 3555 delete counts[k]; 3556 var q = r[1]; 3557 while (q[0] !== r[0][0] || q[1] !== r[0][1]) { 3558 var p = q, 3559 qs = edges[p.join(",")], 3560 n = qs.length; 3561 for (var i = 0; i < n; i++) { 3562 q = qs[i].split(",").map(Number); 3563 var edge = [p, q].sort(ascendingCoords).join(":"); 3564 if (counts[edge]) { 3565 delete counts[edge]; 3566 r.push(q); 3567 break; 3568 } 3569 } 3570 } 3571 } 3572 3573 if (this.padding !== 0) { 3574 result = pad(result, this.padding); 3575 } 3576 3577 return result; 3578 3579 function pad(bounds, amount) { 3580 var result = []; 3581 bounds.forEach(function (bound) { 3582 var padded = []; 3583 3584 var area$$1 = 0;
3585 bound.forEach(function (p, i) { 3586 // http://forums.esri.com/Thread.asp?c=2&f=1718&t=174277 3587 // Area = Area + (X2 - X1) * (Y2 + Y1) / 2 3588 3589 var im1 = i - 1; 3590 if (i == 0) { 3591 im1 = bound.length - 1; 3592 } 3593 var pm = bound[im1]; 3594 area$$1 += (p[0] - pm[0]) * (p[1] + pm[1]) / 2; 3595 }); 3596 var handedness = 1; 3597 if (area$$1 > 0) handedness = -1; 3598 bound.forEach(function (p, i) { 3599 // average the tangent between 3600 var im1 = i - 1; 3601 if (i == 0) { 3602 im1 = bound.length - 2; 3603 } 3604 //var tp = getTangent(p, bound[ip1]); 3605 var tm = getTangent(p, bound[im1]); 3606 //var avg = { x: (tp.x + tm.x)/2, y: (tp.y + tm.y)/2 }; 3607 //var normal = rotate2d(avg, 90); 3608 var normal = rotate2d(tm, 90 * handedness); 3609 padded.push([p[0] + normal.x * amount, p[1] + normal.y * amount]); 3610 }); 3611 result.push(padded); 3612 }); 3613 return result 3614 } 3615 3616 function getTangent(a, b) { 3617 var vector = {x: b[0] - a[0], y: b[1] - a[1]}; 3618 var magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y); 3619 vector.x /= magnitude; 3620 vector.y /= magnitude; 3621 return vector 3622 } 3623 3624 function rotate2d(vector, angle) { 3625 //rotate a vector 3626 angle *= Math.PI / 180; //convert to radians 3627 return { 3628 x: vector.x * Math.cos(angle) - vector.y * Math.sin(angle), 3629 y: vector.x * Math.sin(angle) + vector.y * Math.cos(angle) 3630 } 3631 } 3632 3633 function ascendingCoords(a, b) { 3634 return a[0] === b[0] ? b[1] - a[1] : b[0] - a[0]; 3635 } 3636 } 3637 stdevDistance(delaunay) { 3638 var sides = []; 3639 delaunay.forEach(function (d) { 3640 sides.push(distance(d[0], d[1])); 3641 sides.push(distance(d[0], d[2])); 3642 sides.push(distance(d[1], d[2])); 3643 }); 3644 3645 var dev = d3.deviation(sides); 3646 var mean$$1 = d3.mean(sides); 3647 3648 return mean$$1 + dev; 3649 } 3650 } 3651 function concaveHullArea(concaveHull) { 3652 let concaveArea = 0; 3653 concaveHull.forEach(hull$$1 => { 3654 let area$$1 = area(hull$$1); 3655 concaveArea += area$$1>0?area$$1:-area$$1; 3656 }); 3657 return concaveArea; 3658 } 3659 function concaveHullLength(concaveHull) { 3660 let concaveLength = 0; 3661 concaveHull.forEach(hull$$1 => { 3662 concaveLength += length(hull$$1); 3663 }); 3664 return concaveLength; 3665 } 3666 3667 class Convex { 3668 constructor(tree) { 3669 //Clone the tree to avoid modifying it 3670 this.tree = JSON.parse(JSON.stringify(tree)); 3671 let sites = this.tree.nodes.map(d => d.id); 3672 this.delaunay = Delaunay.from(sites); 3673 this.delaunay.points = sites; 3674 this.delaunay.triangleCoordinates = function(){ 3675 let triangles = this.triangles; 3676 let tc = []; 3677 for (let i = 0; i < triangles.length; i += 3) { 3678 tc.push([ 3679 this.points[triangles[i]], 3680 this.points[triangles[i + 1]], 3681 this.points[triangles[i + 2]] 3682 ]); 3683 } 3684 return tc; 3685 }; 3686 } 3687 /** 3688 * Returns convex score 3689 * @returns {number} 3690 */ 3691 score() { 3692 let concaveArea = concaveHullArea(this.concaveHull()); 3693 return concaveArea/area(this.convexHull()); 3694 } 3695 noOutlyingTriangleCoordinates(){ 3696 3697 return this.delaunay.triangleCoordinates(); 3698 } 3699 concaveHull() { 3700 //Use quantile as cutoff values. 3701 let allLengths = this.tree.links.map(l => l.weight), 3702 concaveHull = new ConcaveHull(quantile(allLengths, 0.999)).concaveHull(this.noOutlyingTriangleCoordinates()); 3703 // concaveHull = new ConcaveHull().coveringConcaveHull(this.noOutlyingTriangleCoordinates()); 3704 return concaveHull; 3705 } 3706 convexHull() { 3707 let hull$$1 = this.delaunay.hull; 3708 let convexHull = []; 3709 let e = hull$$1; 3710 do { 3711 convexHull.push([e.x, e.y]); 3712 e = e.next; 3713 } while (e != hull$$1); 3714 return convexHull; 3715 } 3716 } 3717 3718 class Skinny { 3719 constructor(alphaHull) { 3720 //Clone it in order to avoid modifying it. 3721 this.alphaHull = alphaHull.slice(0); 3722 } 3723 /** 3724 * Returns skinny score 3725 * @returns {number} 3726 */ 3727 score() { 3728 return 1 - Math.sqrt(4*Math.PI*concaveHullArea(this.alphaHull))/concaveHullLength(this.alphaHull); 3729 } 3730 } 3731 3732 class Stringy { 3733 constructor(tree) { 3734 //Clone the tree to avoid modifying it 3735 this.tree = JSON.parse(JSON.stringify(tree)); 3736 } 3737 3738 /** 3739 * Returns striated score 3740 * @returns {number} 3741 */ 3742 score() { 3743 //Loop through the nodes. 3744 let verticesCount = this.tree.nodes.length; 3745 let v2Count = this.getAllV2Corners().length; 3746 let v1Count = this.getAllV1s().length; 3747 return v2Count/(verticesCount-v1Count); 3748 } 3749 3750 /** 3751 * This function returns corners (three vertices) of vertices of degree two in the for mat of 3752 * point1, point2, point3 => point1 is the the vertex with degree two (two edges connected to it are [point1, point2] and [point1, point3] (order of the points in each edge is not important)). 3753 * @returns {Array} 3754 */ 3755 getAllV2Corners(){ 3756 return getAllV2CornersFromTree(this.tree); 3757 } 3758 3759 /** 3760 * This function returns 3761 * @returns {Array} 3762 */ 3763 getAllV1s(){ 3764 return getAllV1sFromTree(this.tree); 3765 } 3766 } 3767 3768 class Monotonic { 3769 constructor(points) { 3770 //Clone it in order to avoid modifying it. 3771 this.points = points.slice(0); 3772 } 3773 /** 3774 * Returns monotonic score 3775 * @returns {number} 3776 */ 3777 score() { 3778 let xArr = []; 3779 let yArr = [];
3780 this.points.forEach(p=>{ 3781 xArr.push(p[0]); 3782 yArr.push(p[1]); 3783 }); 3784 let r = computeSpearmans(xArr, yArr); 3785 return Math.pow(r, 2); 3786 3787 /**Adopted from: https://bl.ocks.org/nkullman/f65d5619843dc22e061d957249121408**/ 3788 function computeSpearmans(arrX, arrY) { 3789 // simple error handling for input arrays of nonequal lengths 3790 if (arrX.length !== arrY.length) { return null; } 3791 3792 // number of observations 3793 let n = arrX.length; 3794 3795 // rank datasets 3796 let xRanked = rankArray(arrX), 3797 yRanked = rankArray(arrY); 3798 3799 // sum of distances between ranks 3800 let dsq = 0; 3801 for (let i = 0; i < n; i++) { 3802 dsq += Math.pow(xRanked[i] - yRanked[i], 2); 3803 } 3804 3805 // compute correction for ties 3806 let xTies = countTies(arrX), 3807 yTies = countTies(arrY); 3808 let xCorrection = 0, 3809 yCorrection = 0; 3810 for (let tieLength in xTies) { 3811 xCorrection += xTies[tieLength] * tieLength * (Math.pow(tieLength, 2) - 1); 3812 } 3813 xCorrection /= 12.0; 3814 for (let tieLength in yTies) { 3815 yCorrection += yTies[tieLength] * tieLength * (Math.pow(tieLength, 2) - 1); 3816 } 3817 yCorrection /= 12.0; 3818 3819 // denominator 3820 let denominator = n * (Math.pow(n, 2) - 1) / 6.0; 3821 3822 // compute rho 3823 let rho = denominator - dsq - xCorrection - yCorrection; 3824 rho /= Math.sqrt((denominator - 2 * xCorrection) * (denominator - 2 * yCorrection)); 3825 3826 return rho; 3827 } 3828 /** Computes the rank array for arr, where each entry in arr is 3829 * assigned a value 1 thru n, where n is arr.length. 3830 * 3831 * Tied entries in arr are each given the average rank of the ties. 3832 * Lower ranks are not increased 3833 */ 3834 function rankArray(arr) { 3835 3836 // ranking without averaging 3837 let sorted = arr.slice().sort(function (a, b) { return b - a }); 3838 let ranks = arr.slice().map(function (v) { return sorted.indexOf(v) + 1 }); 3839 3840 // counts of each rank 3841 let counts = {}; 3842 ranks.forEach(function (x) { counts[x] = (counts[x] || 0) + 1; }); 3843 3844 // average duplicates 3845 ranks = ranks.map(function (x) { return x + 0.5 * ((counts[x] || 0) - 1); }); 3846 3847 return ranks; 3848 } 3849 /** Counts the number of ties in arr, and returns 3850 * an object with 3851 * a key for each tie length (an entry n for each n-way tie) and 3852 * a value corresponding to the number of key-way (n-way) ties 3853 */ 3854 function countTies(arr) { 3855 let ties = {}, 3856 arrSorted = arr.slice().sort(), 3857 currValue = arrSorted[0], 3858 tieLength = 1; 3859 3860 for (let i = 1; i < arrSorted.length; i++) { 3861 if (arrSorted[i] === currValue) { 3862 tieLength++; 3863 } else { 3864 if (tieLength > 1) { 3865 if (ties[tieLength] === undefined) ties[tieLength] = 0; 3866 ties[tieLength]++; 3867 } 3868 currValue = arrSorted[i]; 3869 tieLength = 1; 3870 } 3871 } 3872 if (tieLength > 1) { 3873 if (ties[tieLength] === undefined) ties[tieLength] = 0; 3874 ties[tieLength]++; 3875 } 3876 return ties; 3877 } 3878 } 3879 } 3880 3881 class Normalizer { 3882 constructor(points) { 3883 this.points = points.slice(0); 3884 let [xArr, yArr] = [this.xArr, this.yArr] = underscore.unzip(this.points), 3885 maxX = this.maxX = underscore.max(xArr), 3886 minX = this.minX = underscore.min(xArr), 3887 maxY = this.maxY = underscore.max(yArr), 3888 minY = this.minY = underscore.min(yArr), 3889 rangeX = this.rangeX = (maxX!=minX) ? maxX - minX: 1, 3890 rangeY = this.rangeY = (maxY!=minY) ? maxY - minY: 1, 3891 normalizedXArr = this.normalizedXArr = xArr.map(x => (x - minX) / rangeX), 3892 normalizedYArr = this.normalizedYArr = yArr.map(y => (y - minY) / rangeY); 3893 this.normalizedPoints = underscore.zip(normalizedXArr, normalizedYArr); 3894 //Add one step to pass the data over if there is. 3895 let length = this.points.length; 3896 for (let i = 0; i < length; i++) { 3897 this.normalizedPoints[i].data = this.points[i].data; 3898 } 3899 } 3900 3901 /** 3902 * Input a set of points in this scale range [0, 1] and will be scaled back to 3903 * - Original scale ([minX, maxX], [minY, maxY]) 3904 * @param points 3905 */ 3906 scaleBackPoints(points) { 3907 return points.map(point=>{ 3908 return this.scaleBackPoint(point); 3909 }); 3910 } 3911 /** 3912 * Input a single point in this scale range [0, 1] and will be scaled back to 3913 * - Original scale ([minX, maxX], [minY, maxY]) 3914 * @param points 3915 */ 3916 scaleBackPoint(point) { 3917 let xs = point[0], 3918 x = this.rangeX * xs + this.minX, 3919 ys = point[1], 3920 y = this.rangeY * ys + this.minY; 3921 return [x, y]; 3922 } 3923 } 3924 3925 class LeaderBinner{ 3926 constructor(points, radius){ 3927 //TODO: Should check if there are more than 3 unique values here or even after the binning. 3928 //TODO: May need to clone the points to avoid modifying it, but we don't do to reserve other data or to make the process faster 3929 // //Clone these to avoid modifying them 3930 // this.points = points.map(p=>p.slice(0)); 3931 this.points = points;
3932 this.radius = radius; 3933 } 3934 get leaders(){ 3935 let self = this; 3936 let theLeaders = []; 3937 //find all the leaders 3938 this.points.forEach(point=>{ 3939 let leader = closestLeader(theLeaders, point); 3940 if(!leader){ 3941 let newLeader = []; 3942 newLeader.x = point[0]; 3943 newLeader.y = point[1]; 3944 theLeaders.push(newLeader); 3945 } 3946 }); 3947 //now do this again to set the closest leader. 3948 this.points.forEach(point=>{ 3949 let leader = closestLeader(theLeaders, point); 3950 leader.push(point); 3951 }); 3952 return theLeaders; 3953 function closestLeader(leaders, point){ 3954 let length = leaders.length; 3955 let minDistance = 2;//select 2 since normalized distance can't be greater than 2. 3956 let theLeader = null; 3957 for (let i = 0; i < length; ++i) { 3958 let l = leaders[i]; 3959 let d = distance([l.x, l.y], point); 3960 if(d< self.radius){ 3961 if(d<minDistance){ 3962 minDistance = d; 3963 theLeader = l; 3964 } 3965 } 3966 } 3967 return theLeader; 3968 3969 // let distances = leaders.map(l=>distance([l.x, l.y], point)); 3970 // //Filter the distance to be <= the radius 3971 // let copiedDistances = distances.filter(d=>d<self.radius); 3972 // if(copiedDistances.length===0){ 3973 // return null; 3974 // } 3975 // let theDistance = _.min(distances); 3976 // let theLeader = leaders[distances.indexOf(theDistance)]; 3977 // return theLeader; 3978 } 3979 } 3980 } 3981 3982 //Inspired from https://github.com/d3/d3-hexbin/blob/master/src/hexbin.js 3983 function Binner(){ 3984 let thirdPi = Math.PI/3, 3985 angles = [0, thirdPi, 2*thirdPi, 3*thirdPi, 4*thirdPi, 5*thirdPi]; 3986 3987 function pointX(d){ 3988 return d[0]; 3989 } 3990 function pointY(d){ 3991 return d[1]; 3992 } 3993 3994 var x0 = 0, 3995 y0 = 0, 3996 x1 = 1, 3997 y1 = 1, 3998 x = pointX, 3999 y = pointY, 4000 r, 4001 dx, 4002 dy; 4003 4004 function hexbin(points){ 4005 var binsById = {}, bins = [], i, n = points.length; 4006 4007 for (i = 0; i < n; ++i) { 4008 if (isNaN(px = +x.call(null, point = points[i], i, points)) 4009 || isNaN(py = +y.call(null, point, i, points))) continue; 4010 4011 var point, 4012 px, 4013 py, 4014 pj = Math.round(py = py / dy), 4015 pi = Math.round(px = px / dx - (pj & 1) / 2), 4016 py1 = py - pj; 4017 4018 if (Math.abs(py1) * 3 > 1) { 4019 var px1 = px - pi, 4020 pi2 = pi + (px < pi ? -1 : 1) / 2, 4021 pj2 = pj + (py < pj ? -1 : 1), 4022 px2 = px - pi2, 4023 py2 = py - pj2; 4024 if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) pi = pi2 + (pj & 1 ? 1 : -1) / 2, pj = pj2; 4025 } 4026 4027 var id = pi + "-" + pj, bin = binsById[id]; 4028 if (bin) bin.push(point); 4029 else { 4030 bins.push(bin = binsById[id] = [point]); 4031 bin.x = (pi + (pj & 1) / 2) * dx; 4032 bin.y = pj * dy; 4033 //TODO: fix this since there are results with bin.x, bin.y > 1 => we may need to do the rounding. 4034 // if(bin.x>1){bin.x = 1;} 4035 // if(bin.y>1){bin.y =1;} 4036 } 4037 } 4038 4039 return bins; 4040 } 4041 function hexagon(radius){ 4042 var x0 = 0, y0 = 0; 4043 return angles.map(function(angle) { 4044 var x1 = Math.sin(angle) * radius, 4045 y1 = -Math.cos(angle) * radius, 4046 dx = x1 - x0, 4047 dy = y1 - y0; 4048 x0 = x1, y0 = y1; 4049 4050 return [dx, dy]; 4051 }); 4052 } 4053 4054 //Export it 4055 this.hexbin = hexbin; 4056 this.hexagon = function(radius){ 4057 return "m" + hexagon(radius == null ? r : +radius).join("l") + "z"; 4058 }; 4059 this.centers = function(){ 4060 var centers = [], 4061 j = Math.round(y0 / dy), 4062 i = Math.round(x0 / dx); 4063 for (var y = j * dy; y < y1 + r; y += dy, ++j) { 4064 for (var x = i * dx + (j & 1) * dx / 2; x < x1 + dx / 2; x += dx) { 4065 centers.push([x, y]); 4066 } 4067 } 4068 return centers; 4069 }; 4070 this.mesh = function(){ 4071 var fragment = hexagon(r).slice(0, 4).join("l"); 4072 return hexbin.centers().map(function(p) { return "M" + p + "m" + fragment; }).join(""); 4073 }; 4074 //Setters/getters 4075 this.x = function(_){ 4076 return arguments.length ? (x = _, this) : x; 4077 }; 4078 this.y = function(_){ 4079 return arguments.length ? (y = _, this) : y; 4080 }; 4081 this.radius = function(_){ 4082 return arguments.length ? (r= +_, dx = r * 2 * Math.sin(thirdPi), dy = r*1.5, this): r; 4083 }; 4084 this.size = function(_) { 4085 return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], this) : [x1 - x0, y1 - y0]; 4086 }; 4087 this.extent = function(_){ 4088 return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], this): [[x0, y0], [x1, y1]]; 4089 }; 4090 4091 return this.radius(1); 4092 } 4093 4094 (function(window){ 4095 /** 4096 * initialize a scagnostic object 4097 * @param inputPoints {*[][]} set of points from the scatter plot 4098 * @returns {*[][]} 4099 */ 4100 window.scagnostics = function(inputPoints, binType,startBinGridSize, isNormalized, isBinned, outlyingUpperBound, minBins, maxBins) { 4101 //Clone it to avoid modifying it. 4102 let points = inputPoints.slice(0); 4103 let normalizedPoints = points; 4104 /******This section is about normalizing the data******/ 4105 if(!isNormalized){ 4106 let normalizer = new Normalizer(points); 4107 normalizedPoints = normalizer.normalizedPoints; 4108 } 4109 outputValue("normalizedPoints", normalizedPoints); 4110 /******This section is about finding number of bins and binners******/ 4111 let sites = null; 4112 let bins = null; 4113 let binner = null; 4114 let binSize = null; 4115 let binRadius = 0; 4116 4117 if(!isBinned){//Only do the binning if needed. 4118 if(!startBinGridSize){ 4119 startBinGridSize = 40; 4120 } 4121 bins = []; 4122 //Default number of bins 4123 let minNumOfBins = 50; 4124 let maxNumOfBins = 250; 4125 if(minBins){ 4126 minNumOfBins =minBins; 4127 } 4128 if(maxBins){ 4129 maxNumOfBins = maxBins; 4130 } 4131 //Don't do the binning if the unique set of values are less than 50. Just return the unique set. 4132 let uniqueKeys = underscore.uniq(normalizedPoints.map(p=>p.join(','))); 4133 let groups = underscore.groupBy(normalizedPoints, p=>p.join(',')); 4134 if(uniqueKeys.length<minNumOfBins){ 4135 uniqueKeys.forEach(key=>{ 4136 let bin = groups[key]; 4137 //Take the coordinate of the first point in the group to be the bin leader (they should have the same points actually=> so just take the first one. 4138 bin.x = bin[0][0]; 4139 bin.y = bin[0][1]; 4140 bin.binRadius = 0; 4141 bins.push(bin); 4142 }); 4143 }else{ 4144 do{
4145 //Start with 40x40 bins, and divided by 2 every time there are more than maxNumberofBins none empty cells, increase 5 (+5) if less than minNumberOfBins 4146 if(binSize===null){ 4147 binSize = startBinGridSize; 4148 }else if(bins.length>maxNumOfBins){ 4149 binSize = binSize/2; 4150 }else if(bins.length<minNumOfBins){ 4151 binSize = binSize + 5; 4152 } 4153 if(binType==="hexagon"){ 4154 // This section uses hexagon binning 4155 let shortDiagonal = 1/binSize; 4156 binRadius = Math.sqrt(3)*shortDiagonal/2; 4157 binner = new Binner().radius(binRadius).extent([[0, 0], [1, 1]]);//extent from [0, 0] to [1, 1] since we already normalized data. 4158 bins = binner.hexbin(normalizedPoints); 4159 }else if(!binType || binType==="leader"){ 4160 // This section uses leader binner 4161 binRadius = 1/(binSize*2); 4162 binner = new LeaderBinner(normalizedPoints, binRadius); 4163 bins = binner.leaders; 4164 } 4165 }while(bins.length > maxNumOfBins || bins.length < minNumOfBins); 4166 } 4167 sites = bins.map(d => [d.x, d.y]); //=>sites are the set of centers of all bins 4168 /******This section is about the binning and binning results******/ 4169 outputValue("binner", binner); 4170 outputValue("bins", bins); 4171 outputValue("binSize", binSize); 4172 outputValue("binRadius", binRadius); 4173 }else{ 4174 sites = normalizedPoints; 4175 } 4176 4177 outputValue("binnedSites", sites); 4178 4179 /******This section is about the triangulating and triangulating results******/ 4180 //Triangulation calculation 4181 let delaunay = Delaunay.from(sites); 4182 //TODO: There are many placed we need the triangleCoordinates function => we should build it as a prototype instead of copy/paste this function in many different places. 4183 delaunay.points = sites; 4184 let triangles = delaunay.triangles; 4185 delaunay.triangleCoordinates = function(){ 4186 let triangles = this.triangles; 4187 let tc = []; 4188 for (let i = 0; i < triangles.length; i += 3) { 4189 tc.push([ 4190 this.points[triangles[i]], 4191 this.points[triangles[i + 1]], 4192 this.points[triangles[i + 2]] 4193 ]); 4194 } 4195 return tc; 4196 }; 4197 let triangleCoordinates = delaunay.triangleCoordinates(); 4198 //Assigning output values 4199 outputValue("delaunay", delaunay); 4200 outputValue("triangles", triangles); 4201 outputValue("triangleCoordinates", triangleCoordinates); 4202 4203 /******This section is about the spanning tree and spanning tree results******/ 4204 //Spanning tree calculation 4205 let graph = createGraph(triangleCoordinates); 4206 let mstree = mst(graph); 4207 //Assigning the output values 4208 outputValue("graph", graph); 4209 outputValue("mst", mstree); 4210 4211 /******This section is about the outlying score and outlying score results******/ 4212 let outlying = new Outlying(mstree); 4213 let outlyingScore = outlying.score(); 4214 outlyingUpperBound = outlying.upperBound; 4215 let outlyingLinks = outlying.links(); 4216 let outlyingPoints = outlying.points(); 4217 let noOutlyingTree = outlying.removeOutlying(); 4218 outputValue("outlyingScore", outlyingScore); 4219 outputValue("outlyingUpperBound", outlyingUpperBound); 4220 outputValue("outlyingLinks", outlyingLinks); 4221 outputValue("outlyingPoints", outlyingPoints); 4222 outputValue("noOutlyingTree", noOutlyingTree); 4223 4224 /******This section is about the skewed score and skewed score results******/ 4225 let skewed = new Skewed(noOutlyingTree); 4226 outputValue("skewedScore", skewed.score()); 4227 4228 /******This section is about the sparse score and sparse score results******/ 4229 let sparse = new Sparse(noOutlyingTree); 4230 outputValue("sparseScore", sparse.score()); 4231 4232 /******This section is about the clumpy score and clumpy score results******/ 4233 let clumpy = new Clumpy(noOutlyingTree); 4234 outputValue("clumpy", clumpy); 4235 outputValue("clumpyScore", clumpy.score()); 4236 4237 /******This section is about the striated score and striated score results******/ 4238 let striated = new Striated(noOutlyingTree); 4239 let v2Corners = striated.getAllV2Corners(); 4240 let obtuseV2Corners = striated.getAllObtuseV2Corners(); 4241 outputValue("striatedScore", striated.score()); 4242 outputValue("v2Corners", v2Corners); 4243 outputValue("obtuseV2Corners", obtuseV2Corners); 4244 4245 /******This section is about the convex hull and convex hull results******/ 4246 let convex = new Convex(noOutlyingTree); 4247 let convexHull = convex.convexHull(); 4248 let noOutlyingTriangleCoordinates = convex.noOutlyingTriangleCoordinates(); 4249 outputValue("noOutlyingTriangleCoordinates", noOutlyingTriangleCoordinates); 4250 outputValue("convexHull", convexHull); 4251 4252 /******This section is about the concave hull and concave hull results******/ 4253 let concaveHull = convex.concaveHull(); 4254 outputValue("concaveHull", concaveHull); 4255 4256 /******This section is about the convex score and convex score results******/ 4257 let convexScore = convex.score(); 4258 outputValue("convexScore", convexScore); 4259 4260 /******This section is about the skinny score and skinny score results******/ 4261 let skinny = new Skinny(concaveHull); 4262 let skinnyScore = skinny.score(); 4263 outputValue("skinnyScore", skinnyScore); 4264 4265 /******This section is about the stringy score and stringy score results******/ 4266 let stringy = new Stringy(noOutlyingTree); 4267 let v1s = stringy.getAllV1s(); 4268 let stringyScore = stringy.score(); 4269 outputValue("v1s", v1s); 4270 outputValue("stringyScore", stringyScore); 4271 4272 /******This section is about the monotonic score and monotonic score results******/ 4273 let monotonic = new Monotonic(noOutlyingTree.nodes.map(n=>n.id)); 4274 let monotonicScore = monotonic.score(); 4275 outputValue("monotonicScore", monotonicScore); 4276 4277 return window.scagnostics; 4278 function outputValue(name, value){ 4279 window.scagnostics[name] = value; 4280 } 4281 }; 4282 4283 })(window); 4284 4285}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.