vega-functions 5.11.0 → 5.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vega-functions might be problematic. Click here for more details.

@@ -1,11 +1,11 @@
1
- import { truthy, error, hasOwnProperty, isFunction, isString, stringValue, extend, isArray, isObject, peek, identity, array as array$1, isBoolean, isDate, isNumber, isRegExp, toBoolean, toDate, toNumber, toString, flush, lerp, pad, span, inrange, truncate, quarter, utcquarter, extent, clampRange, panLinear, panLog, panPow, panSymlog, zoomLinear, zoomLog, zoomPow, zoomSymlog } from 'vega-util';
2
- import { Literal, constants, codegen, functions, parse, CallExpression } from 'vega-expression';
1
+ import { truthy, error, hasOwnProperty, isFunction, isString, stringValue, extend, isArray, isObject, field, peek, identity, array as array$1, isBoolean, isDate, isNumber, isRegExp, toBoolean, toDate, toNumber, toString, flush, lerp, pad, span, inrange, truncate, quarter, utcquarter, extent, clampRange, panLinear, panLog, panPow, panSymlog, zoomLinear, zoomLog, zoomPow, zoomSymlog } from 'vega-util';
2
+ import { Literal, codegenExpression, constants, functions, parseExpression, CallExpression } from 'vega-expression';
3
3
  import { geoBounds as geoBounds$1, geoCentroid as geoCentroid$1, geoArea as geoArea$1 } from 'd3-geo';
4
4
  import { rgb, lab, hcl, hsl } from 'd3-color';
5
5
  import { isTuple } from 'vega-dataflow';
6
6
  import { bandSpace, scale as scale$1, scaleFraction } from 'vega-scale';
7
7
  import { Gradient, pathRender, pathParse, Bounds, intersect as intersect$1 } from 'vega-scenegraph';
8
- import { selectionVisitor, selectionTest, selectionResolve } from 'vega-selections';
8
+ import { selectionVisitor, selectionTest, selectionIdTest, selectionResolve, selectionTuples } from 'vega-selections';
9
9
  import { random, cumulativeNormal, cumulativeLogNormal, cumulativeUniform, densityNormal, densityLogNormal, densityUniform, quantileNormal, quantileLogNormal, quantileUniform, sampleNormal, sampleLogNormal, sampleUniform } from 'vega-statistics';
10
10
  import { utcOffset, utcSequence, timeOffset, timeSequence, timeUnitSpecifier, week, utcweek, dayofyear, utcdayofyear } from 'vega-time';
11
11
  import { range as range$1 } from 'd3-array';
@@ -317,6 +317,12 @@ function pinchAngle(event) {
317
317
  return Math.atan2(t[0].clientY - t[1].clientY, t[0].clientX - t[1].clientX);
318
318
  }
319
319
 
320
+ const accessors = {};
321
+ function pluck (data, name) {
322
+ const accessor = accessors[name] || (accessors[name] = field(name));
323
+ return isArray(data) ? data.map(accessor) : accessor(data);
324
+ }
325
+
320
326
  function array(seq) {
321
327
  return isArray(seq) || ArrayBuffer.isView(seq) ? seq : null;
322
328
  }
@@ -325,16 +331,32 @@ function sequence(seq) {
325
331
  return array(seq) || (isString(seq) ? seq : null);
326
332
  }
327
333
 
328
- function join(seq, ...args) {
334
+ function join(seq) {
335
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
336
+ args[_key - 1] = arguments[_key];
337
+ }
338
+
329
339
  return array(seq).join(...args);
330
340
  }
331
- function indexof(seq, ...args) {
341
+ function indexof(seq) {
342
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
343
+ args[_key2 - 1] = arguments[_key2];
344
+ }
345
+
332
346
  return sequence(seq).indexOf(...args);
333
347
  }
334
- function lastindexof(seq, ...args) {
348
+ function lastindexof(seq) {
349
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
350
+ args[_key3 - 1] = arguments[_key3];
351
+ }
352
+
335
353
  return sequence(seq).lastIndexOf(...args);
336
354
  }
337
- function slice(seq, ...args) {
355
+ function slice(seq) {
356
+ for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
357
+ args[_key4 - 1] = arguments[_key4];
358
+ }
359
+
338
360
  return sequence(seq).slice(...args);
339
361
  }
340
362
  function replace(str, pattern, repl) {
@@ -468,6 +490,98 @@ function filter(opt) {
468
490
  return p;
469
491
  }
470
492
 
493
+ /**
494
+ * Appends a new point to the lasso
495
+ *
496
+ * @param {*} lasso the lasso in pixel space
497
+ * @param {*} x the x coordinate in pixel space
498
+ * @param {*} y the y coordinate in pixel space
499
+ * @param {*} minDist the minimum distance, in pixels, that thenew point needs to be apart from the last point
500
+ * @returns a new array containing the lasso with the new point
501
+ */
502
+
503
+ function lassoAppend(lasso, x, y) {
504
+ let minDist = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 5;
505
+ const last = lasso[lasso.length - 1]; // Add point to lasso if distance to last point exceed minDist or its the first point
506
+
507
+ if (last === undefined || Math.sqrt((last[0] - x) ** 2 + (last[1] - y) ** 2) > minDist) {
508
+ lasso.push([x, y]);
509
+ return [...lasso];
510
+ }
511
+
512
+ return lasso;
513
+ }
514
+ /**
515
+ * Generates a svg path command which draws a lasso
516
+ *
517
+ * @param {*} lasso the lasso in pixel space in the form [[x,y], [x,y], ...]
518
+ * @returns the svg path command that draws the lasso
519
+ */
520
+
521
+ function lassoPath(lasso) {
522
+ return (lasso !== null && lasso !== void 0 ? lasso : []).reduce((svg, _ref, i) => {
523
+ let [x, y] = _ref;
524
+ return svg += i == 0 ? "M ".concat(x, ",").concat(y, " ") : i === lasso.length - 1 ? ' Z' : "L ".concat(x, ",").concat(y, " ");
525
+ }, '');
526
+ }
527
+ /**
528
+ * Inverts the lasso from pixel space to an array of vega scenegraph tuples
529
+ *
530
+ * @param {*} data the dataset
531
+ * @param {*} pixelLasso the lasso in pixel space, [[x,y], [x,y], ...]
532
+ * @param {*} unit the unit where the lasso is defined
533
+ *
534
+ * @returns an array of vega scenegraph tuples
535
+ */
536
+
537
+ function intersectLasso(markname, pixelLasso, unit) {
538
+ const {
539
+ x,
540
+ y,
541
+ mark
542
+ } = unit;
543
+ const bb = new Bounds().set(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); // Get bounding box around lasso
544
+
545
+ for (const [px, py] of pixelLasso) {
546
+ if (px < bb.x1) bb.x1 = px;
547
+ if (px > bb.x2) bb.x2 = px;
548
+ if (py < bb.y1) bb.y1 = py;
549
+ if (py > bb.y2) bb.y2 = py;
550
+ } // Translate bb against unit coordinates
551
+
552
+
553
+ bb.translate(x, y);
554
+ const intersection = intersect([[bb.x1, bb.y1], [bb.x2, bb.y2]], markname, mark); // Check every point against the lasso
555
+
556
+ return intersection.filter(tuple => pointInPolygon(tuple.x, tuple.y, pixelLasso));
557
+ }
558
+ /**
559
+ * Performs a test if a point is inside a polygon based on the idea from
560
+ * https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
561
+ *
562
+ * This method will not need the same start/end point since it wraps around the edges of the array
563
+ *
564
+ * @param {*} test a point to test against
565
+ * @param {*} polygon a polygon in the form [[x,y], [x,y], ...]
566
+ * @returns true if the point lies inside the polygon, false otherwise
567
+ */
568
+
569
+ function pointInPolygon(testx, testy, polygon) {
570
+ let intersections = 0;
571
+
572
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
573
+ const [prevX, prevY] = polygon[j];
574
+ const [x, y] = polygon[i]; // count intersections
575
+
576
+ if (y > testy != prevY > testy && testx < (prevX - x) * (testy - y) / (prevY - y) + x) {
577
+ intersections++;
578
+ }
579
+ } // point is in polygon if intersection count is odd
580
+
581
+
582
+ return intersections & 1;
583
+ }
584
+
471
585
  const functionContext = {
472
586
  random() {
473
587
  return random();
@@ -505,7 +619,12 @@ const functionContext = {
505
619
  },
506
620
 
507
621
  toBoolean,
508
- toDate,
622
+
623
+ toDate(_) {
624
+ return toDate(_);
625
+ },
626
+
627
+ // suppress extra arguments
509
628
  toNumber,
510
629
  toString,
511
630
  indexof,
@@ -519,6 +638,7 @@ const functionContext = {
519
638
  merge,
520
639
  pad,
521
640
  peek,
641
+ pluck,
522
642
  span,
523
643
  inrange,
524
644
  truncate,
@@ -552,7 +672,12 @@ const functionContext = {
552
672
  warn,
553
673
  info,
554
674
  debug,
555
- extent,
675
+
676
+ extent(_) {
677
+ return extent(_);
678
+ },
679
+
680
+ // suppress extra arguments
556
681
  inScope,
557
682
  intersect,
558
683
  clampRange,
@@ -573,7 +698,10 @@ const functionContext = {
573
698
  zoomPow,
574
699
  zoomSymlog,
575
700
  encode,
576
- modify
701
+ modify,
702
+ lassoAppend,
703
+ lassoPath,
704
+ intersectLasso
577
705
  };
578
706
  const eventFunctions = ['view', 'item', 'group', 'xy', 'x', 'y'],
579
707
  // event functions
@@ -594,7 +722,7 @@ const codegenParams = {
594
722
  visitors: astVisitors
595
723
  }; // export code generator
596
724
 
597
- const codeGenerator = codegen(codegenParams); // Build expression function registry
725
+ const codeGenerator = codegenExpression(codegenParams); // Build expression function registry
598
726
 
599
727
  function buildFunctions(codegen) {
600
728
  const fn = functions(codegen);
@@ -641,7 +769,9 @@ expressionFunction('treePath', treePath, dataVisitor);
641
769
  expressionFunction('treeAncestors', treeAncestors, dataVisitor); // register Vega-Lite selection functions
642
770
 
643
771
  expressionFunction('vlSelectionTest', selectionTest, selectionVisitor);
772
+ expressionFunction('vlSelectionIdTest', selectionIdTest, selectionVisitor);
644
773
  expressionFunction('vlSelectionResolve', selectionResolve, selectionVisitor);
774
+ expressionFunction('vlSelectionTuples', selectionTuples);
645
775
 
646
776
  function parser (expr, scope) {
647
777
  const params = {}; // parse the expression to an abstract syntax tree (ast)
@@ -650,7 +780,7 @@ function parser (expr, scope) {
650
780
 
651
781
  try {
652
782
  expr = isString(expr) ? expr : stringValue(expr) + '';
653
- ast = parse(expr);
783
+ ast = parseExpression(expr);
654
784
  } catch (err) {
655
785
  error('Expression parse error: ' + expr);
656
786
  } // analyze ast function calls for dependencies
@@ -684,4 +814,4 @@ function parser (expr, scope) {
684
814
  };
685
815
  }
686
816
 
687
- export { DataPrefix, IndexPrefix, ScalePrefix, SignalPrefix, bandspace, bandwidth, codeGenerator, codegenParams, containerSize, contrast, copy, data, dataVisitor, dayAbbrevFormat, dayFormat, debug, domain, encode, expressionFunction, format, functionContext, geoArea, geoBounds, geoCentroid, geoShape, inScope, indata, indataVisitor, indexof, info, invert, join, lastindexof, luminance, merge, modify, monthAbbrevFormat, monthFormat, parser as parseExpression, pathShape, pinchAngle, pinchDistance, range, replace, reverse, scale, scaleGradient, scaleVisitor, screen, setdata, slice, timeFormat, timeParse, treeAncestors, treePath, utcFormat, utcParse, warn, windowSize };
817
+ export { DataPrefix, IndexPrefix, ScalePrefix, SignalPrefix, bandspace, bandwidth, codeGenerator, codegenParams, containerSize, contrast, copy, data, dataVisitor, dayAbbrevFormat, dayFormat, debug, domain, encode, expressionFunction, format, functionContext, geoArea, geoBounds, geoCentroid, geoShape, inScope, indata, indataVisitor, indexof, info, invert, join, lastindexof, luminance, merge, modify, monthAbbrevFormat, monthFormat, parser as parseExpression, pathShape, pinchAngle, pinchDistance, pluck, range, replace, reverse, scale, scaleGradient, scaleVisitor, screen, setdata, slice, timeFormat, timeParse, treeAncestors, treePath, utcFormat, utcParse, warn, windowSize };
package/index.js CHANGED
@@ -54,6 +54,10 @@ export {
54
54
  pinchAngle
55
55
  } from './src/functions/pinch';
56
56
 
57
+ export {
58
+ default as pluck
59
+ } from './src/functions/pluck';
60
+
57
61
  export {
58
62
  indexof,
59
63
  join,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vega-functions",
3
- "version": "5.11.0",
3
+ "version": "5.13.0",
4
4
  "description": "Custom functions for the Vega expression language.",
5
5
  "keywords": [
6
6
  "vega",
@@ -21,20 +21,20 @@
21
21
  "prepublishOnly": "yarn test && yarn build"
22
22
  },
23
23
  "dependencies": {
24
- "d3-array": "^2.7.1",
25
- "d3-color": "^2.0.0",
26
- "d3-geo": "^2.0.1",
24
+ "d3-array": "^3.1.1",
25
+ "d3-color": "^3.0.1",
26
+ "d3-geo": "^3.0.1",
27
27
  "vega-dataflow": "^5.7.3",
28
- "vega-expression": "^4.0.1",
29
- "vega-scale": "^7.1.1",
30
- "vega-scenegraph": "^4.9.2",
31
- "vega-selections": "^5.2.0",
28
+ "vega-expression": "^5.0.0",
29
+ "vega-scale": "^7.2.0",
30
+ "vega-scenegraph": "^4.9.3",
31
+ "vega-selections": "^5.3.1",
32
32
  "vega-statistics": "^1.7.9",
33
- "vega-time": "^2.0.4",
33
+ "vega-time": "^2.1.0",
34
34
  "vega-util": "^1.16.0"
35
35
  },
36
36
  "devDependencies": {
37
- "vega-format": "^1.0.4"
37
+ "vega-format": "^1.1.0"
38
38
  },
39
- "gitHead": "0f8859294bb83eb637da209d952faf65eebcf908"
39
+ "gitHead": "9a3faca4395cade9ecdfde90af98f1c53e9916b2"
40
40
  }
package/src/codegen.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import {
2
- codegen,
2
+ codegenExpression,
3
3
  constants,
4
4
  functions
5
5
  } from 'vega-expression';
@@ -9,8 +9,10 @@ import {
9
9
  } from 'vega-dataflow';
10
10
 
11
11
  import {
12
+ selectionIdTest,
12
13
  selectionResolve,
13
14
  selectionTest,
15
+ selectionTuples,
14
16
  selectionVisitor
15
17
  } from 'vega-selections';
16
18
 
@@ -138,6 +140,8 @@ import {
138
140
  pinchDistance
139
141
  } from './functions/pinch';
140
142
 
143
+ import pluck from './functions/pluck';
144
+
141
145
  import {
142
146
  indexof,
143
147
  join,
@@ -147,6 +151,12 @@ import {
147
151
  slice
148
152
  } from './functions/sequence';
149
153
 
154
+ import {
155
+ intersectLasso,
156
+ lassoAppend,
157
+ lassoPath
158
+ } from './functions/lasso';
159
+
150
160
  import {
151
161
  bandspace,
152
162
  bandwidth,
@@ -215,7 +225,7 @@ export const functionContext = {
215
225
  isTuple,
216
226
  isValid(_) { return _ != null && _ === _; },
217
227
  toBoolean,
218
- toDate,
228
+ toDate(_) { return toDate(_); }, // suppress extra arguments
219
229
  toNumber,
220
230
  toString,
221
231
  indexof,
@@ -229,6 +239,7 @@ export const functionContext = {
229
239
  merge,
230
240
  pad,
231
241
  peek,
242
+ pluck,
232
243
  span,
233
244
  inrange,
234
245
  truncate,
@@ -262,7 +273,7 @@ export const functionContext = {
262
273
  warn,
263
274
  info,
264
275
  debug,
265
- extent,
276
+ extent(_) { return extent(_); }, // suppress extra arguments
266
277
  inScope,
267
278
  intersect,
268
279
  clampRange,
@@ -283,7 +294,10 @@ export const functionContext = {
283
294
  zoomPow,
284
295
  zoomSymlog,
285
296
  encode,
286
- modify
297
+ modify,
298
+ lassoAppend,
299
+ lassoPath,
300
+ intersectLasso
287
301
  };
288
302
 
289
303
  const eventFunctions = ['view', 'item', 'group', 'xy', 'x', 'y'], // event functions
@@ -303,7 +317,7 @@ export const codegenParams = {
303
317
  };
304
318
 
305
319
  // export code generator
306
- export const codeGenerator = codegen(codegenParams);
320
+ export const codeGenerator = codegenExpression(codegenParams);
307
321
 
308
322
  // Build expression function registry
309
323
  function buildFunctions(codegen) {
@@ -351,4 +365,6 @@ expressionFunction('treeAncestors', treeAncestors, dataVisitor);
351
365
 
352
366
  // register Vega-Lite selection functions
353
367
  expressionFunction('vlSelectionTest', selectionTest, selectionVisitor);
368
+ expressionFunction('vlSelectionIdTest', selectionIdTest, selectionVisitor);
354
369
  expressionFunction('vlSelectionResolve', selectionResolve, selectionVisitor);
370
+ expressionFunction('vlSelectionTuples', selectionTuples);
@@ -0,0 +1,110 @@
1
+ import intersect from './intersect';
2
+ import { Bounds } from 'vega-scenegraph';
3
+
4
+ /**
5
+ * Appends a new point to the lasso
6
+ *
7
+ * @param {*} lasso the lasso in pixel space
8
+ * @param {*} x the x coordinate in pixel space
9
+ * @param {*} y the y coordinate in pixel space
10
+ * @param {*} minDist the minimum distance, in pixels, that thenew point needs to be apart from the last point
11
+ * @returns a new array containing the lasso with the new point
12
+ */
13
+ export function lassoAppend(lasso, x, y, minDist = 5) {
14
+ const last = lasso[lasso.length - 1];
15
+
16
+ // Add point to lasso if distance to last point exceed minDist or its the first point
17
+ if (last === undefined || Math.sqrt(((last[0] - x) ** 2) + ((last[1] - y) ** 2)) > minDist) {
18
+ lasso.push([x, y]);
19
+
20
+ return [...lasso];
21
+ }
22
+
23
+ return lasso;
24
+ }
25
+
26
+
27
+ /**
28
+ * Generates a svg path command which draws a lasso
29
+ *
30
+ * @param {*} lasso the lasso in pixel space in the form [[x,y], [x,y], ...]
31
+ * @returns the svg path command that draws the lasso
32
+ */
33
+ export function lassoPath(lasso) {
34
+ return (lasso ?? []).reduce((svg, [x, y], i) => {
35
+ return svg += i == 0
36
+ ? `M ${x},${y} `
37
+ : i === lasso.length - 1
38
+ ? ' Z'
39
+ : `L ${x},${y} `;
40
+ }, '');
41
+ }
42
+
43
+
44
+
45
+ /**
46
+ * Inverts the lasso from pixel space to an array of vega scenegraph tuples
47
+ *
48
+ * @param {*} data the dataset
49
+ * @param {*} pixelLasso the lasso in pixel space, [[x,y], [x,y], ...]
50
+ * @param {*} unit the unit where the lasso is defined
51
+ *
52
+ * @returns an array of vega scenegraph tuples
53
+ */
54
+ export function intersectLasso(markname, pixelLasso, unit) {
55
+ const { x, y, mark } = unit;
56
+
57
+ const bb = new Bounds().set(
58
+ Number.MAX_SAFE_INTEGER,
59
+ Number.MAX_SAFE_INTEGER,
60
+ Number.MIN_SAFE_INTEGER,
61
+ Number.MIN_SAFE_INTEGER
62
+ );
63
+
64
+ // Get bounding box around lasso
65
+ for (const [px, py] of pixelLasso) {
66
+ if (px < bb.x1) bb.x1 = px;
67
+ if (px > bb.x2) bb.x2 = px;
68
+ if (py < bb.y1) bb.y1 = py;
69
+ if (py > bb.y2) bb.y2 = py;
70
+ }
71
+
72
+ // Translate bb against unit coordinates
73
+ bb.translate(x, y);
74
+
75
+ const intersection = intersect([[bb.x1, bb.y1], [bb.x2, bb.y2]],
76
+ markname,
77
+ mark);
78
+
79
+ // Check every point against the lasso
80
+ return intersection.filter(tuple => pointInPolygon(tuple.x, tuple.y, pixelLasso));
81
+ }
82
+
83
+
84
+
85
+ /**
86
+ * Performs a test if a point is inside a polygon based on the idea from
87
+ * https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
88
+ *
89
+ * This method will not need the same start/end point since it wraps around the edges of the array
90
+ *
91
+ * @param {*} test a point to test against
92
+ * @param {*} polygon a polygon in the form [[x,y], [x,y], ...]
93
+ * @returns true if the point lies inside the polygon, false otherwise
94
+ */
95
+ function pointInPolygon(testx, testy, polygon) {
96
+ let intersections = 0;
97
+
98
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
99
+ const [prevX, prevY] = polygon[j];
100
+ const [x, y] = polygon[i];
101
+
102
+ // count intersections
103
+ if (((y > testy) != (prevY > testy)) && (testx < (prevX - x) * (testy - y) / (prevY - y) + x)) {
104
+ intersections++;
105
+ }
106
+ }
107
+
108
+ // point is in polygon if intersection count is odd
109
+ return intersections & 1;
110
+ }
@@ -0,0 +1,9 @@
1
+ import {field, isArray} from 'vega-util';
2
+
3
+ // memoize accessor functions
4
+ const accessors = {};
5
+
6
+ export default function(data, name) {
7
+ const accessor = accessors[name] || (accessors[name] = field(name));
8
+ return isArray(data) ? data.map(accessor) : accessor(data);
9
+ }
package/src/parser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {codeGenerator, codegenParams} from './codegen';
2
2
  import {SignalPrefix} from './constants';
3
- import {CallExpression, parse} from 'vega-expression';
3
+ import {CallExpression, parseExpression} from 'vega-expression';
4
4
  import {error, extend, hasOwnProperty, isString, stringValue} from 'vega-util';
5
5
 
6
6
  export default function(expr, scope) {
@@ -10,7 +10,7 @@ export default function(expr, scope) {
10
10
  let ast;
11
11
  try {
12
12
  expr = isString(expr) ? expr : (stringValue(expr) + '');
13
- ast = parse(expr);
13
+ ast = parseExpression(expr);
14
14
  } catch (err) {
15
15
  error('Expression parse error: ' + expr);
16
16
  }