/
map.ts
240 lines (235 loc) · 5.36 KB
/
map.ts
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import type {
IVector,
VecOpV,
VecOpVN,
VecOpVV,
VecOpVVN,
VecOpVVV,
} from "./api";
/**
* Vec2/3/4 view based buffer transformation for {@link VecOpVV} type
* ops and supporting arbitrary component and element layouts of all
* input and output buffers.
*
* @remarks
* The given pre-initialized vectors MUST be separate instances, are
* used as sliding cursors / views of their respective backing buffers
* and will be modified as part of the transformation process (though
* the input buffers themselves are treated as immutable, unless `out`
* is configured to use one of the input buffers).
*
* In each iteration `op` is called via `op(out, a, b)`, followed by
* cursor updates to process the next vector view. No bounds checking is
* performed.
*
* This function returns `out`'s backing buffer.
*
* @example
* ```ts
* // each input buffer contains 2 2D vectors, but using
* // different strided data layouts
* mapVV(
* // transformation function
* add,
* // init output buffer view
* new Vec2(),
* // wrap 1st input buffer & configure offset & component stride
* new Vec2([1,0,2,0,0,0,0,0,3,0,4,0,0,0,0,0], 0, 2),
* // wrap 2nd input buffer
* new Vec2([0,10,0,0,20,0,0,30,0,0,40], 1, 3),
* 2, // num vectors
* 2, // output element stride
* 8, // input #1 element stride
* 6 // input #2 element stride
* );
* // [ 11, 22, 33, 44 ]
* ```
*
* Alternatively, `Vec2/3/4.iterator()` combined with transducers can be
* used to achieve the same (and more flexible) transformations, but
* will incur more intermediate object allocations. `mapV*()` functions
* only use (and mutate) the provided vector instances and do not
* allocate any further objects.
*
* ```ts
* // output buffer
* const out = new Array(4);
*
* tx.run(
* tx.map(([o, a, b]) => add(o, a, b)),
* tx.zip(
* Vec2.iterator(out, 2),
* Vec2.iterator([1,0,2,0,0,0,0,0,3,0,4,0,0,0,0,0], 2, 0, 2, 8),
* Vec2.iterator([0,10,0,0,20,0,0,30,0,0,40], 2, 1, 3, 6),
* )
* );
*
* out
* // [ 11, 22, 33, 44 ]
* ```
*
* @param op -
* @param out -
* @param a -
* @param b -
* @param num -
* @param so -
* @param sa -
* @param sb -
*/
export const mapVV = (
op: VecOpVV,
out: IVector<any>,
a: IVector<any>,
b: IVector<any>,
num: number,
so = out.length * out.stride,
sa = a.length * a.stride,
sb = b.length * b.stride
) => {
while (num-- > 0) {
op(out, a, b);
out.offset += so;
a.offset += sa;
b.offset += sb;
}
return out.buf;
};
/**
* Like {@link mapVV}, but for {@link VecOpV} type ops and hence only using
* single input.
*
* @example
* ```ts
* // 4x 2D vectors in SOA layout
* // i.e. [x1, x2, x3, x4, y1, y2, y3, y4]
* buf = [1, 3, 5, 7, 2, 4, 6, 8];
*
* // use `swapXY` to swizzle each vector and use AOS for output
* res = mapV(swapXY, new Vec2(), new Vec2(buf, 0, 4), 4, 2, 1);
* // [ 2, 1, 4, 3, 6, 5, 8, 7 ]
*
* // unpack result for demonstration purposes
* [...Vec2.iterator(res, 4)].map(v => [...v]);
* // [ [ 2, 1 ], [ 4, 3 ], [ 6, 5 ], [ 8, 7 ] ]
* ```
*
* @param op -
* @param out -
* @param a -
* @param num -
* @param so -
* @param sa -
*/
export const mapV = (
op: VecOpV,
out: IVector<any>,
a: IVector<any>,
num: number,
so = out.length * out.stride,
sa = a.length * a.stride
) => {
while (num-- > 0) {
op(out, a);
out.offset += so;
a.offset += sa;
}
return out.buf;
};
/**
* Like {@link mapVV}, but for {@link VecOpVN} type ops and hence using
* a single vector input buffer `a` and a scalar `n`.
*
* @param op -
* @param out -
* @param a -
* @param n -
* @param num -
* @param so -
* @param sa -
*/
export const mapVN = (
op: VecOpVN,
out: IVector<any>,
a: IVector<any>,
n: number,
num: number,
so = out.length * out.stride,
sa = a.length * a.stride
) => {
while (num-- > 0) {
op(out, a, n);
out.offset += so;
a.offset += sa;
}
return out.buf;
};
/**
* Like {@link mapVV}, but for {@link VecOpVVV} type ops and hence using
* three vector input buffers `a`, `b`, `c`.
*
* @param op -
* @param out -
* @param a -
* @param b -
* @param c -
* @param num -
* @param so -
* @param sa -
* @param sb -
* @param sc -
*/
export const mapVVV = (
op: VecOpVVV,
out: IVector<any>,
a: IVector<any>,
b: IVector<any>,
c: IVector<any>,
num: number,
so = out.length * out.stride,
sa = a.length * a.stride,
sb = b.length * b.stride,
sc = c.length * c.stride
) => {
while (num-- > 0) {
op(out, a, b, c);
out.offset += so;
a.offset += sa;
b.offset += sb;
c.offset += sc;
}
return out.buf;
};
/**
* Like {@link mapVV}, but for {@link VecOpVVN} type ops and hence using
* two vector input buffers `a`, `b` and a scalar `n`.
*
* @param op -
* @param out -
* @param a -
* @param b -
* @param n -
* @param num -
* @param so -
* @param sa -
* @param sb -
*/
export const mapVVN = (
op: VecOpVVN,
out: IVector<any>,
a: IVector<any>,
b: IVector<any>,
n: number,
num: number,
so = out.length * out.stride,
sa = a.length * a.stride,
sb = b.length * b.stride
) => {
while (num-- > 0) {
op(out, a, b, n);
out.offset += so;
a.offset += sa;
b.offset += sb;
}
return out.buf;
};