-
Notifications
You must be signed in to change notification settings - Fork 94
/
util.js
115 lines (97 loc) · 3.47 KB
/
util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import * as PIXI from 'pixi.js';
import { Extent } from '@rapid-sdk/math';
// Accepts an array of entities -or- entityIDs
export function utilTotalExtent(array, graph) {
return array.reduce(function(extent, val) {
let entity = (typeof val === 'string' ? graph.hasEntity(val) : val);
if (entity) {
let other = entity.extent(graph);
// update extent in place
extent.min = [ Math.min(extent.min[0], other.min[0]), Math.min(extent.min[1], other.min[1]) ];
extent.max = [ Math.max(extent.max[0], other.max[0]), Math.max(extent.max[1], other.max[1]) ];
}
return extent;
}, new Extent());
}
export function flatCoordsToPoints(coords) {
let points = [];
for (let i = 0; i < coords.length; i += 2){
points.push(new PIXI.Point(coords[i], coords[i + 1]));
}
return points;
}
// Adds or removes highlight styling for the specified entities
export function utilHighlightEntities(ids, highlighted, context) {
const scene = context.scene();
if (highlighted) {
ids.forEach(id => scene.classData('osm', id, 'highlighted'));
} else {
scene.clearClass('highlighted');
}
context.systems.map.immediateRedraw();
}
// `utilSetTransform`
// Applies a CSS transformation to the given selection
export function utilSetTransform(selection, x, y, scale, rotate) {
const t = `translate3d(${x}px,${y}px,0)`;
const s = scale ? ` scale(${scale})` : '';
const r = rotate ? ` rotate(${rotate}deg)` : '';
return selection.style('transform', `${t}${s}${r}`);
}
// a d3.mouse-alike which
// 1. Only works on HTML elements, not SVG
// 2. Does not cause style recalculation
export function utilFastMouse(container) {
const rect = container.getBoundingClientRect();
const rectLeft = rect.left;
const rectTop = rect.top;
const clientLeft = +container.clientLeft;
const clientTop = +container.clientTop;
return function(e) {
return [
e.clientX - rectLeft - clientLeft,
e.clientY - rectTop - clientTop
];
};
}
/**
* utilWrap
* Wraps an index to an interval [0..max-1]
* (Essentially modulo/remainder that works for negative numbers also)
* @param {number} index
* @param {number} max
* @return {number} result
*/
export function utilWrap(index, max) {
if (index < 0) {
index += Math.ceil(-index / max) * max;
}
return index % max;
}
/**
* utilFunctor
* A functor is just a way of turning anything into a function.
* This is particulary useful in places where D3 wants a function to be.
* If passed a function, it returns that function.
* If passed a value, it returns a function that returns that value.
* @param {*} value any value
* @return {Function} a function that returns that value or the value if it's a function
*/
export function utilFunctor(value) {
return (typeof value === 'function') ? value : (() => value);
}
/**
* utilNoAuto
* Sets common attributes on `<input>` or `<textarea>` elements to avoid autocomplete and other annoyances.
* @param {d3-selection} selection - A d3-selection to a `<input>` or `<textarea>`
* @return {d3-selection} same selection but with the attributes set
*/
export function utilNoAuto(selection) {
const isText = (selection.size() && selection.node().tagName.toLowerCase() === 'textarea');
return selection
// assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
.attr('autocomplete', 'new-password')
.attr('autocorrect', 'off')
.attr('autocapitalize', 'off')
.attr('spellcheck', isText ? 'true' : 'false');
}