@woosh/meep-engine 2.109.23 → 2.109.24
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.
- package/package.json +1 -1
- package/src/core/collection/table/bind/TableRecord.d.ts.map +1 -1
- package/src/core/collection/table/bind/TableRecord.js +4 -0
- package/src/core/model/validate_enum_schema.d.ts +10 -0
- package/src/core/model/validate_enum_schema.d.ts.map +1 -0
- package/src/core/model/validate_enum_schema.js +31 -0
- package/src/engine/animation/async/TimeSeries.d.ts +76 -0
- package/src/engine/animation/async/TimeSeries.d.ts.map +1 -0
- package/src/engine/animation/async/TimeSeries.js +285 -0
- package/src/engine/animation/async/findSampleIndex.d.ts +9 -0
- package/src/engine/animation/async/findSampleIndex.d.ts.map +1 -0
- package/src/engine/animation/async/findSampleIndex.js +31 -0
- package/src/engine/animation/async/prototypeAsyncAnimation.js +31 -83
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +14 -11
- package/src/engine/graphics/particles/particular/engine/emitter/ParticlePool.js +1 -1
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableRecord.d.ts","sourceRoot":"","sources":["../../../../../../src/core/collection/table/bind/TableRecord.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TableRecord.d.ts","sourceRoot":"","sources":["../../../../../../src/core/collection/table/bind/TableRecord.js"],"names":[],"mappings":"AAKA;IA+DI;;;OAGG;IACH,yBAYC;IA/BD,qBAEC;IAND,kBAEC;IAOD,uBAIC;IAED,YAEC;IAoBD,sCAEC;IAED;;;;OAIG;IACH,sCAFW,MAAM,QAKhB;;CACJ;kCAhGiC,yBAAyB"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { assert } from "../../../assert.js";
|
|
2
|
+
import { BinaryDataType } from "../../../binary/type/BinaryDataType.js";
|
|
3
|
+
import { validate_enum_schema } from "../../../model/validate_enum_schema.js";
|
|
2
4
|
import { RowFirstTableSpec } from "../RowFirstTableSpec.js";
|
|
3
5
|
|
|
4
6
|
export class TableRecord {
|
|
@@ -72,6 +74,8 @@ export class TableRecord {
|
|
|
72
74
|
assert.notNull(schema, 'schema');
|
|
73
75
|
assert.defined(schema, 'schema');
|
|
74
76
|
|
|
77
|
+
assert.ok(validate_enum_schema(schema, BinaryDataType, console.warn), 'invalid schema');
|
|
78
|
+
|
|
75
79
|
this.#schema = schema;
|
|
76
80
|
const keys = Object.keys(schema);
|
|
77
81
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template T
|
|
3
|
+
* @param {Object<BinaryDataType>} schema
|
|
4
|
+
* @param {Object<T>} enumerable
|
|
5
|
+
* @param {function(message:string)} [error_consumer]
|
|
6
|
+
* @param {*} [error_consumer_context]
|
|
7
|
+
*/
|
|
8
|
+
export function validate_enum_schema<T>(schema: any, enumerable: any, error_consumer?: typeof noop, error_consumer_context?: any): boolean;
|
|
9
|
+
import { noop } from "../function/noop.js";
|
|
10
|
+
//# sourceMappingURL=validate_enum_schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate_enum_schema.d.ts","sourceRoot":"","sources":["../../../../src/core/model/validate_enum_schema.js"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,2IAoBC;qBA7BoB,qBAAqB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { assert } from "../assert.js";
|
|
2
|
+
import { noop } from "../function/noop.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @template T
|
|
6
|
+
* @param {Object<BinaryDataType>} schema
|
|
7
|
+
* @param {Object<T>} enumerable
|
|
8
|
+
* @param {function(message:string)} [error_consumer]
|
|
9
|
+
* @param {*} [error_consumer_context]
|
|
10
|
+
*/
|
|
11
|
+
export function validate_enum_schema(schema, enumerable, error_consumer = noop, error_consumer_context) {
|
|
12
|
+
|
|
13
|
+
assert.notNull(schema, 'schema');
|
|
14
|
+
assert.isObject(schema, 'schema');
|
|
15
|
+
|
|
16
|
+
let valid = true;
|
|
17
|
+
const valid_types = Object.values(enumerable);
|
|
18
|
+
|
|
19
|
+
for (const schemaKey in schema) {
|
|
20
|
+
|
|
21
|
+
const type = schema[schemaKey];
|
|
22
|
+
|
|
23
|
+
if (!valid_types.includes(type)) {
|
|
24
|
+
error_consumer.call(error_consumer_context, `Field '${schemaKey}' is expected to have have BinaryDataType value, instead found '${type}'. Valid values are: ${valid_types.join(', ')}.`);
|
|
25
|
+
valid = false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return valid;
|
|
31
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template T
|
|
3
|
+
*/
|
|
4
|
+
export class TimeSeries<T> {
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {Object<BinaryDataType>} schema
|
|
8
|
+
* @param {string} [time_column_name]
|
|
9
|
+
*/
|
|
10
|
+
constructor(schema: any, time_column_name?: string);
|
|
11
|
+
/**
|
|
12
|
+
* @type {RowFirstTableSpec}
|
|
13
|
+
*/
|
|
14
|
+
spec: RowFirstTableSpec;
|
|
15
|
+
/**
|
|
16
|
+
* @type {RowFirstTable}
|
|
17
|
+
*/
|
|
18
|
+
table: RowFirstTable;
|
|
19
|
+
time_column_index: number;
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @param {number} index
|
|
23
|
+
* @returns {Object}
|
|
24
|
+
*/
|
|
25
|
+
getSampleObjectByIndex(index: number): any;
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @returns {number}
|
|
29
|
+
*/
|
|
30
|
+
get sample_count(): number;
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @returns {number}
|
|
34
|
+
*/
|
|
35
|
+
get last_timestamp(): number;
|
|
36
|
+
validateNextSample(data: any): void;
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @param {number[]} data
|
|
40
|
+
*/
|
|
41
|
+
addSample(data: number[]): void;
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param {Object} sample
|
|
45
|
+
*/
|
|
46
|
+
addObjectSample(sample: any): void;
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* @param {number[]} result
|
|
50
|
+
* @param {number} index
|
|
51
|
+
*/
|
|
52
|
+
getSampleByIndex(result: number[], index: number): void;
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
* @param {number} time
|
|
56
|
+
* @returns {number}
|
|
57
|
+
*/
|
|
58
|
+
findLowSampleIndexByTime(time: number): number;
|
|
59
|
+
/**
|
|
60
|
+
*
|
|
61
|
+
* @param {number[]} result
|
|
62
|
+
* @param {number} result_offset
|
|
63
|
+
* @param {number} time
|
|
64
|
+
*/
|
|
65
|
+
sampleLinear(result: number[], result_offset: number, time: number): void;
|
|
66
|
+
/**
|
|
67
|
+
* Get linearly interpolated sample for given time in object form, following supplied schema
|
|
68
|
+
* @param {number} time
|
|
69
|
+
* @returns {Object}
|
|
70
|
+
*/
|
|
71
|
+
sampleObjectLinear(time: number): any;
|
|
72
|
+
#private;
|
|
73
|
+
}
|
|
74
|
+
import { RowFirstTableSpec } from "../../../core/collection/table/RowFirstTableSpec.js";
|
|
75
|
+
import { RowFirstTable } from "../../../core/collection/table/RowFirstTable.js";
|
|
76
|
+
//# sourceMappingURL=TimeSeries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeSeries.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/async/TimeSeries.js"],"names":[],"mappings":"AAaA;;GAEG;AACH;IA+BI;;;;OAIG;IACH,4CAFW,MAAM,EAuChB;IAxED;;OAEG;IACH,MAFU,iBAAiB,CAEvB;IAEJ;;OAEG;IACH,OAFU,aAAa,CAElB;IAEL,0BAAsB;IAyHtB;;;;OAIG;IACH,8BAHW,MAAM,OAOhB;IAED;;;OAGG;IACH,2BAEC;IAED;;;OAGG;IACH,6BAWC;IAED,oCAcC;IAED;;;OAGG;IACH,gBAFW,MAAM,EAAE,QAQlB;IAED;;;OAGG;IACH,mCAIC;IAED;;;;OAIG;IACH,yBAHW,MAAM,EAAE,SACR,MAAM,QAIhB;IAED;;;;OAIG;IACH,+BAHW,MAAM,GACJ,MAAM,CAMlB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,EAAE,iBACR,MAAM,QACN,MAAM,QAuBhB;IAED;;;;OAIG;IACH,yBAHW,MAAM,OAShB;;CAEJ;kCAzRiC,qDAAqD;8BADzD,iDAAiD"}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { BinaryDataType } from "../../../core/binary/type/BinaryDataType.js";
|
|
3
|
+
import { RowFirstTable } from "../../../core/collection/table/RowFirstTable.js";
|
|
4
|
+
import { RowFirstTableSpec } from "../../../core/collection/table/RowFirstTableSpec.js";
|
|
5
|
+
import { inverseLerp } from "../../../core/math/inverseLerp.js";
|
|
6
|
+
import { lerp } from "../../../core/math/lerp.js";
|
|
7
|
+
import { max2 } from "../../../core/math/max2.js";
|
|
8
|
+
import { min2 } from "../../../core/math/min2.js";
|
|
9
|
+
import { validate_enum_schema } from "../../../core/model/validate_enum_schema.js";
|
|
10
|
+
import { findSampleIndex } from "./findSampleIndex.js";
|
|
11
|
+
|
|
12
|
+
const scratch_row = [];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @template T
|
|
16
|
+
*/
|
|
17
|
+
export class TimeSeries {
|
|
18
|
+
/**
|
|
19
|
+
* @type {RowFirstTableSpec}
|
|
20
|
+
*/
|
|
21
|
+
spec
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @type {RowFirstTable}
|
|
25
|
+
*/
|
|
26
|
+
table
|
|
27
|
+
|
|
28
|
+
time_column_index = 0;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @type {Object<BinaryDataType>}
|
|
33
|
+
*/
|
|
34
|
+
#schema = {}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
*
|
|
38
|
+
* @type {Object<number>}
|
|
39
|
+
*/
|
|
40
|
+
#schema_column_names_to_indices = {};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @type {string[]}
|
|
45
|
+
*/
|
|
46
|
+
#schema_column_indices_to_names = [];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {Object<BinaryDataType>} schema
|
|
51
|
+
* @param {string} [time_column_name]
|
|
52
|
+
*/
|
|
53
|
+
constructor(schema, time_column_name = 'time') {
|
|
54
|
+
assert.isString(time_column_name, 'time_column_name');
|
|
55
|
+
assert.notNull(schema, 'schema');
|
|
56
|
+
assert.isObject(schema, 'schema');
|
|
57
|
+
|
|
58
|
+
let issues = [];
|
|
59
|
+
|
|
60
|
+
if (!validate_enum_schema(schema, BinaryDataType, (issue) => issues.push(issue))) {
|
|
61
|
+
throw new TypeError(`Invalid schema. Problems:\n ${issues.join('\n')}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.#schema = schema;
|
|
65
|
+
|
|
66
|
+
const schema_keys = Object.keys(schema);
|
|
67
|
+
|
|
68
|
+
const time_row_index = schema_keys.indexOf(time_column_name);
|
|
69
|
+
|
|
70
|
+
if (time_row_index === -1) {
|
|
71
|
+
throw new TypeError(`supplies schema does not include time column '${time_column_name}', exising columns: ${schema_keys.join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.time_column_index = time_row_index;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
this.#schema_column_names_to_indices = {};
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < schema_keys.length; i++) {
|
|
80
|
+
const key = schema_keys[i];
|
|
81
|
+
|
|
82
|
+
this.#schema_column_names_to_indices[key] = i;
|
|
83
|
+
this.#schema_column_indices_to_names[i] = key;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.spec = RowFirstTableSpec.get(Object.values(schema));
|
|
87
|
+
|
|
88
|
+
this.table = new RowFirstTable(this.spec);
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @param {number[]} row
|
|
95
|
+
* @param {Object} object
|
|
96
|
+
*/
|
|
97
|
+
#object_to_row(row, object) {
|
|
98
|
+
|
|
99
|
+
const columnCount = this.spec.getColumnCount();
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < columnCount; i++) {
|
|
102
|
+
|
|
103
|
+
const key = this.#schema_column_indices_to_names[i];
|
|
104
|
+
|
|
105
|
+
const value = object[key];
|
|
106
|
+
|
|
107
|
+
const t = typeof value;
|
|
108
|
+
|
|
109
|
+
if (t !== "number") {
|
|
110
|
+
throw new TypeError(`Expected sample.${key} to be a number, instead was ${t} (=${value})`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
row[i] = value;
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
*
|
|
120
|
+
* @param {Object} object
|
|
121
|
+
* @param {number[]} row
|
|
122
|
+
*/
|
|
123
|
+
#row_to_object(object, row) {
|
|
124
|
+
|
|
125
|
+
const columnCount = this.spec.getColumnCount();
|
|
126
|
+
for (let i = 0; i < columnCount; i++) {
|
|
127
|
+
|
|
128
|
+
const key = this.#schema_column_indices_to_names[i];
|
|
129
|
+
|
|
130
|
+
object[key] = row[i];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
*
|
|
137
|
+
* @param {number[]} row
|
|
138
|
+
* @returns {Object}
|
|
139
|
+
*/
|
|
140
|
+
#row_to_object_allocating(row) {
|
|
141
|
+
|
|
142
|
+
const result = {};
|
|
143
|
+
|
|
144
|
+
this.#row_to_object(result, row);
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
*
|
|
151
|
+
* @param {number} index
|
|
152
|
+
* @returns {Object}
|
|
153
|
+
*/
|
|
154
|
+
getSampleObjectByIndex(index) {
|
|
155
|
+
this.table.getRow(index, scratch_row);
|
|
156
|
+
|
|
157
|
+
return this.#row_to_object_allocating(scratch_row);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
*
|
|
162
|
+
* @returns {number}
|
|
163
|
+
*/
|
|
164
|
+
get sample_count() {
|
|
165
|
+
return this.table.length;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
*
|
|
170
|
+
* @returns {number}
|
|
171
|
+
*/
|
|
172
|
+
get last_timestamp() {
|
|
173
|
+
const table = this.table;
|
|
174
|
+
const record_count = table.length;
|
|
175
|
+
|
|
176
|
+
if (record_count === 0) {
|
|
177
|
+
// default
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const time_column = this.time_column_index;
|
|
182
|
+
return table.readCellValue(record_count - 1, time_column);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
validateNextSample(data) {
|
|
186
|
+
|
|
187
|
+
const table = this.table;
|
|
188
|
+
const record_count = table.length;
|
|
189
|
+
|
|
190
|
+
if (record_count > 0) {
|
|
191
|
+
const time_column = this.time_column_index;
|
|
192
|
+
const last_time = table.readCellValue(record_count - 1, time_column);
|
|
193
|
+
|
|
194
|
+
if (data[time_column] <= last_time) {
|
|
195
|
+
throw new Error(`Sample.time[${time_column}] = ${data[time_column]}, which is <= to previous time stamp(=${last_time}). Each sample's time stamp must be strictly greater than the previous one`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
*
|
|
203
|
+
* @param {number[]} data
|
|
204
|
+
*/
|
|
205
|
+
addSample(data) {
|
|
206
|
+
// validate
|
|
207
|
+
this.validateNextSample(data);
|
|
208
|
+
|
|
209
|
+
const table = this.table;
|
|
210
|
+
table.addRow(data);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
*
|
|
215
|
+
* @param {Object} sample
|
|
216
|
+
*/
|
|
217
|
+
addObjectSample(sample) {
|
|
218
|
+
this.#object_to_row(scratch_row, sample);
|
|
219
|
+
|
|
220
|
+
this.addSample(scratch_row);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
*
|
|
225
|
+
* @param {number[]} result
|
|
226
|
+
* @param {number} index
|
|
227
|
+
*/
|
|
228
|
+
getSampleByIndex(result, index) {
|
|
229
|
+
this.table.getRow(index, result)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
*
|
|
234
|
+
* @param {number} time
|
|
235
|
+
* @returns {number}
|
|
236
|
+
*/
|
|
237
|
+
findLowSampleIndexByTime(time) {
|
|
238
|
+
const table = this.table;
|
|
239
|
+
const time_column_index = this.time_column_index;
|
|
240
|
+
return max2(0, findSampleIndex(table, time, time_column_index))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
*
|
|
245
|
+
* @param {number[]} result
|
|
246
|
+
* @param {number} result_offset
|
|
247
|
+
* @param {number} time
|
|
248
|
+
*/
|
|
249
|
+
sampleLinear(result, result_offset, time) {
|
|
250
|
+
const table = this.table;
|
|
251
|
+
const time_column_index = this.time_column_index;
|
|
252
|
+
|
|
253
|
+
// seek to the right sample
|
|
254
|
+
const sample_index = this.findLowSampleIndexByTime(time);
|
|
255
|
+
const next_sample_index = min2(table.length - 1, sample_index + 1);
|
|
256
|
+
|
|
257
|
+
const prev = table.readCellValue(sample_index, time_column_index);
|
|
258
|
+
const next = table.readCellValue(next_sample_index, time_column_index);
|
|
259
|
+
|
|
260
|
+
const normalized_offset = inverseLerp(prev, next, time);
|
|
261
|
+
|
|
262
|
+
const column_count = this.spec.getColumnCount();
|
|
263
|
+
|
|
264
|
+
for (let i = 0; i < column_count; i++) {
|
|
265
|
+
const v0 = table.readCellValue(sample_index, i);
|
|
266
|
+
const v1 = table.readCellValue(next_sample_index, i);
|
|
267
|
+
|
|
268
|
+
result[result_offset + i] = lerp(v0, v1, normalized_offset)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get linearly interpolated sample for given time in object form, following supplied schema
|
|
274
|
+
* @param {number} time
|
|
275
|
+
* @returns {Object}
|
|
276
|
+
*/
|
|
277
|
+
sampleObjectLinear(time) {
|
|
278
|
+
|
|
279
|
+
this.sampleLinear(scratch_row, 0, time);
|
|
280
|
+
|
|
281
|
+
return this.#row_to_object_allocating(scratch_row);
|
|
282
|
+
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {RowFirstTable} table
|
|
4
|
+
* @param {number} time
|
|
5
|
+
* @param {number} [column_index]
|
|
6
|
+
* @return {number}
|
|
7
|
+
*/
|
|
8
|
+
export function findSampleIndex(table: RowFirstTable, time: number, column_index?: number): number;
|
|
9
|
+
//# sourceMappingURL=findSampleIndex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findSampleIndex.d.ts","sourceRoot":"","sources":["../../../../../src/engine/animation/async/findSampleIndex.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,4DAJW,MAAM,iBACN,MAAM,GACL,MAAM,CAuBjB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { min2 } from "../../../core/math/min2.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {RowFirstTable} table
|
|
6
|
+
* @param {number} time
|
|
7
|
+
* @param {number} [column_index]
|
|
8
|
+
* @return {number}
|
|
9
|
+
*/
|
|
10
|
+
export function findSampleIndex(table, time, column_index = 0) {
|
|
11
|
+
let minIndex = 0;
|
|
12
|
+
let maxIndex = table.length - 1;
|
|
13
|
+
|
|
14
|
+
while (minIndex <= maxIndex) {
|
|
15
|
+
|
|
16
|
+
const pivotIndex = (minIndex + maxIndex) >> 1;
|
|
17
|
+
|
|
18
|
+
const cmp = time - table.readCellValue(pivotIndex, column_index);
|
|
19
|
+
|
|
20
|
+
if (cmp > 0) {
|
|
21
|
+
minIndex = pivotIndex + 1;
|
|
22
|
+
} else if (cmp < 0) {
|
|
23
|
+
maxIndex = pivotIndex - 1;
|
|
24
|
+
} else {
|
|
25
|
+
//set low boundary for next step based on assumption that upper bound is higher than lower bound
|
|
26
|
+
return pivotIndex;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return min2(minIndex, maxIndex);
|
|
31
|
+
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { GUI } from "dat.gui";
|
|
2
2
|
import { MeshBasicMaterial, MeshStandardMaterial, OctahedronBufferGeometry } from "three";
|
|
3
3
|
import { BinaryDataType } from "../../../core/binary/type/BinaryDataType.js";
|
|
4
|
-
import { RowFirstTable } from "../../../core/collection/table/RowFirstTable.js";
|
|
5
|
-
import { RowFirstTableSpec } from "../../../core/collection/table/RowFirstTableSpec.js";
|
|
6
4
|
import { Color } from "../../../core/color/Color.js";
|
|
7
|
-
import { inverseLerp } from "../../../core/math/inverseLerp.js";
|
|
8
|
-
import { lerp } from "../../../core/math/lerp.js";
|
|
9
5
|
import { max2 } from "../../../core/math/max2.js";
|
|
10
6
|
import { min2 } from "../../../core/math/min2.js";
|
|
11
7
|
import { randomFloatBetween } from "../../../core/math/random/randomFloatBetween.js";
|
|
@@ -27,42 +23,14 @@ import { BehaviorComponent } from "../../intelligence/behavior/ecs/BehaviorCompo
|
|
|
27
23
|
import { BehaviorSystem } from "../../intelligence/behavior/ecs/BehaviorSystem.js";
|
|
28
24
|
import { ActionBehavior } from "../../intelligence/behavior/primitive/ActionBehavior.js";
|
|
29
25
|
import { RandomDelayBehavior } from "../../intelligence/behavior/util/RandomDelayBehavior.js";
|
|
26
|
+
import { TimeSeries } from "./TimeSeries.js";
|
|
30
27
|
|
|
31
28
|
const harness = new EngineHarness();
|
|
32
29
|
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
* @param {RowFirstTable} table
|
|
36
|
-
* @param {number} time
|
|
37
|
-
* @return {number}
|
|
38
|
-
*/
|
|
39
|
-
function findSampleIndex(table, time) {
|
|
40
|
-
let minIndex = 0;
|
|
41
|
-
let maxIndex = table.length - 1;
|
|
42
|
-
|
|
43
|
-
while (minIndex <= maxIndex) {
|
|
44
|
-
|
|
45
|
-
const pivotIndex = (minIndex + maxIndex) >> 1;
|
|
46
|
-
|
|
47
|
-
const cmp = time - table.readCellValue(pivotIndex, 0);
|
|
48
|
-
|
|
49
|
-
if (cmp > 0) {
|
|
50
|
-
minIndex = pivotIndex + 1;
|
|
51
|
-
} else if (cmp < 0) {
|
|
52
|
-
maxIndex = pivotIndex - 1;
|
|
53
|
-
} else {
|
|
54
|
-
//set low boundary for next step based on assumption that upper bound is higher than lower bound
|
|
55
|
-
return pivotIndex;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return min2(minIndex, maxIndex);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
30
|
/**
|
|
63
31
|
*
|
|
64
32
|
* @param {EntityComponentDataset} ecd
|
|
65
|
-
* @param {
|
|
33
|
+
* @param {TimeSeries} table
|
|
66
34
|
* @param {number} [count]
|
|
67
35
|
*/
|
|
68
36
|
function makeGhostMarkers({
|
|
@@ -108,10 +76,12 @@ function makeGhostMarkers({
|
|
|
108
76
|
return {
|
|
109
77
|
set time(time) {
|
|
110
78
|
|
|
111
|
-
const
|
|
79
|
+
const sample = table.sampleObjectLinear(time);
|
|
80
|
+
|
|
81
|
+
const ref_index = table.findLowSampleIndexByTime(time);
|
|
112
82
|
|
|
113
83
|
const start_index = max2(0, ref_index - Math.floor(count / 2));
|
|
114
|
-
const limit_index = min2(table.
|
|
84
|
+
const limit_index = min2(table.sample_count - 1, ref_index + Math.ceil(count / 2));
|
|
115
85
|
|
|
116
86
|
for (let i = 0; i < count; i++) {
|
|
117
87
|
|
|
@@ -137,10 +107,12 @@ function makeGhostMarkers({
|
|
|
137
107
|
|
|
138
108
|
const transform = entity.getComponentSafe(Transform);
|
|
139
109
|
|
|
110
|
+
const sample = table.getSampleObjectByIndex(sample_index);
|
|
111
|
+
|
|
140
112
|
transform.position.set(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
113
|
+
sample.x,
|
|
114
|
+
sample.y,
|
|
115
|
+
sample.z
|
|
144
116
|
);
|
|
145
117
|
|
|
146
118
|
}
|
|
@@ -158,23 +130,19 @@ async function main(engine) {
|
|
|
158
130
|
engine
|
|
159
131
|
});
|
|
160
132
|
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
])
|
|
168
|
-
);
|
|
133
|
+
const timeSeries = new TimeSeries({
|
|
134
|
+
time: BinaryDataType.Float64,
|
|
135
|
+
x: BinaryDataType.Float32,
|
|
136
|
+
y: BinaryDataType.Float32,
|
|
137
|
+
z: BinaryDataType.Float32,
|
|
138
|
+
}, 'time');
|
|
169
139
|
|
|
170
140
|
const random = seededRandom(42);
|
|
171
141
|
|
|
172
|
-
|
|
142
|
+
timeSeries.addSample([
|
|
173
143
|
0, 0, 1, 0
|
|
174
144
|
]);
|
|
175
145
|
|
|
176
|
-
const sample = [];
|
|
177
|
-
|
|
178
146
|
const sim_clock = new Clock();
|
|
179
147
|
sim_clock.start();
|
|
180
148
|
|
|
@@ -183,9 +151,7 @@ async function main(engine) {
|
|
|
183
151
|
const time_delta = sim_clock.getDelta();
|
|
184
152
|
|
|
185
153
|
// get last row
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
table.getRow(row_count - 1, sample);
|
|
154
|
+
const last_record = timeSeries.getSampleObjectByIndex(timeSeries.sample_count - 1);
|
|
189
155
|
|
|
190
156
|
const speed = 7;
|
|
191
157
|
|
|
@@ -196,15 +162,14 @@ async function main(engine) {
|
|
|
196
162
|
const displacement_x = displacement * Math.cos(direction);
|
|
197
163
|
const displacement_y = displacement * Math.sin(direction);
|
|
198
164
|
|
|
199
|
-
const new_x =
|
|
200
|
-
const new_z =
|
|
165
|
+
const new_x = last_record.x + displacement_x;
|
|
166
|
+
const new_z = last_record.z + displacement_y;
|
|
201
167
|
|
|
202
|
-
|
|
203
|
-
|
|
168
|
+
timeSeries.addSample([
|
|
169
|
+
last_record.time + time_delta,
|
|
204
170
|
new_x,
|
|
205
171
|
1,
|
|
206
172
|
new_z,
|
|
207
|
-
|
|
208
173
|
]);
|
|
209
174
|
}
|
|
210
175
|
|
|
@@ -226,14 +191,14 @@ async function main(engine) {
|
|
|
226
191
|
playing: true,
|
|
227
192
|
play_time: -1,
|
|
228
193
|
get latest_data_time() {
|
|
229
|
-
return
|
|
194
|
+
return timeSeries.last_timestamp;
|
|
230
195
|
},
|
|
231
196
|
get behind_latest() {
|
|
232
|
-
return
|
|
197
|
+
return timeSeries.last_timestamp - this.play_time;
|
|
233
198
|
},
|
|
234
199
|
set behind_latest(v) {
|
|
235
200
|
|
|
236
|
-
const current_delay =
|
|
201
|
+
const current_delay = timeSeries.last_timestamp - this.play_time;
|
|
237
202
|
|
|
238
203
|
const delta = v - current_delay;
|
|
239
204
|
|
|
@@ -260,38 +225,21 @@ async function main(engine) {
|
|
|
260
225
|
sim_parameters.play_time += delta;
|
|
261
226
|
}
|
|
262
227
|
|
|
263
|
-
|
|
264
|
-
// seek to the right sample
|
|
265
|
-
const sample_index = max2(0, findSampleIndex(table, sim_parameters.play_time));
|
|
266
|
-
const next_sample_index = min2(table.length - 1, sample_index + 1);
|
|
267
|
-
|
|
268
|
-
const prev = table.readCellValue(sample_index, 0);
|
|
269
|
-
const next = table.readCellValue(next_sample_index, 0);
|
|
270
|
-
|
|
271
|
-
const normalized_offset = inverseLerp(prev, next, sim_parameters.play_time);
|
|
228
|
+
const sample = timeSeries.sampleObjectLinear(sim_parameters.play_time);
|
|
272
229
|
|
|
273
230
|
const transform = subject.getComponentSafe(Transform);
|
|
274
231
|
|
|
275
|
-
const x0 = table.readCellValue(sample_index, 1);
|
|
276
|
-
const x1 = table.readCellValue(next_sample_index, 1);
|
|
277
|
-
|
|
278
|
-
const y0 = table.readCellValue(sample_index, 2);
|
|
279
|
-
const y1 = table.readCellValue(next_sample_index, 2);
|
|
280
|
-
|
|
281
|
-
const z0 = table.readCellValue(sample_index, 3);
|
|
282
|
-
const z1 = table.readCellValue(next_sample_index, 3);
|
|
283
|
-
|
|
284
232
|
transform.position.set(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
233
|
+
sample.x,
|
|
234
|
+
sample.y,
|
|
235
|
+
sample.z,
|
|
288
236
|
);
|
|
289
237
|
})
|
|
290
238
|
)))
|
|
291
239
|
.build(ecd);
|
|
292
240
|
|
|
293
241
|
const markers = makeGhostMarkers({
|
|
294
|
-
ecd, table, count: 5
|
|
242
|
+
ecd, table: timeSeries, count: 5
|
|
295
243
|
});
|
|
296
244
|
|
|
297
245
|
engine.ticker.onTick.add(() => markers.time = sim_parameters.play_time);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParticleEmitter.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ParticleEmitter.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js"],"names":[],"mappings":"AA2FA;IAyiCI;;;;OAIG;IACH,4BAFa,eAAe,CAM3B;IAjjCD;;OAEG;IACH,IAFU,MAAM,CAEE;IAElB;;;;OAIG;IACH,wBAAoB;IAEpB;;;OAGG;IACH,UAFU,OAAO,CAEe;IAChC,eAA6B;IAC7B,qBAAsC;IAEtC;;;;;OAKG;IACH,qCAA2C;IAE3C;;;;OAIG;IACH,WAFU,MAAM,CAEF;IAEd;;;OAGG;IACH,cAFU,YAAY,GAAC,MAAM,CAEM;IAEnC;;;OAGG;IACH,WAFU,YAAY,GAAC,IAAI,CAEV;IAEjB;;;OAGG;IACH,MAFU,WAAS,IAAI,CAEX;IAEZ;;;OAGG;IACH,mBAFU,SAAS,CAEQ;IAG3B;;;;OAIG;IACH,yBAFU,KAAK,CAE2E;IAE1F;;;OAGG;IACH,yBAFU,KAAK,CAE8B;IAG7C;;;OAGG;IACH,OAFU,MAAM,CAE6F;IAE7G;;;;OAIG;IACH,eAAW;IAGX;;;OAGG;IACH,qBAFU,YAAY,CAEU;IAWhC,wBAIC;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,mBAAmB,GACxB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,mBAAmB,GACxB,IAAI,CAIhB;IAED;;;;OAIG;IACH,gBAHW,MAAM,GAAC,mBAAmB,SAC1B,OAAO,QAQjB;IAED;;;;OAIG;IACH,cAHW,MAAM,GAAC,mBAAmB,GACxB,OAAO,CAInB;IAED;;OAEG;IACH,gCAaC;IAED;;;;;;;;;;;;;MAeC;IAED,0BAkEC;IAGD;;;OAGG;IACH,gBAFW,aAAa,QASvB;IAED;;;;OAIG;IACH,kDAUC;IAED;;;;OAIG;IACH,iCAHW,aAAa,GACZ,MAAM,CA2CjB;IAED,8BA0DC;IAED,8BAmBC;IAED,2BAmBC;IAED,6BAkBC;IAED;;;OAGG;IACH,uCAHW,MAAM,GACJ,MAAM,CA+BlB;IAED,cAuCC;IAED,mBA6BC;IAuBD,eAOC;IAED;;;;OAIG;IACH,cAHW,eAAe,GACb,OAAO,CAQnB;IAED;;;;OAIG;IACH,qCA2FC;IAED,eAOC;IAED;;;OAGG;IACH,4BAFY,MAAM,CAejB;IAED;;;OAGG;IACH,mBAFW,MAAM,QAMhB;IAiCD;;;;OAIG;IACH,sCA+GC;IAED;;;;OAIG;IACH,qCA6GC;IAED;;;OAGG;IACH,aA4CC;IAGD;;OAEG;IACH,gBAIC;;CAYJ;;;;;;;;;;;;;;;;oBAloCmB,wCAAwC;uBAFrC,2CAA2C;6BAOrC,6CAA6C;6BAqB7C,mBAAmB;0BApCtB,+CAA+C;sBAInD,8CAA8C;6BAYvC,8BAA8B;oCAiBvB,0BAA0B;8BAChC,oBAAoB;uCAF3C,0BAA0B;6CAA1B,0BAA0B;kDAA1B,0BAA0B;4CAA1B,0BAA0B;4CAA1B,0BAA0B;kDAA1B,0BAA0B;wCAA1B,0BAA0B;sCAA1B,0BAA0B;4CAA1B,0BAA0B;AAWjC,yCAKC"}
|
|
@@ -3,6 +3,7 @@ import { assert } from "../../../../../../core/assert.js";
|
|
|
3
3
|
import { BvhClient } from "../../../../../../core/bvh2/bvh3/BvhClient.js";
|
|
4
4
|
import { computeHashIntegerArray } from "../../../../../../core/collection/array/computeHashIntegerArray.js";
|
|
5
5
|
import List from "../../../../../../core/collection/list/List.js";
|
|
6
|
+
import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../../../../core/collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
|
|
6
7
|
import { AABB3 } from "../../../../../../core/geom/3d/aabb/AABB3.js";
|
|
7
8
|
import { aabb3_array_combine } from "../../../../../../core/geom/3d/aabb/aabb3_array_combine.js";
|
|
8
9
|
import { aabb3_expand_array } from "../../../../../../core/geom/3d/aabb/aabb3_expand_array.js";
|
|
@@ -60,9 +61,9 @@ const scratch_near_plane_normal = new Float32Array(3);
|
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
63
|
*
|
|
63
|
-
* @type {
|
|
64
|
+
* @type {Float32Array}
|
|
64
65
|
*/
|
|
65
|
-
const
|
|
66
|
+
const scratch_aabb3 = new Float32Array(6);
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
*
|
|
@@ -484,17 +485,17 @@ export class ParticleEmitter {
|
|
|
484
485
|
//retire dead particles
|
|
485
486
|
const particles = this.particles;
|
|
486
487
|
|
|
487
|
-
particles.computeAttributeVector3AxisAlignedBoundingBox(PARTICLE_ATTRIBUTE_POSITION,
|
|
488
|
+
particles.computeAttributeVector3AxisAlignedBoundingBox(PARTICLE_ATTRIBUTE_POSITION, scratch_aabb3);
|
|
488
489
|
|
|
489
490
|
//expand bounds by maximum sprite size
|
|
490
491
|
const extents = this.computeSpriteMaxHalfSize();
|
|
491
492
|
|
|
492
|
-
aabb3_expand_array(
|
|
493
|
+
aabb3_expand_array(scratch_aabb3, scratch_aabb3, extents);
|
|
493
494
|
|
|
494
495
|
//write updated bonds
|
|
495
496
|
const bb = this.particleBounds;
|
|
496
497
|
|
|
497
|
-
bb.readFromArray(
|
|
498
|
+
bb.readFromArray(scratch_aabb3);
|
|
498
499
|
|
|
499
500
|
this.updateGeometryBounds();
|
|
500
501
|
|
|
@@ -516,7 +517,7 @@ export class ParticleEmitter {
|
|
|
516
517
|
const ebb = this.emissionBounds;
|
|
517
518
|
const pbb = this.particleBounds;
|
|
518
519
|
|
|
519
|
-
aabb3_array_combine(this.bvh_leaf.bounds,0, ebb,0, pbb,0);
|
|
520
|
+
aabb3_array_combine(this.bvh_leaf.bounds, 0, ebb, 0, pbb, 0);
|
|
520
521
|
|
|
521
522
|
this.bvh_leaf.write_bounds();
|
|
522
523
|
|
|
@@ -732,15 +733,17 @@ export class ParticleEmitter {
|
|
|
732
733
|
}
|
|
733
734
|
|
|
734
735
|
//Stack-based implementation, avoiding recursion for performance improvement
|
|
735
|
-
const stack =
|
|
736
|
+
const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
|
|
737
|
+
|
|
738
|
+
const stack_top = stack.pointer;
|
|
739
|
+
let stackPointer = stack_top;
|
|
736
740
|
|
|
737
|
-
stack[
|
|
738
|
-
stack[
|
|
741
|
+
stack[stackPointer++] = 0;
|
|
742
|
+
stack[stackPointer++] = particleCount - 1;
|
|
739
743
|
|
|
740
|
-
let stackPointer = 2;
|
|
741
744
|
let i, j;
|
|
742
745
|
|
|
743
|
-
while (stackPointer >
|
|
746
|
+
while (stackPointer > stack_top) {
|
|
744
747
|
stackPointer -= 2;
|
|
745
748
|
|
|
746
749
|
const right = stack[stackPointer + 1];
|