-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.js
157 lines (140 loc) · 5.19 KB
/
utils.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// utils
const flattenObject = (ob) => {
var toReturn = {};
for (var i in ob) {
if (!ob.hasOwnProperty(i)) continue;
if ((typeof ob[i]) == 'object') {
var flatObject = flattenObject(ob[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
toReturn[i + '.' + x] = flatObject[x];
}
} else {
toReturn[i] = ob[i];
}
}
return toReturn;
};
const interpolateArray = (data, fitCount) => {
const linearInterpolate = function (before, after, atPoint) {
return before + (after - before) * atPoint;
};
const newData = new Array();
const springFactor = new Number((data.length - 1) / (fitCount - 1));
newData[0] = data[0]; // for new allocation
for ( var i = 1; i < fitCount - 1; i++) {
const tmp = i * springFactor;
const before = new Number(Math.floor(tmp)).toFixed();
const after = new Number(Math.ceil(tmp)).toFixed();
const atPoint = tmp - before;
newData[i] = linearInterpolate(data[before], data[after], atPoint);
}
newData[fitCount - 1] = data[data.length - 1]; // for new allocation
return newData;
};
/**
* @param {Point} pt1
* @param {Point} pt1
* @return number The Euclidean distance between `pt1` and `pt2`.
*/
function distance( pt1, pt2 ){
var deltaX = pt1[0] - pt2[0];
var deltaY = pt1[1] - pt2[1];
return Math.sqrt( deltaX * deltaX + deltaY * deltaY );
}
/**
* @param {Point} point The Point object to offset.
* @param {number} dx The delta-x of the line segment from which `point` will
* be offset.
* @param {number} dy The delta-y of the line segment from which `point` will
* be offset.
* @param {number} distRatio The quotient of the distance to offset `point`
* by and the distance of the line segment from which it is being offset.
*/
function offsetPoint( point, dx, dy, distRatio ){
return [
point[ 0 ] - dy * distRatio,
point[ 1 ] + dx * distRatio
];
}
/**
* @param {array of Point} ctrlPoints The vertices of the (multi-segment) line
* to be interpolate along.
* @param {int} number The number of points to interpolate along the line; this
* includes the endpoints, and has an effective minimum value of 2 (if a
* smaller number is given, then the endpoints will still be returned).
* @param {number} [offsetDist] An optional perpendicular distance to offset
* each point from the line-segment it would otherwise lie on.
* @param {int} [minGap] An optional minimum gap to maintain between subsequent
* interpolated points; if the projected gap between subsequent points for
* a set of `number` points is lower than this value, `number` will be
* decreased to a suitable value.
*/
function interpolateLineRange( ctrlPoints, number, offsetDist, minGap ){
minGap = minGap || 0;
offsetDist = offsetDist || 0;
// Calculate path distance from each control point (vertex) to the beginning
// of the line, and also the ratio of `offsetDist` to the length of every
// line segment, for use in computing offsets.
var totalDist = 0;
var ctrlPtDists = [ 0 ];
var ptOffsetRatios = [];
for( var pt = 1; pt < ctrlPoints.length; pt++ ){
var dist = distance( ctrlPoints[ pt ], ctrlPoints[ pt - 1 ] );
totalDist += dist;
ptOffsetRatios.push( offsetDist / dist );
ctrlPtDists.push( totalDist );
}
if( totalDist / (number - 1) < minGap ){
number = totalDist / minGap + 1;
}
// Variables used to control interpolation.
var step = totalDist / (number - 1);
var interpPoints = [ offsetPoint(
ctrlPoints[ 0 ],
ctrlPoints[ 1 ][ 0 ] - ctrlPoints[ 0 ][ 0 ],
ctrlPoints[ 1 ][ 1 ] - ctrlPoints[ 0 ][ 1 ],
ptOffsetRatios[ 0 ]
)];
var prevCtrlPtInd = 0;
var currDist = 0;
var currPoint = ctrlPoints[ 0 ];
var nextDist = step;
for( pt = 0; pt < number - 2; pt++ ){
// Find the segment in which the next interpolated point lies.
while( nextDist > ctrlPtDists[ prevCtrlPtInd + 1 ] ){
prevCtrlPtInd++;
currDist = ctrlPtDists[ prevCtrlPtInd ];
currPoint = ctrlPoints[ prevCtrlPtInd ];
}
// Interpolate the coordinates of the next point along the current segment.
var remainingDist = nextDist - currDist;
var ctrlPtsDeltaX = ctrlPoints[ prevCtrlPtInd + 1 ][ 0 ] -
ctrlPoints[ prevCtrlPtInd ][ 0 ];
var ctrlPtsDeltaY = ctrlPoints[ prevCtrlPtInd + 1 ][ 1 ] -
ctrlPoints[ prevCtrlPtInd ][ 1 ];
var ctrlPtsDist = ctrlPtDists[ prevCtrlPtInd + 1 ] -
ctrlPtDists[ prevCtrlPtInd ];
var distRatio = remainingDist / ctrlPtsDist;
currPoint = [
currPoint[ 0 ] + ctrlPtsDeltaX * distRatio,
currPoint[ 1 ] + ctrlPtsDeltaY * distRatio
];
// Offset currPoint according to `offsetDist`.
var offsetRatio = offsetDist / ctrlPtsDist;
interpPoints.push( offsetPoint(
currPoint, ctrlPtsDeltaX, ctrlPtsDeltaY, ptOffsetRatios[ prevCtrlPtInd ])
);
currDist = nextDist;
nextDist += step;
}
interpPoints.push( offsetPoint(
ctrlPoints[ ctrlPoints.length - 1 ],
ctrlPoints[ ctrlPoints.length - 1 ][ 0 ] -
ctrlPoints[ ctrlPoints.length - 2 ][ 0 ],
ctrlPoints[ ctrlPoints.length - 1 ][ 1 ] -
ctrlPoints[ ctrlPoints.length - 2 ][ 1 ],
ptOffsetRatios[ ptOffsetRatios.length - 1 ]
));
return interpPoints;
}