@holoscript/std 2.1.0 → 6.0.3
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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +197 -0
- package/dist/__tests__/EconomicPrimitives.test.d.ts +2 -0
- package/dist/__tests__/EconomicPrimitives.test.d.ts.map +1 -0
- package/dist/__tests__/EconomicTraits.test.d.ts +2 -0
- package/dist/__tests__/EconomicTraits.test.d.ts.map +1 -0
- package/dist/__tests__/SimulationLabPrimitives.test.d.ts +2 -0
- package/dist/__tests__/SimulationLabPrimitives.test.d.ts.map +1 -0
- package/dist/__tests__/collections.test.d.ts +2 -0
- package/dist/__tests__/collections.test.d.ts.map +1 -0
- package/dist/__tests__/events.test.d.ts +2 -0
- package/dist/__tests__/events.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/materials.test.d.ts +2 -0
- package/dist/__tests__/materials.test.d.ts.map +1 -0
- package/dist/__tests__/math.test.d.ts +2 -0
- package/dist/__tests__/math.test.d.ts.map +1 -0
- package/dist/__tests__/physics.test.d.ts +2 -0
- package/dist/__tests__/physics.test.d.ts.map +1 -0
- package/dist/__tests__/spatial.test.d.ts +2 -0
- package/dist/__tests__/spatial.test.d.ts.map +1 -0
- package/dist/__tests__/standard-library-fundamentals.test.d.ts +2 -0
- package/dist/__tests__/standard-library-fundamentals.test.d.ts.map +1 -0
- package/dist/__tests__/string-and-time-utilities.test.d.ts +2 -0
- package/dist/__tests__/string-and-time-utilities.test.d.ts.map +1 -0
- package/dist/__tests__/string-decoupled.test.d.ts +2 -0
- package/dist/__tests__/string-decoupled.test.d.ts.map +1 -0
- package/dist/__tests__/string.test.d.ts +2 -0
- package/dist/__tests__/string.test.d.ts.map +1 -0
- package/dist/__tests__/time.test.d.ts +2 -0
- package/dist/__tests__/time.test.d.ts.map +1 -0
- package/dist/chunk-7HVUYGPS.js +679 -0
- package/dist/chunk-7HVUYGPS.js.map +1 -0
- package/dist/chunk-P4O3WNG2.js +287 -0
- package/dist/chunk-P4O3WNG2.js.map +1 -0
- package/dist/chunk-PFX2JM4X.js +683 -0
- package/dist/chunk-PFX2JM4X.js.map +1 -0
- package/dist/chunk-W2Q3LUCM.js +348 -0
- package/dist/chunk-W2Q3LUCM.js.map +1 -0
- package/dist/chunk-WS6W35YC.js +443 -0
- package/dist/chunk-WS6W35YC.js.map +1 -0
- package/dist/chunk-ZXVZAQCZ.js +470 -0
- package/dist/chunk-ZXVZAQCZ.js.map +1 -0
- package/dist/collections.cjs +715 -0
- package/dist/collections.cjs.map +1 -0
- package/dist/collections.d.ts.map +1 -1
- package/dist/collections.js +16 -719
- package/dist/collections.js.map +1 -1
- package/dist/events.d.ts +32 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/index.cjs +4101 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +21 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1270 -157
- package/dist/index.js.map +1 -1
- package/dist/materials.d.ts +50 -0
- package/dist/materials.d.ts.map +1 -0
- package/dist/math.cjs +591 -0
- package/dist/math.cjs.map +1 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +51 -537
- package/dist/math.js.map +1 -1
- package/dist/physics.d.ts +59 -0
- package/dist/physics.d.ts.map +1 -0
- package/dist/spatial.d.ts +82 -0
- package/dist/spatial.d.ts.map +1 -0
- package/dist/string.basic.test.d.ts +5 -0
- package/dist/string.basic.test.d.ts.map +1 -0
- package/dist/string.cjs +422 -0
- package/dist/string.cjs.map +1 -0
- package/dist/string.d.ts +5 -1
- package/dist/string.d.ts.map +1 -1
- package/dist/string.js +106 -442
- package/dist/string.js.map +1 -1
- package/dist/string.test.d.ts +5 -0
- package/dist/string.test.d.ts.map +1 -0
- package/dist/time.cjs +512 -0
- package/dist/time.cjs.map +1 -0
- package/dist/time.d.ts.map +1 -1
- package/dist/time.js +41 -528
- package/dist/time.js.map +1 -1
- package/dist/traits/ARTraits.d.ts +14 -0
- package/dist/traits/ARTraits.d.ts.map +1 -0
- package/dist/traits/EconomicPrimitives.cjs +335 -0
- package/dist/traits/EconomicPrimitives.cjs.map +1 -0
- package/dist/traits/EconomicPrimitives.d.ts +345 -0
- package/dist/traits/EconomicPrimitives.d.ts.map +1 -0
- package/dist/traits/EconomicPrimitives.js +55 -0
- package/dist/traits/EconomicPrimitives.js.map +1 -0
- package/dist/traits/EconomicTraits.cjs +471 -0
- package/dist/traits/EconomicTraits.cjs.map +1 -0
- package/dist/traits/EconomicTraits.d.ts +71 -0
- package/dist/traits/EconomicTraits.d.ts.map +1 -0
- package/dist/traits/EconomicTraits.js +17 -0
- package/dist/traits/EconomicTraits.js.map +1 -0
- package/dist/traits/IoTTraits.d.ts +14 -0
- package/dist/traits/IoTTraits.d.ts.map +1 -0
- package/dist/traits/SimulationLabPrimitives.d.ts +187 -0
- package/dist/traits/SimulationLabPrimitives.d.ts.map +1 -0
- package/dist/traits/SimulationLabTraits.d.ts +42 -0
- package/dist/traits/SimulationLabTraits.d.ts.map +1 -0
- package/dist/traits/VRRTraits.d.ts +150 -0
- package/dist/traits/VRRTraits.d.ts.map +1 -0
- package/dist/traits/__tests__/ARTraits.test.d.ts +2 -0
- package/dist/traits/__tests__/ARTraits.test.d.ts.map +1 -0
- package/dist/traits/__tests__/EconomicPrimitives.test.d.ts +2 -0
- package/dist/traits/__tests__/EconomicPrimitives.test.d.ts.map +1 -0
- package/dist/traits/__tests__/EconomicTraits.test.d.ts +2 -0
- package/dist/traits/__tests__/EconomicTraits.test.d.ts.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/error-utf8.log +6 -0
- package/error.log +0 -0
- package/package.json +42 -19
- package/src/__tests__/EconomicPrimitives.test.ts +690 -0
- package/src/__tests__/EconomicTraits.test.ts +425 -0
- package/src/__tests__/SimulationLabPrimitives.test.ts +415 -0
- package/src/__tests__/collections.test.ts +182 -0
- package/src/__tests__/events.test.ts +135 -0
- package/src/__tests__/index.test.ts +14 -0
- package/src/__tests__/materials.test.ts +84 -0
- package/src/__tests__/math.test.ts +246 -0
- package/src/__tests__/physics.test.ts +83 -0
- package/src/__tests__/spatial.test.ts +333 -0
- package/src/__tests__/standard-library-fundamentals.test.ts +1301 -0
- package/src/__tests__/string-and-time-utilities.test.ts +1070 -0
- package/src/__tests__/string-decoupled.test.ts +14 -0
- package/src/__tests__/string.test.ts +164 -0
- package/src/__tests__/time.test.ts +110 -0
- package/src/collections.ts +8 -2
- package/src/events.ts +88 -0
- package/src/index.ts +161 -11
- package/src/materials.ts +109 -0
- package/src/math.ts +30 -14
- package/src/physics.ts +141 -0
- package/src/spatial.ts +320 -0
- package/src/string.basic.test.ts +14 -0
- package/src/string.test.ts +335 -0
- package/src/string.ts +45 -13
- package/src/time.ts +10 -11
- package/src/traits/ARTraits.ts +103 -0
- package/src/traits/EconomicPrimitives.ts +755 -0
- package/src/traits/EconomicTraits.ts +552 -0
- package/src/traits/IoTTraits.ts +102 -0
- package/src/traits/SimulationLabPrimitives.ts +650 -0
- package/src/traits/SimulationLabTraits.ts +191 -0
- package/src/traits/VRRTraits.ts +326 -0
- package/src/traits/__tests__/ARTraits.test.ts +14 -0
- package/src/traits/__tests__/EconomicPrimitives.test.ts +14 -0
- package/src/traits/__tests__/EconomicTraits.test.ts +14 -0
- package/src/types.ts +47 -12
- package/std_error.log +5 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +10 -0
- package/dist/types.js +0 -198
- package/dist/types.js.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AABB: () => AABB,
|
|
24
|
+
CountdownTimer: () => CountdownTimer,
|
|
25
|
+
DEFAULT_BONDING_CURVE: () => DEFAULT_BONDING_CURVE,
|
|
26
|
+
DEFAULT_DEPRECIATION: () => DEFAULT_DEPRECIATION,
|
|
27
|
+
DEFAULT_PID: () => DEFAULT_PID,
|
|
28
|
+
DEFAULT_WEALTH_TAX: () => DEFAULT_WEALTH_TAX,
|
|
29
|
+
DEG_TO_RAD: () => DEG_TO_RAD,
|
|
30
|
+
DateTime: () => DateTime,
|
|
31
|
+
EPSILON: () => EPSILON,
|
|
32
|
+
EconomicTraits: () => EconomicTraits,
|
|
33
|
+
EventBus: () => EventBus,
|
|
34
|
+
FrameTimer: () => FrameTimer,
|
|
35
|
+
HALF_PI: () => HALF_PI,
|
|
36
|
+
HoloMap: () => HoloMap,
|
|
37
|
+
HoloSet: () => HoloSet,
|
|
38
|
+
IntervalTimer: () => IntervalTimer,
|
|
39
|
+
List: () => List,
|
|
40
|
+
MATERIAL_PRESETS: () => MATERIAL_PRESETS,
|
|
41
|
+
Map: () => HoloMap,
|
|
42
|
+
PI: () => PI,
|
|
43
|
+
PriorityQueue: () => PriorityQueue,
|
|
44
|
+
Quaternion: () => Quaternion,
|
|
45
|
+
RAD_TO_DEG: () => RAD_TO_DEG,
|
|
46
|
+
Ray: () => Ray,
|
|
47
|
+
Set: () => HoloSet,
|
|
48
|
+
SimulationLabTraits: () => SimulationLabTraits,
|
|
49
|
+
SpatialGrid: () => SpatialGrid,
|
|
50
|
+
Stopwatch: () => Stopwatch,
|
|
51
|
+
TAU: () => TAU,
|
|
52
|
+
Transform: () => Transform,
|
|
53
|
+
Vec3: () => Vec3,
|
|
54
|
+
aabb: () => aabb,
|
|
55
|
+
aabbMath: () => aabbMath,
|
|
56
|
+
arrayToQuat: () => arrayToQuat,
|
|
57
|
+
arrayToVec3: () => arrayToVec3,
|
|
58
|
+
assert: () => assert,
|
|
59
|
+
assertDefined: () => assertDefined,
|
|
60
|
+
bondingCurveBuyCost: () => bondingCurveBuyCost,
|
|
61
|
+
bondingCurvePrice: () => bondingCurvePrice,
|
|
62
|
+
bondingCurveSellRefund: () => bondingCurveSellRefund,
|
|
63
|
+
calculateDepreciation: () => calculateDepreciation,
|
|
64
|
+
calculateRedistribution: () => calculateRedistribution,
|
|
65
|
+
calculateRepairCost: () => calculateRepairCost,
|
|
66
|
+
calculateTaxAmount: () => calculateTaxAmount,
|
|
67
|
+
calculateTaxRate: () => calculateTaxRate,
|
|
68
|
+
camelCase: () => camelCase,
|
|
69
|
+
capitalize: () => capitalize,
|
|
70
|
+
center: () => center,
|
|
71
|
+
chars: () => chars,
|
|
72
|
+
chiSquaredTest: () => chiSquaredTest,
|
|
73
|
+
clamp: () => clamp2,
|
|
74
|
+
clone: () => clone,
|
|
75
|
+
cohensD: () => cohensD,
|
|
76
|
+
collapseWhitespace: () => collapseWhitespace,
|
|
77
|
+
colorToHex: () => colorToHex,
|
|
78
|
+
compose: () => compose,
|
|
79
|
+
constant: () => constant,
|
|
80
|
+
constantCase: () => constantCase,
|
|
81
|
+
containsIgnoreCase: () => containsIgnoreCase,
|
|
82
|
+
count: () => count,
|
|
83
|
+
createBoxCollider: () => createBoxCollider,
|
|
84
|
+
createCapsuleCollider: () => createCapsuleCollider,
|
|
85
|
+
createDualLoopPIDState: () => createDualLoopPIDState,
|
|
86
|
+
createFaucetSinkMetrics: () => createFaucetSinkMetrics,
|
|
87
|
+
createPBRMaterial: () => createPBRMaterial,
|
|
88
|
+
createPIDState: () => createPIDState,
|
|
89
|
+
createRigidbody: () => createRigidbody,
|
|
90
|
+
createSphereCollider: () => createSphereCollider,
|
|
91
|
+
createTicker: () => createTicker,
|
|
92
|
+
debounce: () => debounce,
|
|
93
|
+
dedent: () => dedent,
|
|
94
|
+
degToRad: () => degToRad2,
|
|
95
|
+
depreciatedValue: () => depreciatedValue,
|
|
96
|
+
distance: () => distance,
|
|
97
|
+
endsWithIgnoreCase: () => endsWithIgnoreCase,
|
|
98
|
+
equals: () => equals,
|
|
99
|
+
escapeHtml: () => escapeHtml,
|
|
100
|
+
escapeRegex: () => escapeRegex,
|
|
101
|
+
executeTrade: () => executeTrade,
|
|
102
|
+
expandSweep: () => expandSweep,
|
|
103
|
+
extractMetric: () => extractMetric,
|
|
104
|
+
extractTraits: () => extractTraits,
|
|
105
|
+
format: () => format,
|
|
106
|
+
formatBytes: () => formatBytes,
|
|
107
|
+
formatDuration: () => formatDuration,
|
|
108
|
+
formatNumber: () => formatNumber,
|
|
109
|
+
fract: () => fract,
|
|
110
|
+
generateSweepCombinations: () => generateSweepCombinations,
|
|
111
|
+
generateTxHash: () => generateTxHash,
|
|
112
|
+
getEconomicTrait: () => getEconomicTrait,
|
|
113
|
+
getEconomicTraitNames: () => getEconomicTraitNames,
|
|
114
|
+
getRequiredPermissions: () => getRequiredPermissions,
|
|
115
|
+
getSimulationTrait: () => getSimulationTrait,
|
|
116
|
+
getSimulationTraitNames: () => getSimulationTraitNames,
|
|
117
|
+
groupByParameter: () => groupByParameter,
|
|
118
|
+
hrTime: () => hrTime,
|
|
119
|
+
hsl: () => hsl,
|
|
120
|
+
identity: () => identity,
|
|
121
|
+
indent: () => indent,
|
|
122
|
+
inverseLerp: () => inverseLerp,
|
|
123
|
+
isAlpha: () => isAlpha,
|
|
124
|
+
isAlphanumeric: () => isAlphanumeric,
|
|
125
|
+
isBlank: () => isBlank,
|
|
126
|
+
isDestroyed: () => isDestroyed,
|
|
127
|
+
isNotBlank: () => isNotBlank,
|
|
128
|
+
isNumeric: () => isNumeric,
|
|
129
|
+
isParameterRange: () => isParameterRange,
|
|
130
|
+
isValidIdentifier: () => isValidIdentifier,
|
|
131
|
+
join: () => join,
|
|
132
|
+
kebabCase: () => kebabCase,
|
|
133
|
+
lerp: () => lerp2,
|
|
134
|
+
levenshtein: () => levenshtein,
|
|
135
|
+
lines: () => lines,
|
|
136
|
+
mannWhitneyU: () => mannWhitneyU,
|
|
137
|
+
mean: () => mean,
|
|
138
|
+
measure: () => measure,
|
|
139
|
+
mod: () => mod,
|
|
140
|
+
noise: () => noise,
|
|
141
|
+
noop: () => noop,
|
|
142
|
+
now: () => now,
|
|
143
|
+
numberWithCommas: () => numberWithCommas,
|
|
144
|
+
oneSampleTTest: () => oneSampleTTest,
|
|
145
|
+
padLeft: () => padLeft,
|
|
146
|
+
padRight: () => padRight,
|
|
147
|
+
parseColor: () => parseColor,
|
|
148
|
+
pascalCase: () => pascalCase,
|
|
149
|
+
pipe: () => pipe,
|
|
150
|
+
print: () => print,
|
|
151
|
+
printError: () => printError,
|
|
152
|
+
printWarn: () => printWarn,
|
|
153
|
+
quat: () => quat,
|
|
154
|
+
quatMath: () => quatMath,
|
|
155
|
+
quatToArray: () => quatToArray,
|
|
156
|
+
radToDeg: () => radToDeg2,
|
|
157
|
+
random: () => random,
|
|
158
|
+
randomString: () => randomString,
|
|
159
|
+
ray: () => ray,
|
|
160
|
+
recordFaucet: () => recordFaucet,
|
|
161
|
+
recordSink: () => recordSink,
|
|
162
|
+
remap: () => remap,
|
|
163
|
+
removePrefix: () => removePrefix,
|
|
164
|
+
removeSuffix: () => removeSuffix,
|
|
165
|
+
removeWhitespace: () => removeWhitespace,
|
|
166
|
+
repeat: () => repeat,
|
|
167
|
+
resetMetricsPeriod: () => resetMetricsPeriod,
|
|
168
|
+
retry: () => retry,
|
|
169
|
+
reverse: () => reverse,
|
|
170
|
+
rgb: () => rgb,
|
|
171
|
+
rgba: () => rgba,
|
|
172
|
+
schedule: () => schedule,
|
|
173
|
+
scheduleFrame: () => scheduleFrame,
|
|
174
|
+
scheduleInterval: () => scheduleInterval,
|
|
175
|
+
sign: () => sign,
|
|
176
|
+
similarity: () => similarity,
|
|
177
|
+
sleep: () => sleep,
|
|
178
|
+
slugify: () => slugify,
|
|
179
|
+
smootherstep: () => smootherstep,
|
|
180
|
+
smoothstep: () => smoothstep,
|
|
181
|
+
snakeCase: () => snakeCase,
|
|
182
|
+
spatialClamp: () => clamp,
|
|
183
|
+
spatialDegToRad: () => degToRad,
|
|
184
|
+
spatialLerp: () => lerp,
|
|
185
|
+
spatialPrice: () => spatialPrice,
|
|
186
|
+
spatialRadToDeg: () => radToDeg,
|
|
187
|
+
standardDeviation: () => standardDeviation,
|
|
188
|
+
standardError: () => standardError,
|
|
189
|
+
startsWithIgnoreCase: () => startsWithIgnoreCase,
|
|
190
|
+
step: () => step,
|
|
191
|
+
summarizeMetrics: () => summarizeMetrics,
|
|
192
|
+
tTest: () => tTest,
|
|
193
|
+
throttle: () => throttle,
|
|
194
|
+
timed: () => timed,
|
|
195
|
+
titleCase: () => titleCase,
|
|
196
|
+
toDegrees: () => radToDeg2,
|
|
197
|
+
toRadians: () => degToRad2,
|
|
198
|
+
todo: () => todo,
|
|
199
|
+
transform: () => transform,
|
|
200
|
+
truncate: () => truncate,
|
|
201
|
+
truncateMiddle: () => truncateMiddle,
|
|
202
|
+
unescapeHtml: () => unescapeHtml,
|
|
203
|
+
unreachable: () => unreachable,
|
|
204
|
+
unwrap: () => unwrap,
|
|
205
|
+
updateDualLoopPID: () => updateDualLoopPID,
|
|
206
|
+
updatePID: () => updatePID,
|
|
207
|
+
uuid: () => uuid,
|
|
208
|
+
validateTraitComposition: () => validateTraitComposition,
|
|
209
|
+
variance: () => variance,
|
|
210
|
+
vec2: () => vec2,
|
|
211
|
+
vec2Math: () => vec2Math,
|
|
212
|
+
vec3: () => vec3,
|
|
213
|
+
vec3Math: () => vec3Math,
|
|
214
|
+
vec3ToArray: () => vec3ToArray,
|
|
215
|
+
vec4: () => vec4,
|
|
216
|
+
waitUntil: () => waitUntil,
|
|
217
|
+
withTimeout: () => withTimeout,
|
|
218
|
+
wordWrap: () => wordWrap,
|
|
219
|
+
words: () => words,
|
|
220
|
+
wrapString: () => wrap
|
|
221
|
+
});
|
|
222
|
+
module.exports = __toCommonJS(index_exports);
|
|
223
|
+
|
|
224
|
+
// src/types.ts
|
|
225
|
+
function vec2(x = 0, y = 0) {
|
|
226
|
+
return { x, y };
|
|
227
|
+
}
|
|
228
|
+
function vec3(x = 0, y = 0, z = 0) {
|
|
229
|
+
return { x, y, z };
|
|
230
|
+
}
|
|
231
|
+
function vec4(x = 0, y = 0, z = 0, w = 0) {
|
|
232
|
+
return { x, y, z, w };
|
|
233
|
+
}
|
|
234
|
+
function quat(x = 0, y = 0, z = 0, w = 1) {
|
|
235
|
+
return { x, y, z, w };
|
|
236
|
+
}
|
|
237
|
+
function transform(position = vec3(), rotation = quat(), scale = vec3(1, 1, 1)) {
|
|
238
|
+
return { position, rotation, scale };
|
|
239
|
+
}
|
|
240
|
+
function rgb(r, g, b) {
|
|
241
|
+
return { r, g, b };
|
|
242
|
+
}
|
|
243
|
+
function rgba(r, g, b, a = 1) {
|
|
244
|
+
return { r, g, b, a };
|
|
245
|
+
}
|
|
246
|
+
function hsl(h, s, l) {
|
|
247
|
+
return { h, s, l };
|
|
248
|
+
}
|
|
249
|
+
function aabb(min = vec3(), max = vec3()) {
|
|
250
|
+
return { min, max };
|
|
251
|
+
}
|
|
252
|
+
function ray(origin, direction) {
|
|
253
|
+
return { origin, direction };
|
|
254
|
+
}
|
|
255
|
+
function vec3ToArray(v) {
|
|
256
|
+
return [v.x, v.y, v.z];
|
|
257
|
+
}
|
|
258
|
+
function arrayToVec3(arr) {
|
|
259
|
+
return { x: arr[0] || 0, y: arr[1] || 0, z: arr[2] || 0 };
|
|
260
|
+
}
|
|
261
|
+
function quatToArray(q) {
|
|
262
|
+
return [q.x, q.y, q.z, q.w];
|
|
263
|
+
}
|
|
264
|
+
function arrayToQuat(arr) {
|
|
265
|
+
return { x: arr[0] || 0, y: arr[1] || 0, z: arr[2] || 0, w: arr[3] ?? 1 };
|
|
266
|
+
}
|
|
267
|
+
function parseColor(color) {
|
|
268
|
+
if (typeof color === "string") {
|
|
269
|
+
if (color.startsWith("#")) {
|
|
270
|
+
const hex = color.slice(1);
|
|
271
|
+
if (hex.length === 3) {
|
|
272
|
+
return {
|
|
273
|
+
r: parseInt(hex[0] + hex[0], 16) / 255,
|
|
274
|
+
g: parseInt(hex[1] + hex[1], 16) / 255,
|
|
275
|
+
b: parseInt(hex[2] + hex[2], 16) / 255,
|
|
276
|
+
a: 1
|
|
277
|
+
};
|
|
278
|
+
} else if (hex.length === 6) {
|
|
279
|
+
return {
|
|
280
|
+
r: parseInt(hex.slice(0, 2), 16) / 255,
|
|
281
|
+
g: parseInt(hex.slice(2, 4), 16) / 255,
|
|
282
|
+
b: parseInt(hex.slice(4, 6), 16) / 255,
|
|
283
|
+
a: 1
|
|
284
|
+
};
|
|
285
|
+
} else if (hex.length === 8) {
|
|
286
|
+
return {
|
|
287
|
+
r: parseInt(hex.slice(0, 2), 16) / 255,
|
|
288
|
+
g: parseInt(hex.slice(2, 4), 16) / 255,
|
|
289
|
+
b: parseInt(hex.slice(4, 6), 16) / 255,
|
|
290
|
+
a: parseInt(hex.slice(6, 8), 16) / 255
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const namedColors = {
|
|
295
|
+
white: { r: 1, g: 1, b: 1, a: 1 },
|
|
296
|
+
black: { r: 0, g: 0, b: 0, a: 1 },
|
|
297
|
+
red: { r: 1, g: 0, b: 0, a: 1 },
|
|
298
|
+
green: { r: 0, g: 1, b: 0, a: 1 },
|
|
299
|
+
blue: { r: 0, g: 0, b: 1, a: 1 },
|
|
300
|
+
yellow: { r: 1, g: 1, b: 0, a: 1 },
|
|
301
|
+
cyan: { r: 0, g: 1, b: 1, a: 1 },
|
|
302
|
+
magenta: { r: 1, g: 0, b: 1, a: 1 }
|
|
303
|
+
};
|
|
304
|
+
return namedColors[color.toLowerCase()] || { r: 1, g: 1, b: 1, a: 1 };
|
|
305
|
+
}
|
|
306
|
+
if (typeof color === "number") {
|
|
307
|
+
return {
|
|
308
|
+
r: (color >> 16 & 255) / 255,
|
|
309
|
+
g: (color >> 8 & 255) / 255,
|
|
310
|
+
b: (color & 255) / 255,
|
|
311
|
+
a: 1
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if ("h" in color) {
|
|
315
|
+
const { h, s, l } = color;
|
|
316
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
317
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
318
|
+
const m = l - c / 2;
|
|
319
|
+
let r = 0, g = 0, b = 0;
|
|
320
|
+
if (h < 60) {
|
|
321
|
+
r = c;
|
|
322
|
+
g = x;
|
|
323
|
+
} else if (h < 120) {
|
|
324
|
+
r = x;
|
|
325
|
+
g = c;
|
|
326
|
+
} else if (h < 180) {
|
|
327
|
+
g = c;
|
|
328
|
+
b = x;
|
|
329
|
+
} else if (h < 240) {
|
|
330
|
+
g = x;
|
|
331
|
+
b = c;
|
|
332
|
+
} else if (h < 300) {
|
|
333
|
+
r = x;
|
|
334
|
+
b = c;
|
|
335
|
+
} else {
|
|
336
|
+
r = c;
|
|
337
|
+
b = x;
|
|
338
|
+
}
|
|
339
|
+
return { r: r + m, g: g + m, b: b + m, a: 1 };
|
|
340
|
+
}
|
|
341
|
+
if ("a" in color) {
|
|
342
|
+
return color;
|
|
343
|
+
}
|
|
344
|
+
return { ...color, a: 1 };
|
|
345
|
+
}
|
|
346
|
+
function colorToHex(color) {
|
|
347
|
+
const r = Math.round(color.r * 255).toString(16).padStart(2, "0");
|
|
348
|
+
const g = Math.round(color.g * 255).toString(16).padStart(2, "0");
|
|
349
|
+
const b = Math.round(color.b * 255).toString(16).padStart(2, "0");
|
|
350
|
+
return `#${r}${g}${b}`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/spatial.ts
|
|
354
|
+
var Vec3 = class _Vec3 {
|
|
355
|
+
constructor(x = 0, y = 0, z = 0) {
|
|
356
|
+
this.x = x;
|
|
357
|
+
this.y = y;
|
|
358
|
+
this.z = z;
|
|
359
|
+
}
|
|
360
|
+
static zero() {
|
|
361
|
+
return new _Vec3(0, 0, 0);
|
|
362
|
+
}
|
|
363
|
+
static one() {
|
|
364
|
+
return new _Vec3(1, 1, 1);
|
|
365
|
+
}
|
|
366
|
+
static up() {
|
|
367
|
+
return new _Vec3(0, 1, 0);
|
|
368
|
+
}
|
|
369
|
+
static down() {
|
|
370
|
+
return new _Vec3(0, -1, 0);
|
|
371
|
+
}
|
|
372
|
+
static forward() {
|
|
373
|
+
return new _Vec3(0, 0, -1);
|
|
374
|
+
}
|
|
375
|
+
static right() {
|
|
376
|
+
return new _Vec3(1, 0, 0);
|
|
377
|
+
}
|
|
378
|
+
add(other) {
|
|
379
|
+
return new _Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
|
|
380
|
+
}
|
|
381
|
+
sub(other) {
|
|
382
|
+
return new _Vec3(this.x - other.x, this.y - other.y, this.z - other.z);
|
|
383
|
+
}
|
|
384
|
+
mul(s) {
|
|
385
|
+
return new _Vec3(this.x * s, this.y * s, this.z * s);
|
|
386
|
+
}
|
|
387
|
+
div(s) {
|
|
388
|
+
return new _Vec3(this.x / s, this.y / s, this.z / s);
|
|
389
|
+
}
|
|
390
|
+
dot(other) {
|
|
391
|
+
return this.x * other.x + this.y * other.y + this.z * other.z;
|
|
392
|
+
}
|
|
393
|
+
cross(other) {
|
|
394
|
+
return new _Vec3(
|
|
395
|
+
this.y * other.z - this.z * other.y,
|
|
396
|
+
this.z * other.x - this.x * other.z,
|
|
397
|
+
this.x * other.y - this.y * other.x
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
length() {
|
|
401
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
402
|
+
}
|
|
403
|
+
lengthSquared() {
|
|
404
|
+
return this.x * this.x + this.y * this.y + this.z * this.z;
|
|
405
|
+
}
|
|
406
|
+
normalize() {
|
|
407
|
+
const l = this.length();
|
|
408
|
+
return l > 0 ? this.div(l) : _Vec3.zero();
|
|
409
|
+
}
|
|
410
|
+
distanceTo(other) {
|
|
411
|
+
return this.sub(other).length();
|
|
412
|
+
}
|
|
413
|
+
lerp(other, t) {
|
|
414
|
+
return this.add(other.sub(this).mul(t));
|
|
415
|
+
}
|
|
416
|
+
toArray() {
|
|
417
|
+
return [this.x, this.y, this.z];
|
|
418
|
+
}
|
|
419
|
+
static fromArray(arr) {
|
|
420
|
+
return new _Vec3(arr[0] || 0, arr[1] || 0, arr[2] || 0);
|
|
421
|
+
}
|
|
422
|
+
equals(other, epsilon = 1e-6) {
|
|
423
|
+
return Math.abs(this.x - other.x) < epsilon && Math.abs(this.y - other.y) < epsilon && Math.abs(this.z - other.z) < epsilon;
|
|
424
|
+
}
|
|
425
|
+
toString() {
|
|
426
|
+
return `Vec3(${this.x}, ${this.y}, ${this.z})`;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
var Quaternion = class _Quaternion {
|
|
430
|
+
constructor(x = 0, y = 0, z = 0, w = 1) {
|
|
431
|
+
this.x = x;
|
|
432
|
+
this.y = y;
|
|
433
|
+
this.z = z;
|
|
434
|
+
this.w = w;
|
|
435
|
+
}
|
|
436
|
+
static identity() {
|
|
437
|
+
return new _Quaternion(0, 0, 0, 1);
|
|
438
|
+
}
|
|
439
|
+
static fromEuler(x, y, z) {
|
|
440
|
+
const cx = Math.cos(x * 0.5), sx = Math.sin(x * 0.5);
|
|
441
|
+
const cy = Math.cos(y * 0.5), sy = Math.sin(y * 0.5);
|
|
442
|
+
const cz = Math.cos(z * 0.5), sz = Math.sin(z * 0.5);
|
|
443
|
+
return new _Quaternion(
|
|
444
|
+
sx * cy * cz - cx * sy * sz,
|
|
445
|
+
cx * sy * cz + sx * cy * sz,
|
|
446
|
+
cx * cy * sz - sx * sy * cz,
|
|
447
|
+
cx * cy * cz + sx * sy * sz
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
static fromAxisAngle(axis, angle) {
|
|
451
|
+
const half = angle * 0.5;
|
|
452
|
+
const s = Math.sin(half);
|
|
453
|
+
const n = axis.normalize();
|
|
454
|
+
return new _Quaternion(n.x * s, n.y * s, n.z * s, Math.cos(half));
|
|
455
|
+
}
|
|
456
|
+
multiply(other) {
|
|
457
|
+
return new _Quaternion(
|
|
458
|
+
this.w * other.x + this.x * other.w + this.y * other.z - this.z * other.y,
|
|
459
|
+
this.w * other.y - this.x * other.z + this.y * other.w + this.z * other.x,
|
|
460
|
+
this.w * other.z + this.x * other.y - this.y * other.x + this.z * other.w,
|
|
461
|
+
this.w * other.w - this.x * other.x - this.y * other.y - this.z * other.z
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
rotateVec3(v) {
|
|
465
|
+
const u = new Vec3(this.x, this.y, this.z);
|
|
466
|
+
const s = this.w;
|
|
467
|
+
return u.mul(2 * u.dot(v)).add(v.mul(s * s - u.dot(u))).add(u.cross(v).mul(2 * s));
|
|
468
|
+
}
|
|
469
|
+
slerp(other, t) {
|
|
470
|
+
let dot = this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w;
|
|
471
|
+
const neg = dot < 0;
|
|
472
|
+
if (neg) dot = -dot;
|
|
473
|
+
if (dot > 0.9995) {
|
|
474
|
+
const r = new _Quaternion(
|
|
475
|
+
this.x + t * ((neg ? -other.x : other.x) - this.x),
|
|
476
|
+
this.y + t * ((neg ? -other.y : other.y) - this.y),
|
|
477
|
+
this.z + t * ((neg ? -other.z : other.z) - this.z),
|
|
478
|
+
this.w + t * ((neg ? -other.w : other.w) - this.w)
|
|
479
|
+
);
|
|
480
|
+
const len = Math.sqrt(r.x * r.x + r.y * r.y + r.z * r.z + r.w * r.w);
|
|
481
|
+
return new _Quaternion(r.x / len, r.y / len, r.z / len, r.w / len);
|
|
482
|
+
}
|
|
483
|
+
const theta = Math.acos(dot);
|
|
484
|
+
const sinTheta = Math.sin(theta);
|
|
485
|
+
const a = Math.sin((1 - t) * theta) / sinTheta;
|
|
486
|
+
const b = Math.sin(t * theta) / sinTheta * (neg ? -1 : 1);
|
|
487
|
+
return new _Quaternion(
|
|
488
|
+
a * this.x + b * other.x,
|
|
489
|
+
a * this.y + b * other.y,
|
|
490
|
+
a * this.z + b * other.z,
|
|
491
|
+
a * this.w + b * other.w
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
normalize() {
|
|
495
|
+
const len = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
496
|
+
return new _Quaternion(this.x / len, this.y / len, this.z / len, this.w / len);
|
|
497
|
+
}
|
|
498
|
+
toEuler() {
|
|
499
|
+
const sinr = 2 * (this.w * this.x + this.y * this.z);
|
|
500
|
+
const cosr = 1 - 2 * (this.x * this.x + this.y * this.y);
|
|
501
|
+
const sinp = 2 * (this.w * this.y - this.z * this.x);
|
|
502
|
+
const siny = 2 * (this.w * this.z + this.x * this.y);
|
|
503
|
+
const cosy = 1 - 2 * (this.y * this.y + this.z * this.z);
|
|
504
|
+
return new Vec3(
|
|
505
|
+
Math.atan2(sinr, cosr),
|
|
506
|
+
Math.abs(sinp) >= 1 ? Math.sign(sinp) * Math.PI / 2 : Math.asin(sinp),
|
|
507
|
+
Math.atan2(siny, cosy)
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
var Transform = class _Transform {
|
|
512
|
+
constructor(position = Vec3.zero(), rotation = Quaternion.identity(), scale = Vec3.one()) {
|
|
513
|
+
this.position = position;
|
|
514
|
+
this.rotation = rotation;
|
|
515
|
+
this.scale = scale;
|
|
516
|
+
}
|
|
517
|
+
static identity() {
|
|
518
|
+
return new _Transform();
|
|
519
|
+
}
|
|
520
|
+
transformPoint(point) {
|
|
521
|
+
const scaled = new Vec3(point.x * this.scale.x, point.y * this.scale.y, point.z * this.scale.z);
|
|
522
|
+
return this.rotation.rotateVec3(scaled).add(this.position);
|
|
523
|
+
}
|
|
524
|
+
transformDirection(dir) {
|
|
525
|
+
return this.rotation.rotateVec3(dir);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var Ray = class {
|
|
529
|
+
constructor(origin, direction) {
|
|
530
|
+
this.origin = origin;
|
|
531
|
+
this.direction = direction;
|
|
532
|
+
}
|
|
533
|
+
pointAt(t) {
|
|
534
|
+
return this.origin.add(this.direction.mul(t));
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
var AABB = class {
|
|
538
|
+
constructor(min, max) {
|
|
539
|
+
this.min = min;
|
|
540
|
+
this.max = max;
|
|
541
|
+
}
|
|
542
|
+
contains(point) {
|
|
543
|
+
return point.x >= this.min.x && point.x <= this.max.x && point.y >= this.min.y && point.y <= this.max.y && point.z >= this.min.z && point.z <= this.max.z;
|
|
544
|
+
}
|
|
545
|
+
intersects(other) {
|
|
546
|
+
return this.min.x <= other.max.x && this.max.x >= other.min.x && this.min.y <= other.max.y && this.max.y >= other.min.y && this.min.z <= other.max.z && this.max.z >= other.min.z;
|
|
547
|
+
}
|
|
548
|
+
center() {
|
|
549
|
+
return this.min.add(this.max).div(2);
|
|
550
|
+
}
|
|
551
|
+
size() {
|
|
552
|
+
return this.max.sub(this.min);
|
|
553
|
+
}
|
|
554
|
+
intersectsRay(ray2) {
|
|
555
|
+
let tmin = (this.min.x - ray2.origin.x) / ray2.direction.x;
|
|
556
|
+
let tmax = (this.max.x - ray2.origin.x) / ray2.direction.x;
|
|
557
|
+
if (tmin > tmax) [tmin, tmax] = [tmax, tmin];
|
|
558
|
+
let tymin = (this.min.y - ray2.origin.y) / ray2.direction.y;
|
|
559
|
+
let tymax = (this.max.y - ray2.origin.y) / ray2.direction.y;
|
|
560
|
+
if (tymin > tymax) [tymin, tymax] = [tymax, tymin];
|
|
561
|
+
if (tmin > tymax || tymin > tmax) return null;
|
|
562
|
+
tmin = Math.max(tmin, tymin);
|
|
563
|
+
tmax = Math.min(tmax, tymax);
|
|
564
|
+
let tzmin = (this.min.z - ray2.origin.z) / ray2.direction.z;
|
|
565
|
+
let tzmax = (this.max.z - ray2.origin.z) / ray2.direction.z;
|
|
566
|
+
if (tzmin > tzmax) [tzmin, tzmax] = [tzmax, tzmin];
|
|
567
|
+
if (tmin > tzmax || tzmin > tmax) return null;
|
|
568
|
+
return Math.max(tmin, tzmin);
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
function distance(a, b) {
|
|
572
|
+
return a.distanceTo(b);
|
|
573
|
+
}
|
|
574
|
+
function lerp(a, b, t) {
|
|
575
|
+
return a + (b - a) * t;
|
|
576
|
+
}
|
|
577
|
+
function clamp(v, min, max) {
|
|
578
|
+
return Math.min(Math.max(v, min), max);
|
|
579
|
+
}
|
|
580
|
+
function degToRad(deg) {
|
|
581
|
+
return deg * (Math.PI / 180);
|
|
582
|
+
}
|
|
583
|
+
function radToDeg(rad) {
|
|
584
|
+
return rad * (180 / Math.PI);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/physics.ts
|
|
588
|
+
function createBoxCollider(halfExtents, options) {
|
|
589
|
+
return {
|
|
590
|
+
shape: "box",
|
|
591
|
+
isTrigger: false,
|
|
592
|
+
friction: 0.5,
|
|
593
|
+
restitution: 0,
|
|
594
|
+
halfExtents,
|
|
595
|
+
...options
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function createSphereCollider(radius, options) {
|
|
599
|
+
return { shape: "sphere", isTrigger: false, friction: 0.5, restitution: 0, radius, ...options };
|
|
600
|
+
}
|
|
601
|
+
function createCapsuleCollider(radius, height, options) {
|
|
602
|
+
return {
|
|
603
|
+
shape: "capsule",
|
|
604
|
+
isTrigger: false,
|
|
605
|
+
friction: 0.5,
|
|
606
|
+
restitution: 0,
|
|
607
|
+
radius,
|
|
608
|
+
height,
|
|
609
|
+
...options
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function createRigidbody(mass, options) {
|
|
613
|
+
return {
|
|
614
|
+
mass,
|
|
615
|
+
useGravity: true,
|
|
616
|
+
linearDamping: 0,
|
|
617
|
+
angularDamping: 0.05,
|
|
618
|
+
freezePosition: [false, false, false],
|
|
619
|
+
freezeRotation: [false, false, false],
|
|
620
|
+
isKinematic: false,
|
|
621
|
+
...options
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/materials.ts
|
|
626
|
+
var MATERIAL_PRESETS = {
|
|
627
|
+
metal: { roughness: 0.2, metallic: 1 },
|
|
628
|
+
wood: { roughness: 0.85, metallic: 0, baseColor: "#8B6914" },
|
|
629
|
+
glass: { roughness: 0.05, metallic: 0, opacity: 0.3, ior: 1.5 },
|
|
630
|
+
plastic: { roughness: 0.4, metallic: 0 },
|
|
631
|
+
concrete: { roughness: 0.95, metallic: 0, baseColor: "#808080" },
|
|
632
|
+
fabric: { roughness: 0.9, metallic: 0, subsurface: 0.2 },
|
|
633
|
+
water: { roughness: 0.05, metallic: 0, opacity: 0.7, ior: 1.33 },
|
|
634
|
+
rubber: { roughness: 0.95, metallic: 0, baseColor: "#222222" },
|
|
635
|
+
marble: { roughness: 0.3, metallic: 0, baseColor: "#f0f0f0" },
|
|
636
|
+
skin: { roughness: 0.6, metallic: 0, subsurface: 0.5 },
|
|
637
|
+
foliage: { roughness: 0.8, metallic: 0, baseColor: "#2d5a27", subsurface: 0.3 },
|
|
638
|
+
chrome: { roughness: 0.05, metallic: 1, baseColor: "#cccccc" },
|
|
639
|
+
gold: { roughness: 0.2, metallic: 1, baseColor: "#ffd700" },
|
|
640
|
+
copper: { roughness: 0.3, metallic: 1, baseColor: "#b87333" },
|
|
641
|
+
ice: { roughness: 0.1, metallic: 0, opacity: 0.8, ior: 1.31 }
|
|
642
|
+
};
|
|
643
|
+
function createPBRMaterial(name, preset, overrides) {
|
|
644
|
+
const base = preset ? MATERIAL_PRESETS[preset] : {};
|
|
645
|
+
return {
|
|
646
|
+
name,
|
|
647
|
+
baseColor: "#ffffff",
|
|
648
|
+
roughness: 0.5,
|
|
649
|
+
metallic: 0,
|
|
650
|
+
...base,
|
|
651
|
+
...overrides
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/events.ts
|
|
656
|
+
var EventBus = class {
|
|
657
|
+
listeners = /* @__PURE__ */ new Map();
|
|
658
|
+
wildcardListeners = [];
|
|
659
|
+
/** Register an event listener. */
|
|
660
|
+
on(event, handler) {
|
|
661
|
+
if (!this.listeners.has(event)) this.listeners.set(event, []);
|
|
662
|
+
const listener = { handler, once: false };
|
|
663
|
+
this.listeners.get(event).push(listener);
|
|
664
|
+
return () => this.off(event, handler);
|
|
665
|
+
}
|
|
666
|
+
/** Register a one-time event listener. */
|
|
667
|
+
once(event, handler) {
|
|
668
|
+
if (!this.listeners.has(event)) this.listeners.set(event, []);
|
|
669
|
+
const listener = { handler, once: true };
|
|
670
|
+
this.listeners.get(event).push(listener);
|
|
671
|
+
return () => this.off(event, handler);
|
|
672
|
+
}
|
|
673
|
+
/** Remove an event listener. */
|
|
674
|
+
off(event, handler) {
|
|
675
|
+
const list = this.listeners.get(event);
|
|
676
|
+
if (!list) return;
|
|
677
|
+
this.listeners.set(
|
|
678
|
+
event,
|
|
679
|
+
list.filter((l) => l.handler !== handler)
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
/** Emit an event to all registered listeners. */
|
|
683
|
+
emit(event, data) {
|
|
684
|
+
const list = this.listeners.get(event);
|
|
685
|
+
if (list) {
|
|
686
|
+
const remaining = [];
|
|
687
|
+
for (const l of list) {
|
|
688
|
+
l.handler(data);
|
|
689
|
+
if (!l.once) remaining.push(l);
|
|
690
|
+
}
|
|
691
|
+
this.listeners.set(event, remaining);
|
|
692
|
+
}
|
|
693
|
+
for (const l of this.wildcardListeners) {
|
|
694
|
+
l.handler({ event, data });
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
/** Listen to all events. */
|
|
698
|
+
onAny(handler) {
|
|
699
|
+
const listener = { handler, once: false };
|
|
700
|
+
this.wildcardListeners.push(listener);
|
|
701
|
+
return () => {
|
|
702
|
+
this.wildcardListeners = this.wildcardListeners.filter((l) => l !== listener);
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
/** Remove all listeners. */
|
|
706
|
+
clear() {
|
|
707
|
+
this.listeners.clear();
|
|
708
|
+
this.wildcardListeners = [];
|
|
709
|
+
}
|
|
710
|
+
/** Get count of listeners for an event (or all events). */
|
|
711
|
+
listenerCount(event) {
|
|
712
|
+
if (event) return (this.listeners.get(event) || []).length;
|
|
713
|
+
let total = this.wildcardListeners.length;
|
|
714
|
+
for (const [, list] of this.listeners) total += list.length;
|
|
715
|
+
return total;
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// src/math.ts
|
|
720
|
+
var PI = Math.PI;
|
|
721
|
+
var TAU = Math.PI * 2;
|
|
722
|
+
var HALF_PI = Math.PI / 2;
|
|
723
|
+
var DEG_TO_RAD = Math.PI / 180;
|
|
724
|
+
var RAD_TO_DEG = 180 / Math.PI;
|
|
725
|
+
var EPSILON = 1e-6;
|
|
726
|
+
function clamp2(value, min, max) {
|
|
727
|
+
return Math.max(min, Math.min(max, value));
|
|
728
|
+
}
|
|
729
|
+
function lerp2(a, b, t) {
|
|
730
|
+
return a + (b - a) * t;
|
|
731
|
+
}
|
|
732
|
+
function inverseLerp(a, b, value) {
|
|
733
|
+
return (value - a) / (b - a);
|
|
734
|
+
}
|
|
735
|
+
function remap(value, inMin, inMax, outMin, outMax) {
|
|
736
|
+
return lerp2(outMin, outMax, inverseLerp(inMin, inMax, value));
|
|
737
|
+
}
|
|
738
|
+
function smoothstep(edge0, edge1, x) {
|
|
739
|
+
const t = clamp2((x - edge0) / (edge1 - edge0), 0, 1);
|
|
740
|
+
return t * t * (3 - 2 * t);
|
|
741
|
+
}
|
|
742
|
+
function smootherstep(edge0, edge1, x) {
|
|
743
|
+
const t = clamp2((x - edge0) / (edge1 - edge0), 0, 1);
|
|
744
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
745
|
+
}
|
|
746
|
+
function degToRad2(degrees) {
|
|
747
|
+
return degrees * DEG_TO_RAD;
|
|
748
|
+
}
|
|
749
|
+
function radToDeg2(radians) {
|
|
750
|
+
return radians * RAD_TO_DEG;
|
|
751
|
+
}
|
|
752
|
+
function mod(n, m) {
|
|
753
|
+
return (n % m + m) % m;
|
|
754
|
+
}
|
|
755
|
+
function fract(x) {
|
|
756
|
+
return x - Math.floor(x);
|
|
757
|
+
}
|
|
758
|
+
function sign(x) {
|
|
759
|
+
return x > 0 ? 1 : x < 0 ? -1 : 0;
|
|
760
|
+
}
|
|
761
|
+
function step(edge, x) {
|
|
762
|
+
return x < edge ? 0 : 1;
|
|
763
|
+
}
|
|
764
|
+
var vec2Math = {
|
|
765
|
+
add: (a, b) => ({ x: a.x + b.x, y: a.y + b.y }),
|
|
766
|
+
sub: (a, b) => ({ x: a.x - b.x, y: a.y - b.y }),
|
|
767
|
+
mul: (a, s) => ({ x: a.x * s, y: a.y * s }),
|
|
768
|
+
div: (a, s) => ({ x: a.x / s, y: a.y / s }),
|
|
769
|
+
dot: (a, b) => a.x * b.x + a.y * b.y,
|
|
770
|
+
length: (v) => Math.sqrt(v.x * v.x + v.y * v.y),
|
|
771
|
+
lengthSq: (v) => v.x * v.x + v.y * v.y,
|
|
772
|
+
normalize: (v) => {
|
|
773
|
+
const len = vec2Math.length(v);
|
|
774
|
+
return len > 0 ? vec2Math.div(v, len) : { x: 0, y: 0 };
|
|
775
|
+
},
|
|
776
|
+
distance: (a, b) => vec2Math.length(vec2Math.sub(a, b)),
|
|
777
|
+
lerp: (a, b, t) => ({
|
|
778
|
+
x: lerp2(a.x, b.x, t),
|
|
779
|
+
y: lerp2(a.y, b.y, t)
|
|
780
|
+
}),
|
|
781
|
+
rotate: (v, angle) => {
|
|
782
|
+
const c = Math.cos(angle);
|
|
783
|
+
const s = Math.sin(angle);
|
|
784
|
+
return { x: v.x * c - v.y * s, y: v.x * s + v.y * c };
|
|
785
|
+
},
|
|
786
|
+
angle: (v) => Math.atan2(v.y, v.x),
|
|
787
|
+
angleBetween: (a, b) => {
|
|
788
|
+
return Math.acos(clamp2(vec2Math.dot(vec2Math.normalize(a), vec2Math.normalize(b)), -1, 1));
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
var vec3Math = {
|
|
792
|
+
zero: () => vec3(0, 0, 0),
|
|
793
|
+
one: () => vec3(1, 1, 1),
|
|
794
|
+
up: () => vec3(0, 1, 0),
|
|
795
|
+
down: () => vec3(0, -1, 0),
|
|
796
|
+
forward: () => vec3(0, 0, -1),
|
|
797
|
+
back: () => vec3(0, 0, 1),
|
|
798
|
+
left: () => vec3(-1, 0, 0),
|
|
799
|
+
right: () => vec3(1, 0, 0),
|
|
800
|
+
add: (a, b) => ({ x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }),
|
|
801
|
+
sub: (a, b) => ({ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }),
|
|
802
|
+
mul: (a, s) => ({ x: a.x * s, y: a.y * s, z: a.z * s }),
|
|
803
|
+
div: (a, s) => ({ x: a.x / s, y: a.y / s, z: a.z / s }),
|
|
804
|
+
mulVec: (a, b) => ({ x: a.x * b.x, y: a.y * b.y, z: a.z * b.z }),
|
|
805
|
+
dot: (a, b) => a.x * b.x + a.y * b.y + a.z * b.z,
|
|
806
|
+
cross: (a, b) => ({
|
|
807
|
+
x: a.y * b.z - a.z * b.y,
|
|
808
|
+
y: a.z * b.x - a.x * b.z,
|
|
809
|
+
z: a.x * b.y - a.y * b.x
|
|
810
|
+
}),
|
|
811
|
+
length: (v) => Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z),
|
|
812
|
+
lengthSq: (v) => v.x * v.x + v.y * v.y + v.z * v.z,
|
|
813
|
+
normalize: (v) => {
|
|
814
|
+
const len = vec3Math.length(v);
|
|
815
|
+
return len > EPSILON ? vec3Math.div(v, len) : vec3(0, 0, 0);
|
|
816
|
+
},
|
|
817
|
+
distance: (a, b) => vec3Math.length(vec3Math.sub(a, b)),
|
|
818
|
+
distanceSq: (a, b) => vec3Math.lengthSq(vec3Math.sub(a, b)),
|
|
819
|
+
lerp: (a, b, t) => ({
|
|
820
|
+
x: lerp2(a.x, b.x, t),
|
|
821
|
+
y: lerp2(a.y, b.y, t),
|
|
822
|
+
z: lerp2(a.z, b.z, t)
|
|
823
|
+
}),
|
|
824
|
+
slerp: (a, b, t) => {
|
|
825
|
+
const dot = clamp2(vec3Math.dot(a, b), -1, 1);
|
|
826
|
+
const theta = Math.acos(dot) * t;
|
|
827
|
+
const relative = vec3Math.normalize(vec3Math.sub(b, vec3Math.mul(a, dot)));
|
|
828
|
+
return vec3Math.add(vec3Math.mul(a, Math.cos(theta)), vec3Math.mul(relative, Math.sin(theta)));
|
|
829
|
+
},
|
|
830
|
+
negate: (v) => ({ x: -v.x, y: -v.y, z: -v.z }),
|
|
831
|
+
abs: (v) => ({ x: Math.abs(v.x), y: Math.abs(v.y), z: Math.abs(v.z) }),
|
|
832
|
+
min: (a, b) => ({
|
|
833
|
+
x: Math.min(a.x, b.x),
|
|
834
|
+
y: Math.min(a.y, b.y),
|
|
835
|
+
z: Math.min(a.z, b.z)
|
|
836
|
+
}),
|
|
837
|
+
max: (a, b) => ({
|
|
838
|
+
x: Math.max(a.x, b.x),
|
|
839
|
+
y: Math.max(a.y, b.y),
|
|
840
|
+
z: Math.max(a.z, b.z)
|
|
841
|
+
}),
|
|
842
|
+
clamp: (v, min, max) => ({
|
|
843
|
+
x: clamp2(v.x, min.x, max.x),
|
|
844
|
+
y: clamp2(v.y, min.y, max.y),
|
|
845
|
+
z: clamp2(v.z, min.z, max.z)
|
|
846
|
+
}),
|
|
847
|
+
floor: (v) => ({
|
|
848
|
+
x: Math.floor(v.x),
|
|
849
|
+
y: Math.floor(v.y),
|
|
850
|
+
z: Math.floor(v.z)
|
|
851
|
+
}),
|
|
852
|
+
ceil: (v) => ({
|
|
853
|
+
x: Math.ceil(v.x),
|
|
854
|
+
y: Math.ceil(v.y),
|
|
855
|
+
z: Math.ceil(v.z)
|
|
856
|
+
}),
|
|
857
|
+
round: (v) => ({
|
|
858
|
+
x: Math.round(v.x),
|
|
859
|
+
y: Math.round(v.y),
|
|
860
|
+
z: Math.round(v.z)
|
|
861
|
+
}),
|
|
862
|
+
reflect: (v, normal) => {
|
|
863
|
+
const dot = vec3Math.dot(v, normal);
|
|
864
|
+
return vec3Math.sub(v, vec3Math.mul(normal, 2 * dot));
|
|
865
|
+
},
|
|
866
|
+
project: (v, onto) => {
|
|
867
|
+
const dot = vec3Math.dot(v, onto);
|
|
868
|
+
const lenSq = vec3Math.lengthSq(onto);
|
|
869
|
+
return vec3Math.mul(onto, dot / lenSq);
|
|
870
|
+
},
|
|
871
|
+
angle: (a, b) => {
|
|
872
|
+
const dot = vec3Math.dot(vec3Math.normalize(a), vec3Math.normalize(b));
|
|
873
|
+
return Math.acos(clamp2(dot, -1, 1));
|
|
874
|
+
},
|
|
875
|
+
equals: (a, b, epsilon = EPSILON) => {
|
|
876
|
+
return Math.abs(a.x - b.x) < epsilon && Math.abs(a.y - b.y) < epsilon && Math.abs(a.z - b.z) < epsilon;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
var quatMath = {
|
|
880
|
+
identity: () => quat(0, 0, 0, 1),
|
|
881
|
+
fromAxisAngle: (axis, angle) => {
|
|
882
|
+
const halfAngle = angle / 2;
|
|
883
|
+
const s = Math.sin(halfAngle);
|
|
884
|
+
const normalized = vec3Math.normalize(axis);
|
|
885
|
+
return {
|
|
886
|
+
x: normalized.x * s,
|
|
887
|
+
y: normalized.y * s,
|
|
888
|
+
z: normalized.z * s,
|
|
889
|
+
w: Math.cos(halfAngle)
|
|
890
|
+
};
|
|
891
|
+
},
|
|
892
|
+
fromEuler: (euler) => {
|
|
893
|
+
const { x, y, z } = euler;
|
|
894
|
+
const c1 = Math.cos(degToRad2(x) / 2);
|
|
895
|
+
const c2 = Math.cos(degToRad2(y) / 2);
|
|
896
|
+
const c3 = Math.cos(degToRad2(z) / 2);
|
|
897
|
+
const s1 = Math.sin(degToRad2(x) / 2);
|
|
898
|
+
const s2 = Math.sin(degToRad2(y) / 2);
|
|
899
|
+
const s3 = Math.sin(degToRad2(z) / 2);
|
|
900
|
+
return {
|
|
901
|
+
x: s1 * c2 * c3 + c1 * s2 * s3,
|
|
902
|
+
y: c1 * s2 * c3 - s1 * c2 * s3,
|
|
903
|
+
z: c1 * c2 * s3 + s1 * s2 * c3,
|
|
904
|
+
w: c1 * c2 * c3 - s1 * s2 * s3
|
|
905
|
+
};
|
|
906
|
+
},
|
|
907
|
+
toEuler: (q) => {
|
|
908
|
+
const sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
|
|
909
|
+
const cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
|
|
910
|
+
const x = Math.atan2(sinr_cosp, cosr_cosp);
|
|
911
|
+
const sinp = 2 * (q.w * q.y - q.z * q.x);
|
|
912
|
+
let y;
|
|
913
|
+
if (Math.abs(sinp) >= 1) {
|
|
914
|
+
y = Math.PI / 2 * Math.sign(sinp);
|
|
915
|
+
} else {
|
|
916
|
+
y = Math.asin(sinp);
|
|
917
|
+
}
|
|
918
|
+
const siny_cosp = 2 * (q.w * q.z + q.x * q.y);
|
|
919
|
+
const cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
|
|
920
|
+
const z = Math.atan2(siny_cosp, cosy_cosp);
|
|
921
|
+
return {
|
|
922
|
+
x: radToDeg2(x),
|
|
923
|
+
y: radToDeg2(y),
|
|
924
|
+
z: radToDeg2(z)
|
|
925
|
+
};
|
|
926
|
+
},
|
|
927
|
+
multiply: (a, b) => ({
|
|
928
|
+
x: a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
|
929
|
+
y: a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
|
930
|
+
z: a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
|
931
|
+
w: a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z
|
|
932
|
+
}),
|
|
933
|
+
conjugate: (q) => ({ x: -q.x, y: -q.y, z: -q.z, w: q.w }),
|
|
934
|
+
inverse: (q) => {
|
|
935
|
+
const lenSq = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
|
|
936
|
+
return {
|
|
937
|
+
x: -q.x / lenSq,
|
|
938
|
+
y: -q.y / lenSq,
|
|
939
|
+
z: -q.z / lenSq,
|
|
940
|
+
w: q.w / lenSq
|
|
941
|
+
};
|
|
942
|
+
},
|
|
943
|
+
normalize: (q) => {
|
|
944
|
+
const len = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
|
945
|
+
return { x: q.x / len, y: q.y / len, z: q.z / len, w: q.w / len };
|
|
946
|
+
},
|
|
947
|
+
dot: (a, b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w,
|
|
948
|
+
slerp: (a, b, t) => {
|
|
949
|
+
let dot = quatMath.dot(a, b);
|
|
950
|
+
let bAdjusted = b;
|
|
951
|
+
if (dot < 0) {
|
|
952
|
+
bAdjusted = { x: -b.x, y: -b.y, z: -b.z, w: -b.w };
|
|
953
|
+
dot = -dot;
|
|
954
|
+
}
|
|
955
|
+
if (dot > 0.9995) {
|
|
956
|
+
return quatMath.normalize({
|
|
957
|
+
x: lerp2(a.x, bAdjusted.x, t),
|
|
958
|
+
y: lerp2(a.y, bAdjusted.y, t),
|
|
959
|
+
z: lerp2(a.z, bAdjusted.z, t),
|
|
960
|
+
w: lerp2(a.w, bAdjusted.w, t)
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
const theta0 = Math.acos(dot);
|
|
964
|
+
const theta = theta0 * t;
|
|
965
|
+
const sinTheta = Math.sin(theta);
|
|
966
|
+
const sinTheta0 = Math.sin(theta0);
|
|
967
|
+
const s0 = Math.cos(theta) - dot * sinTheta / sinTheta0;
|
|
968
|
+
const s1 = sinTheta / sinTheta0;
|
|
969
|
+
return {
|
|
970
|
+
x: a.x * s0 + bAdjusted.x * s1,
|
|
971
|
+
y: a.y * s0 + bAdjusted.y * s1,
|
|
972
|
+
z: a.z * s0 + bAdjusted.z * s1,
|
|
973
|
+
w: a.w * s0 + bAdjusted.w * s1
|
|
974
|
+
};
|
|
975
|
+
},
|
|
976
|
+
rotateVec3: (q, v) => {
|
|
977
|
+
const qv = { x: q.x, y: q.y, z: q.z };
|
|
978
|
+
const uv = vec3Math.cross(qv, v);
|
|
979
|
+
const uuv = vec3Math.cross(qv, uv);
|
|
980
|
+
return vec3Math.add(v, vec3Math.add(vec3Math.mul(uv, 2 * q.w), vec3Math.mul(uuv, 2)));
|
|
981
|
+
},
|
|
982
|
+
lookAt: (forward, up = vec3(0, 1, 0)) => {
|
|
983
|
+
const f = vec3Math.normalize(forward);
|
|
984
|
+
const r = vec3Math.normalize(vec3Math.cross(up, f));
|
|
985
|
+
const u = vec3Math.cross(f, r);
|
|
986
|
+
const trace = r.x + u.y + f.z;
|
|
987
|
+
let q;
|
|
988
|
+
if (trace > 0) {
|
|
989
|
+
const s = 0.5 / Math.sqrt(trace + 1);
|
|
990
|
+
q = {
|
|
991
|
+
w: 0.25 / s,
|
|
992
|
+
x: (u.z - f.y) * s,
|
|
993
|
+
y: (f.x - r.z) * s,
|
|
994
|
+
z: (r.y - u.x) * s
|
|
995
|
+
};
|
|
996
|
+
} else if (r.x > u.y && r.x > f.z) {
|
|
997
|
+
const s = 2 * Math.sqrt(1 + r.x - u.y - f.z);
|
|
998
|
+
q = {
|
|
999
|
+
w: (u.z - f.y) / s,
|
|
1000
|
+
x: 0.25 * s,
|
|
1001
|
+
y: (u.x + r.y) / s,
|
|
1002
|
+
z: (f.x + r.z) / s
|
|
1003
|
+
};
|
|
1004
|
+
} else if (u.y > f.z) {
|
|
1005
|
+
const s = 2 * Math.sqrt(1 + u.y - r.x - f.z);
|
|
1006
|
+
q = {
|
|
1007
|
+
w: (f.x - r.z) / s,
|
|
1008
|
+
x: (u.x + r.y) / s,
|
|
1009
|
+
y: 0.25 * s,
|
|
1010
|
+
z: (f.y + u.z) / s
|
|
1011
|
+
};
|
|
1012
|
+
} else {
|
|
1013
|
+
const s = 2 * Math.sqrt(1 + f.z - r.x - u.y);
|
|
1014
|
+
q = {
|
|
1015
|
+
w: (r.y - u.x) / s,
|
|
1016
|
+
x: (f.x + r.z) / s,
|
|
1017
|
+
y: (f.y + u.z) / s,
|
|
1018
|
+
z: 0.25 * s
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
return quatMath.normalize(q);
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
var aabbMath = {
|
|
1025
|
+
contains: (aabb2, point) => {
|
|
1026
|
+
return point.x >= aabb2.min.x && point.x <= aabb2.max.x && point.y >= aabb2.min.y && point.y <= aabb2.max.y && point.z >= aabb2.min.z && point.z <= aabb2.max.z;
|
|
1027
|
+
},
|
|
1028
|
+
intersects: (a, b) => {
|
|
1029
|
+
return a.min.x <= b.max.x && a.max.x >= b.min.x && a.min.y <= b.max.y && a.max.y >= b.min.y && a.min.z <= b.max.z && a.max.z >= b.min.z;
|
|
1030
|
+
},
|
|
1031
|
+
center: (aabb2) => vec3Math.mul(vec3Math.add(aabb2.min, aabb2.max), 0.5),
|
|
1032
|
+
size: (aabb2) => vec3Math.sub(aabb2.max, aabb2.min),
|
|
1033
|
+
expand: (aabb2, point) => ({
|
|
1034
|
+
min: vec3Math.min(aabb2.min, point),
|
|
1035
|
+
max: vec3Math.max(aabb2.max, point)
|
|
1036
|
+
}),
|
|
1037
|
+
merge: (a, b) => ({
|
|
1038
|
+
min: vec3Math.min(a.min, b.min),
|
|
1039
|
+
max: vec3Math.max(a.max, b.max)
|
|
1040
|
+
})
|
|
1041
|
+
};
|
|
1042
|
+
var perm = new Uint8Array(512);
|
|
1043
|
+
for (let i = 0; i < 256; i++) perm[i] = perm[i + 256] = i;
|
|
1044
|
+
for (let i = 255; i > 0; i--) {
|
|
1045
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1046
|
+
[perm[i], perm[j]] = [perm[j], perm[i]];
|
|
1047
|
+
perm[i + 256] = perm[i];
|
|
1048
|
+
}
|
|
1049
|
+
function fade(t) {
|
|
1050
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
1051
|
+
}
|
|
1052
|
+
function grad(hash, x, y, z) {
|
|
1053
|
+
const h = hash & 15;
|
|
1054
|
+
const u = h < 8 ? x : y;
|
|
1055
|
+
const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
|
|
1056
|
+
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
|
|
1057
|
+
}
|
|
1058
|
+
var noise = {
|
|
1059
|
+
/**
|
|
1060
|
+
* 3D Perlin noise (-1 to 1)
|
|
1061
|
+
*/
|
|
1062
|
+
perlin3d: (x, y, z) => {
|
|
1063
|
+
const X = Math.floor(x) & 255;
|
|
1064
|
+
const Y = Math.floor(y) & 255;
|
|
1065
|
+
const Z = Math.floor(z) & 255;
|
|
1066
|
+
x -= Math.floor(x);
|
|
1067
|
+
y -= Math.floor(y);
|
|
1068
|
+
z -= Math.floor(z);
|
|
1069
|
+
const u = fade(x);
|
|
1070
|
+
const v = fade(y);
|
|
1071
|
+
const w = fade(z);
|
|
1072
|
+
const A = perm[X] + Y;
|
|
1073
|
+
const AA = perm[A] + Z;
|
|
1074
|
+
const AB = perm[A + 1] + Z;
|
|
1075
|
+
const B = perm[X + 1] + Y;
|
|
1076
|
+
const BA = perm[B] + Z;
|
|
1077
|
+
const BB = perm[B + 1] + Z;
|
|
1078
|
+
return lerp2(
|
|
1079
|
+
lerp2(
|
|
1080
|
+
lerp2(grad(perm[AA], x, y, z), grad(perm[BA], x - 1, y, z), u),
|
|
1081
|
+
lerp2(grad(perm[AB], x, y - 1, z), grad(perm[BB], x - 1, y - 1, z), u),
|
|
1082
|
+
v
|
|
1083
|
+
),
|
|
1084
|
+
lerp2(
|
|
1085
|
+
lerp2(grad(perm[AA + 1], x, y, z - 1), grad(perm[BA + 1], x - 1, y, z - 1), u),
|
|
1086
|
+
lerp2(grad(perm[AB + 1], x, y - 1, z - 1), grad(perm[BB + 1], x - 1, y - 1, z - 1), u),
|
|
1087
|
+
v
|
|
1088
|
+
),
|
|
1089
|
+
w
|
|
1090
|
+
);
|
|
1091
|
+
},
|
|
1092
|
+
/**
|
|
1093
|
+
* 2D Perlin noise (-1 to 1)
|
|
1094
|
+
*/
|
|
1095
|
+
perlin2d: (x, y) => {
|
|
1096
|
+
return noise.perlin3d(x, y, 0);
|
|
1097
|
+
},
|
|
1098
|
+
/**
|
|
1099
|
+
* Fractal Brownian Motion (fBm)
|
|
1100
|
+
*/
|
|
1101
|
+
fbm: (x, y, z, octaves = 6, lacunarity = 2, gain = 0.5) => {
|
|
1102
|
+
let value = 0;
|
|
1103
|
+
let amplitude = 1;
|
|
1104
|
+
let frequency = 1;
|
|
1105
|
+
let maxValue = 0;
|
|
1106
|
+
for (let i = 0; i < octaves; i++) {
|
|
1107
|
+
value += amplitude * noise.perlin3d(x * frequency, y * frequency, z * frequency);
|
|
1108
|
+
maxValue += amplitude;
|
|
1109
|
+
amplitude *= gain;
|
|
1110
|
+
frequency *= lacunarity;
|
|
1111
|
+
}
|
|
1112
|
+
return value / maxValue;
|
|
1113
|
+
},
|
|
1114
|
+
/**
|
|
1115
|
+
* Simplex-like noise (approximation using perlin)
|
|
1116
|
+
*/
|
|
1117
|
+
simplex: (x, y) => {
|
|
1118
|
+
return noise.perlin2d(x * 1.2, y * 1.2);
|
|
1119
|
+
},
|
|
1120
|
+
/**
|
|
1121
|
+
* Worley/Cellular noise
|
|
1122
|
+
*/
|
|
1123
|
+
worley: (x, y, z) => {
|
|
1124
|
+
const xi = Math.floor(x);
|
|
1125
|
+
const yi = Math.floor(y);
|
|
1126
|
+
const zi = Math.floor(z);
|
|
1127
|
+
let minDist = Infinity;
|
|
1128
|
+
for (let i = -1; i <= 1; i++) {
|
|
1129
|
+
for (let j = -1; j <= 1; j++) {
|
|
1130
|
+
for (let k = -1; k <= 1; k++) {
|
|
1131
|
+
const cellX = xi + i;
|
|
1132
|
+
const cellY = yi + j;
|
|
1133
|
+
const cellZ = zi + k;
|
|
1134
|
+
const hash = perm[perm[perm[cellX & 255] + cellY & 255] + cellZ & 255];
|
|
1135
|
+
const px = cellX + hash / 255;
|
|
1136
|
+
const py = cellY + hash * 7 % 255 / 255;
|
|
1137
|
+
const pz = cellZ + hash * 13 % 255 / 255;
|
|
1138
|
+
const dist = (x - px) * (x - px) + (y - py) * (y - py) + (z - pz) * (z - pz);
|
|
1139
|
+
minDist = Math.min(minDist, dist);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return Math.sqrt(minDist);
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
var random = {
|
|
1147
|
+
/**
|
|
1148
|
+
* Random float between 0 and 1
|
|
1149
|
+
*/
|
|
1150
|
+
float: () => Math.random(),
|
|
1151
|
+
/**
|
|
1152
|
+
* Random float in range [min, max)
|
|
1153
|
+
*/
|
|
1154
|
+
range: (min, max) => min + Math.random() * (max - min),
|
|
1155
|
+
/**
|
|
1156
|
+
* Random integer in range [min, max]
|
|
1157
|
+
*/
|
|
1158
|
+
int: (min, max) => Math.floor(min + Math.random() * (max - min + 1)),
|
|
1159
|
+
/**
|
|
1160
|
+
* Random boolean with probability
|
|
1161
|
+
*/
|
|
1162
|
+
bool: (probability = 0.5) => Math.random() < probability,
|
|
1163
|
+
/**
|
|
1164
|
+
* Random element from array
|
|
1165
|
+
*/
|
|
1166
|
+
pick: (array) => array[Math.floor(Math.random() * array.length)],
|
|
1167
|
+
/**
|
|
1168
|
+
* Shuffle array (Fisher-Yates)
|
|
1169
|
+
*/
|
|
1170
|
+
shuffle: (array) => {
|
|
1171
|
+
const result = [...array];
|
|
1172
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
1173
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1174
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
1175
|
+
}
|
|
1176
|
+
return result;
|
|
1177
|
+
},
|
|
1178
|
+
/**
|
|
1179
|
+
* Random Vec3 in unit sphere
|
|
1180
|
+
*/
|
|
1181
|
+
insideUnitSphere: () => {
|
|
1182
|
+
const theta = Math.random() * TAU;
|
|
1183
|
+
const phi = Math.acos(2 * Math.random() - 1);
|
|
1184
|
+
const r = Math.cbrt(Math.random());
|
|
1185
|
+
return {
|
|
1186
|
+
x: r * Math.sin(phi) * Math.cos(theta),
|
|
1187
|
+
y: r * Math.sin(phi) * Math.sin(theta),
|
|
1188
|
+
z: r * Math.cos(phi)
|
|
1189
|
+
};
|
|
1190
|
+
},
|
|
1191
|
+
/**
|
|
1192
|
+
* Random Vec3 on unit sphere surface
|
|
1193
|
+
*/
|
|
1194
|
+
onUnitSphere: () => {
|
|
1195
|
+
const theta = Math.random() * TAU;
|
|
1196
|
+
const phi = Math.acos(2 * Math.random() - 1);
|
|
1197
|
+
return {
|
|
1198
|
+
x: Math.sin(phi) * Math.cos(theta),
|
|
1199
|
+
y: Math.sin(phi) * Math.sin(theta),
|
|
1200
|
+
z: Math.cos(phi)
|
|
1201
|
+
};
|
|
1202
|
+
},
|
|
1203
|
+
/**
|
|
1204
|
+
* Random Vec2 in unit circle
|
|
1205
|
+
*/
|
|
1206
|
+
insideUnitCircle: () => {
|
|
1207
|
+
const theta = Math.random() * TAU;
|
|
1208
|
+
const r = Math.sqrt(Math.random());
|
|
1209
|
+
return {
|
|
1210
|
+
x: r * Math.cos(theta),
|
|
1211
|
+
y: r * Math.sin(theta)
|
|
1212
|
+
};
|
|
1213
|
+
},
|
|
1214
|
+
/**
|
|
1215
|
+
* Seeded random number generator
|
|
1216
|
+
*/
|
|
1217
|
+
seeded: (seed) => {
|
|
1218
|
+
let s = seed;
|
|
1219
|
+
return () => {
|
|
1220
|
+
s = s * 1103515245 + 12345 & 2147483647;
|
|
1221
|
+
return s / 2147483647;
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
// src/collections.ts
|
|
1227
|
+
var List = class _List {
|
|
1228
|
+
items;
|
|
1229
|
+
constructor(items = []) {
|
|
1230
|
+
this.items = Array.from(items);
|
|
1231
|
+
}
|
|
1232
|
+
static of(...items) {
|
|
1233
|
+
return new _List(items);
|
|
1234
|
+
}
|
|
1235
|
+
static from(iterable) {
|
|
1236
|
+
return new _List(iterable);
|
|
1237
|
+
}
|
|
1238
|
+
static range(start, end, step2 = 1) {
|
|
1239
|
+
const items = [];
|
|
1240
|
+
for (let i = start; step2 > 0 ? i < end : i > end; i += step2) {
|
|
1241
|
+
items.push(i);
|
|
1242
|
+
}
|
|
1243
|
+
return new _List(items);
|
|
1244
|
+
}
|
|
1245
|
+
static repeat(value, count2) {
|
|
1246
|
+
return new _List(Array(count2).fill(value));
|
|
1247
|
+
}
|
|
1248
|
+
get length() {
|
|
1249
|
+
return this.items.length;
|
|
1250
|
+
}
|
|
1251
|
+
get isEmpty() {
|
|
1252
|
+
return this.items.length === 0;
|
|
1253
|
+
}
|
|
1254
|
+
get(index) {
|
|
1255
|
+
return index < 0 ? this.items[this.items.length + index] : this.items[index];
|
|
1256
|
+
}
|
|
1257
|
+
first() {
|
|
1258
|
+
return this.items[0];
|
|
1259
|
+
}
|
|
1260
|
+
last() {
|
|
1261
|
+
return this.items[this.items.length - 1];
|
|
1262
|
+
}
|
|
1263
|
+
map(fn) {
|
|
1264
|
+
return new _List(this.items.map(fn));
|
|
1265
|
+
}
|
|
1266
|
+
flatMap(fn) {
|
|
1267
|
+
const result = [];
|
|
1268
|
+
this.items.forEach((item, index) => {
|
|
1269
|
+
const mapped = fn(item, index);
|
|
1270
|
+
if (mapped instanceof _List) {
|
|
1271
|
+
result.push(...mapped.toArray());
|
|
1272
|
+
} else {
|
|
1273
|
+
result.push(...mapped);
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
return new _List(result);
|
|
1277
|
+
}
|
|
1278
|
+
filter(predicate) {
|
|
1279
|
+
return new _List(this.items.filter(predicate));
|
|
1280
|
+
}
|
|
1281
|
+
reject(predicate) {
|
|
1282
|
+
return this.filter((item, index) => !predicate(item, index));
|
|
1283
|
+
}
|
|
1284
|
+
reduce(fn, initial) {
|
|
1285
|
+
return this.items.reduce(fn, initial);
|
|
1286
|
+
}
|
|
1287
|
+
fold(initial, fn) {
|
|
1288
|
+
return this.items.reduce(fn, initial);
|
|
1289
|
+
}
|
|
1290
|
+
find(predicate) {
|
|
1291
|
+
return this.items.find(predicate);
|
|
1292
|
+
}
|
|
1293
|
+
findIndex(predicate) {
|
|
1294
|
+
return this.items.findIndex(predicate);
|
|
1295
|
+
}
|
|
1296
|
+
indexOf(item) {
|
|
1297
|
+
return this.items.indexOf(item);
|
|
1298
|
+
}
|
|
1299
|
+
includes(item) {
|
|
1300
|
+
return this.items.includes(item);
|
|
1301
|
+
}
|
|
1302
|
+
some(predicate) {
|
|
1303
|
+
return this.items.some(predicate);
|
|
1304
|
+
}
|
|
1305
|
+
every(predicate) {
|
|
1306
|
+
return this.items.every(predicate);
|
|
1307
|
+
}
|
|
1308
|
+
none(predicate) {
|
|
1309
|
+
return !this.some(predicate);
|
|
1310
|
+
}
|
|
1311
|
+
count(predicate) {
|
|
1312
|
+
return this.filter(predicate).length;
|
|
1313
|
+
}
|
|
1314
|
+
forEach(fn) {
|
|
1315
|
+
this.items.forEach(fn);
|
|
1316
|
+
}
|
|
1317
|
+
concat(...lists) {
|
|
1318
|
+
const arrays = lists.map((l) => l instanceof _List ? l.toArray() : l);
|
|
1319
|
+
return new _List([...this.items, ...arrays.flat()]);
|
|
1320
|
+
}
|
|
1321
|
+
append(item) {
|
|
1322
|
+
return new _List([...this.items, item]);
|
|
1323
|
+
}
|
|
1324
|
+
prepend(item) {
|
|
1325
|
+
return new _List([item, ...this.items]);
|
|
1326
|
+
}
|
|
1327
|
+
insert(index, item) {
|
|
1328
|
+
const arr = [...this.items];
|
|
1329
|
+
arr.splice(index, 0, item);
|
|
1330
|
+
return new _List(arr);
|
|
1331
|
+
}
|
|
1332
|
+
remove(index) {
|
|
1333
|
+
const arr = [...this.items];
|
|
1334
|
+
arr.splice(index, 1);
|
|
1335
|
+
return new _List(arr);
|
|
1336
|
+
}
|
|
1337
|
+
update(index, item) {
|
|
1338
|
+
const arr = [...this.items];
|
|
1339
|
+
arr[index] = item;
|
|
1340
|
+
return new _List(arr);
|
|
1341
|
+
}
|
|
1342
|
+
slice(start, end) {
|
|
1343
|
+
return new _List(this.items.slice(start, end));
|
|
1344
|
+
}
|
|
1345
|
+
take(n) {
|
|
1346
|
+
return this.slice(0, n);
|
|
1347
|
+
}
|
|
1348
|
+
takeLast(n) {
|
|
1349
|
+
return this.slice(-n);
|
|
1350
|
+
}
|
|
1351
|
+
takeWhile(predicate) {
|
|
1352
|
+
const result = [];
|
|
1353
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
1354
|
+
if (!predicate(this.items[i], i)) break;
|
|
1355
|
+
result.push(this.items[i]);
|
|
1356
|
+
}
|
|
1357
|
+
return new _List(result);
|
|
1358
|
+
}
|
|
1359
|
+
drop(n) {
|
|
1360
|
+
return this.slice(n);
|
|
1361
|
+
}
|
|
1362
|
+
dropLast(n) {
|
|
1363
|
+
return this.slice(0, -n);
|
|
1364
|
+
}
|
|
1365
|
+
dropWhile(predicate) {
|
|
1366
|
+
let startIndex = 0;
|
|
1367
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
1368
|
+
if (!predicate(this.items[i], i)) break;
|
|
1369
|
+
startIndex = i + 1;
|
|
1370
|
+
}
|
|
1371
|
+
return this.slice(startIndex);
|
|
1372
|
+
}
|
|
1373
|
+
reverse() {
|
|
1374
|
+
return new _List([...this.items].reverse());
|
|
1375
|
+
}
|
|
1376
|
+
sort(compareFn) {
|
|
1377
|
+
return new _List([...this.items].sort(compareFn));
|
|
1378
|
+
}
|
|
1379
|
+
sortBy(fn) {
|
|
1380
|
+
return this.sort((a, b) => {
|
|
1381
|
+
const va = fn(a);
|
|
1382
|
+
const vb = fn(b);
|
|
1383
|
+
if (va < vb) return -1;
|
|
1384
|
+
if (va > vb) return 1;
|
|
1385
|
+
return 0;
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
shuffle() {
|
|
1389
|
+
const arr = [...this.items];
|
|
1390
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
1391
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1392
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
1393
|
+
}
|
|
1394
|
+
return new _List(arr);
|
|
1395
|
+
}
|
|
1396
|
+
unique() {
|
|
1397
|
+
return new _List([...new Set(this.items)]);
|
|
1398
|
+
}
|
|
1399
|
+
uniqueBy(fn) {
|
|
1400
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1401
|
+
const result = [];
|
|
1402
|
+
for (const item of this.items) {
|
|
1403
|
+
const key = fn(item);
|
|
1404
|
+
if (!seen.has(key)) {
|
|
1405
|
+
seen.add(key);
|
|
1406
|
+
result.push(item);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
return new _List(result);
|
|
1410
|
+
}
|
|
1411
|
+
groupBy(fn) {
|
|
1412
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1413
|
+
for (const item of this.items) {
|
|
1414
|
+
const key = fn(item);
|
|
1415
|
+
const group = groups.get(key) || [];
|
|
1416
|
+
group.push(item);
|
|
1417
|
+
groups.set(key, group);
|
|
1418
|
+
}
|
|
1419
|
+
const result = /* @__PURE__ */ new Map();
|
|
1420
|
+
for (const [key, items] of groups) {
|
|
1421
|
+
result.set(key, new _List(items));
|
|
1422
|
+
}
|
|
1423
|
+
return new HoloMap(result);
|
|
1424
|
+
}
|
|
1425
|
+
partition(predicate) {
|
|
1426
|
+
const pass = [];
|
|
1427
|
+
const fail = [];
|
|
1428
|
+
this.items.forEach((item, index) => {
|
|
1429
|
+
if (predicate(item, index)) {
|
|
1430
|
+
pass.push(item);
|
|
1431
|
+
} else {
|
|
1432
|
+
fail.push(item);
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
return [new _List(pass), new _List(fail)];
|
|
1436
|
+
}
|
|
1437
|
+
zip(other) {
|
|
1438
|
+
const length = Math.min(this.length, other.length);
|
|
1439
|
+
const result = [];
|
|
1440
|
+
for (let i = 0; i < length; i++) {
|
|
1441
|
+
result.push([this.items[i], other.get(i)]);
|
|
1442
|
+
}
|
|
1443
|
+
return new _List(result);
|
|
1444
|
+
}
|
|
1445
|
+
zipWith(other, fn) {
|
|
1446
|
+
return this.zip(other).map(([a, b]) => fn(a, b));
|
|
1447
|
+
}
|
|
1448
|
+
flatten() {
|
|
1449
|
+
const result = [];
|
|
1450
|
+
for (const item of this.items) {
|
|
1451
|
+
if (item instanceof _List) {
|
|
1452
|
+
result.push(...item.toArray());
|
|
1453
|
+
} else if (Array.isArray(item)) {
|
|
1454
|
+
result.push(...item);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
return new _List(result);
|
|
1458
|
+
}
|
|
1459
|
+
sum() {
|
|
1460
|
+
return this.reduce((acc, n) => acc + n, 0);
|
|
1461
|
+
}
|
|
1462
|
+
min() {
|
|
1463
|
+
if (this.isEmpty) return void 0;
|
|
1464
|
+
return Math.min(...this.items);
|
|
1465
|
+
}
|
|
1466
|
+
max() {
|
|
1467
|
+
if (this.isEmpty) return void 0;
|
|
1468
|
+
return Math.max(...this.items);
|
|
1469
|
+
}
|
|
1470
|
+
average() {
|
|
1471
|
+
if (this.isEmpty) return void 0;
|
|
1472
|
+
return this.sum() / this.length;
|
|
1473
|
+
}
|
|
1474
|
+
join(separator = ",") {
|
|
1475
|
+
return this.items.join(separator);
|
|
1476
|
+
}
|
|
1477
|
+
toArray() {
|
|
1478
|
+
return [...this.items];
|
|
1479
|
+
}
|
|
1480
|
+
toSet() {
|
|
1481
|
+
return new HoloSet(this.items);
|
|
1482
|
+
}
|
|
1483
|
+
[Symbol.iterator]() {
|
|
1484
|
+
return this.items[Symbol.iterator]();
|
|
1485
|
+
}
|
|
1486
|
+
toString() {
|
|
1487
|
+
return `List(${this.items.join(", ")})`;
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
var HoloMap = class _HoloMap {
|
|
1491
|
+
_map;
|
|
1492
|
+
constructor(entries) {
|
|
1493
|
+
this._map = entries instanceof Map ? entries : new Map(entries);
|
|
1494
|
+
}
|
|
1495
|
+
static of(...entries) {
|
|
1496
|
+
return new _HoloMap(entries);
|
|
1497
|
+
}
|
|
1498
|
+
static from(entries) {
|
|
1499
|
+
return new _HoloMap(entries);
|
|
1500
|
+
}
|
|
1501
|
+
static fromObject(obj) {
|
|
1502
|
+
return new _HoloMap(Object.entries(obj));
|
|
1503
|
+
}
|
|
1504
|
+
get size() {
|
|
1505
|
+
return this._map.size;
|
|
1506
|
+
}
|
|
1507
|
+
get isEmpty() {
|
|
1508
|
+
return this._map.size === 0;
|
|
1509
|
+
}
|
|
1510
|
+
get(key) {
|
|
1511
|
+
return this._map.get(key);
|
|
1512
|
+
}
|
|
1513
|
+
getOrDefault(key, defaultValue) {
|
|
1514
|
+
return this._map.has(key) ? this._map.get(key) : defaultValue;
|
|
1515
|
+
}
|
|
1516
|
+
has(key) {
|
|
1517
|
+
return this._map.has(key);
|
|
1518
|
+
}
|
|
1519
|
+
set(key, value) {
|
|
1520
|
+
const newMap = new Map(this._map);
|
|
1521
|
+
newMap.set(key, value);
|
|
1522
|
+
return new _HoloMap(newMap);
|
|
1523
|
+
}
|
|
1524
|
+
delete(key) {
|
|
1525
|
+
const newMap = new Map(this._map);
|
|
1526
|
+
newMap.delete(key);
|
|
1527
|
+
return new _HoloMap(newMap);
|
|
1528
|
+
}
|
|
1529
|
+
update(key, fn) {
|
|
1530
|
+
return this.set(key, fn(this.get(key)));
|
|
1531
|
+
}
|
|
1532
|
+
merge(other) {
|
|
1533
|
+
const newMap = new Map(this._map);
|
|
1534
|
+
for (const [key, value] of other) {
|
|
1535
|
+
newMap.set(key, value);
|
|
1536
|
+
}
|
|
1537
|
+
return new _HoloMap(newMap);
|
|
1538
|
+
}
|
|
1539
|
+
keys() {
|
|
1540
|
+
return new List(this._map.keys());
|
|
1541
|
+
}
|
|
1542
|
+
values() {
|
|
1543
|
+
return new List(this._map.values());
|
|
1544
|
+
}
|
|
1545
|
+
entries() {
|
|
1546
|
+
return new List(this._map.entries());
|
|
1547
|
+
}
|
|
1548
|
+
map(fn) {
|
|
1549
|
+
const entries = [];
|
|
1550
|
+
for (const [key, value] of this._map) {
|
|
1551
|
+
entries.push([key, fn(value, key)]);
|
|
1552
|
+
}
|
|
1553
|
+
return new _HoloMap(entries);
|
|
1554
|
+
}
|
|
1555
|
+
mapKeys(fn) {
|
|
1556
|
+
const entries = [];
|
|
1557
|
+
for (const [key, value] of this._map) {
|
|
1558
|
+
entries.push([fn(key, value), value]);
|
|
1559
|
+
}
|
|
1560
|
+
return new _HoloMap(entries);
|
|
1561
|
+
}
|
|
1562
|
+
filter(predicate) {
|
|
1563
|
+
const entries = [];
|
|
1564
|
+
for (const [key, value] of this._map) {
|
|
1565
|
+
if (predicate(value, key)) {
|
|
1566
|
+
entries.push([key, value]);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
return new _HoloMap(entries);
|
|
1570
|
+
}
|
|
1571
|
+
reduce(fn, initial) {
|
|
1572
|
+
let acc = initial;
|
|
1573
|
+
for (const [key, value] of this._map) {
|
|
1574
|
+
acc = fn(acc, value, key);
|
|
1575
|
+
}
|
|
1576
|
+
return acc;
|
|
1577
|
+
}
|
|
1578
|
+
forEach(fn) {
|
|
1579
|
+
this._map.forEach(fn);
|
|
1580
|
+
}
|
|
1581
|
+
find(predicate) {
|
|
1582
|
+
for (const [key, value] of this._map) {
|
|
1583
|
+
if (predicate(value, key)) {
|
|
1584
|
+
return [key, value];
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
return void 0;
|
|
1588
|
+
}
|
|
1589
|
+
some(predicate) {
|
|
1590
|
+
for (const [key, value] of this._map) {
|
|
1591
|
+
if (predicate(value, key)) return true;
|
|
1592
|
+
}
|
|
1593
|
+
return false;
|
|
1594
|
+
}
|
|
1595
|
+
every(predicate) {
|
|
1596
|
+
for (const [key, value] of this._map) {
|
|
1597
|
+
if (!predicate(value, key)) return false;
|
|
1598
|
+
}
|
|
1599
|
+
return true;
|
|
1600
|
+
}
|
|
1601
|
+
toObject() {
|
|
1602
|
+
const obj = {};
|
|
1603
|
+
for (const [key, value] of this._map) {
|
|
1604
|
+
obj[key] = value;
|
|
1605
|
+
}
|
|
1606
|
+
return obj;
|
|
1607
|
+
}
|
|
1608
|
+
toNativeMap() {
|
|
1609
|
+
return new Map(this._map);
|
|
1610
|
+
}
|
|
1611
|
+
[Symbol.iterator]() {
|
|
1612
|
+
return this._map[Symbol.iterator]();
|
|
1613
|
+
}
|
|
1614
|
+
toString() {
|
|
1615
|
+
const entries = [...this._map].map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
1616
|
+
return `HoloMap { ${entries} }`;
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
var HoloSet = class _HoloSet {
|
|
1620
|
+
set;
|
|
1621
|
+
constructor(items) {
|
|
1622
|
+
this.set = new Set(items);
|
|
1623
|
+
}
|
|
1624
|
+
static of(...items) {
|
|
1625
|
+
return new _HoloSet(items);
|
|
1626
|
+
}
|
|
1627
|
+
static from(iterable) {
|
|
1628
|
+
return new _HoloSet(iterable);
|
|
1629
|
+
}
|
|
1630
|
+
get size() {
|
|
1631
|
+
return this.set.size;
|
|
1632
|
+
}
|
|
1633
|
+
get isEmpty() {
|
|
1634
|
+
return this.set.size === 0;
|
|
1635
|
+
}
|
|
1636
|
+
has(item) {
|
|
1637
|
+
return this.set.has(item);
|
|
1638
|
+
}
|
|
1639
|
+
add(item) {
|
|
1640
|
+
const newSet = new Set(this.set);
|
|
1641
|
+
newSet.add(item);
|
|
1642
|
+
return new _HoloSet(newSet);
|
|
1643
|
+
}
|
|
1644
|
+
delete(item) {
|
|
1645
|
+
const newSet = new Set(this.set);
|
|
1646
|
+
newSet.delete(item);
|
|
1647
|
+
return new _HoloSet(newSet);
|
|
1648
|
+
}
|
|
1649
|
+
addAll(items) {
|
|
1650
|
+
const newSet = new Set(this.set);
|
|
1651
|
+
for (const item of items) {
|
|
1652
|
+
newSet.add(item);
|
|
1653
|
+
}
|
|
1654
|
+
return new _HoloSet(newSet);
|
|
1655
|
+
}
|
|
1656
|
+
union(other) {
|
|
1657
|
+
return this.addAll(other);
|
|
1658
|
+
}
|
|
1659
|
+
intersection(other) {
|
|
1660
|
+
const result = /* @__PURE__ */ new Set();
|
|
1661
|
+
for (const item of this.set) {
|
|
1662
|
+
if (other.has(item)) {
|
|
1663
|
+
result.add(item);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
return new _HoloSet(result);
|
|
1667
|
+
}
|
|
1668
|
+
difference(other) {
|
|
1669
|
+
const result = /* @__PURE__ */ new Set();
|
|
1670
|
+
for (const item of this.set) {
|
|
1671
|
+
if (!other.has(item)) {
|
|
1672
|
+
result.add(item);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
return new _HoloSet(result);
|
|
1676
|
+
}
|
|
1677
|
+
symmetricDifference(other) {
|
|
1678
|
+
return this.difference(other).union(other.difference(this));
|
|
1679
|
+
}
|
|
1680
|
+
isSubsetOf(other) {
|
|
1681
|
+
for (const item of this.set) {
|
|
1682
|
+
if (!other.has(item)) return false;
|
|
1683
|
+
}
|
|
1684
|
+
return true;
|
|
1685
|
+
}
|
|
1686
|
+
isSupersetOf(other) {
|
|
1687
|
+
return other.isSubsetOf(this);
|
|
1688
|
+
}
|
|
1689
|
+
isDisjointFrom(other) {
|
|
1690
|
+
return this.intersection(other).isEmpty;
|
|
1691
|
+
}
|
|
1692
|
+
map(fn) {
|
|
1693
|
+
const result = /* @__PURE__ */ new Set();
|
|
1694
|
+
for (const item of this.set) {
|
|
1695
|
+
result.add(fn(item));
|
|
1696
|
+
}
|
|
1697
|
+
return new _HoloSet(result);
|
|
1698
|
+
}
|
|
1699
|
+
filter(predicate) {
|
|
1700
|
+
const result = /* @__PURE__ */ new Set();
|
|
1701
|
+
for (const item of this.set) {
|
|
1702
|
+
if (predicate(item)) {
|
|
1703
|
+
result.add(item);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
return new _HoloSet(result);
|
|
1707
|
+
}
|
|
1708
|
+
reduce(fn, initial) {
|
|
1709
|
+
let acc = initial;
|
|
1710
|
+
for (const item of this.set) {
|
|
1711
|
+
acc = fn(acc, item);
|
|
1712
|
+
}
|
|
1713
|
+
return acc;
|
|
1714
|
+
}
|
|
1715
|
+
forEach(fn) {
|
|
1716
|
+
this.set.forEach(fn);
|
|
1717
|
+
}
|
|
1718
|
+
find(predicate) {
|
|
1719
|
+
for (const item of this.set) {
|
|
1720
|
+
if (predicate(item)) return item;
|
|
1721
|
+
}
|
|
1722
|
+
return void 0;
|
|
1723
|
+
}
|
|
1724
|
+
some(predicate) {
|
|
1725
|
+
for (const item of this.set) {
|
|
1726
|
+
if (predicate(item)) return true;
|
|
1727
|
+
}
|
|
1728
|
+
return false;
|
|
1729
|
+
}
|
|
1730
|
+
every(predicate) {
|
|
1731
|
+
for (const item of this.set) {
|
|
1732
|
+
if (!predicate(item)) return false;
|
|
1733
|
+
}
|
|
1734
|
+
return true;
|
|
1735
|
+
}
|
|
1736
|
+
toArray() {
|
|
1737
|
+
return [...this.set];
|
|
1738
|
+
}
|
|
1739
|
+
toList() {
|
|
1740
|
+
return new List(this.set);
|
|
1741
|
+
}
|
|
1742
|
+
toNativeSet() {
|
|
1743
|
+
return new Set(this.set);
|
|
1744
|
+
}
|
|
1745
|
+
[Symbol.iterator]() {
|
|
1746
|
+
return this.set[Symbol.iterator]();
|
|
1747
|
+
}
|
|
1748
|
+
toString() {
|
|
1749
|
+
return `HoloSet { ${[...this.set].join(", ")} }`;
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
var SpatialGrid = class {
|
|
1753
|
+
cellSize;
|
|
1754
|
+
cells = /* @__PURE__ */ new Map();
|
|
1755
|
+
constructor(cellSize) {
|
|
1756
|
+
this.cellSize = cellSize;
|
|
1757
|
+
}
|
|
1758
|
+
getCellKey(x, y, z) {
|
|
1759
|
+
const cx = Math.floor(x / this.cellSize);
|
|
1760
|
+
const cy = Math.floor(y / this.cellSize);
|
|
1761
|
+
const cz = Math.floor(z / this.cellSize);
|
|
1762
|
+
return `${cx},${cy},${cz}`;
|
|
1763
|
+
}
|
|
1764
|
+
insert(position, item) {
|
|
1765
|
+
const key = this.getCellKey(position.x, position.y, position.z);
|
|
1766
|
+
if (!this.cells.has(key)) {
|
|
1767
|
+
this.cells.set(key, []);
|
|
1768
|
+
}
|
|
1769
|
+
this.cells.get(key).push(item);
|
|
1770
|
+
}
|
|
1771
|
+
remove(position, item) {
|
|
1772
|
+
const key = this.getCellKey(position.x, position.y, position.z);
|
|
1773
|
+
const cell = this.cells.get(key);
|
|
1774
|
+
if (!cell) return false;
|
|
1775
|
+
const index = cell.indexOf(item);
|
|
1776
|
+
if (index === -1) return false;
|
|
1777
|
+
cell.splice(index, 1);
|
|
1778
|
+
if (cell.length === 0) {
|
|
1779
|
+
this.cells.delete(key);
|
|
1780
|
+
}
|
|
1781
|
+
return true;
|
|
1782
|
+
}
|
|
1783
|
+
query(center2, radius) {
|
|
1784
|
+
const results = [];
|
|
1785
|
+
const cellRadius = Math.ceil(radius / this.cellSize);
|
|
1786
|
+
const cx = Math.floor(center2.x / this.cellSize);
|
|
1787
|
+
const cy = Math.floor(center2.y / this.cellSize);
|
|
1788
|
+
const cz = Math.floor(center2.z / this.cellSize);
|
|
1789
|
+
for (let x = cx - cellRadius; x <= cx + cellRadius; x++) {
|
|
1790
|
+
for (let y = cy - cellRadius; y <= cy + cellRadius; y++) {
|
|
1791
|
+
for (let z = cz - cellRadius; z <= cz + cellRadius; z++) {
|
|
1792
|
+
const cell = this.cells.get(`${x},${y},${z}`);
|
|
1793
|
+
if (cell) {
|
|
1794
|
+
results.push(...cell);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return results;
|
|
1800
|
+
}
|
|
1801
|
+
queryBox(min, max) {
|
|
1802
|
+
const results = [];
|
|
1803
|
+
const minCx = Math.floor(min.x / this.cellSize);
|
|
1804
|
+
const minCy = Math.floor(min.y / this.cellSize);
|
|
1805
|
+
const minCz = Math.floor(min.z / this.cellSize);
|
|
1806
|
+
const maxCx = Math.floor(max.x / this.cellSize);
|
|
1807
|
+
const maxCy = Math.floor(max.y / this.cellSize);
|
|
1808
|
+
const maxCz = Math.floor(max.z / this.cellSize);
|
|
1809
|
+
for (let x = minCx; x <= maxCx; x++) {
|
|
1810
|
+
for (let y = minCy; y <= maxCy; y++) {
|
|
1811
|
+
for (let z = minCz; z <= maxCz; z++) {
|
|
1812
|
+
const cell = this.cells.get(`${x},${y},${z}`);
|
|
1813
|
+
if (cell) {
|
|
1814
|
+
results.push(...cell);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return results;
|
|
1820
|
+
}
|
|
1821
|
+
clear() {
|
|
1822
|
+
this.cells.clear();
|
|
1823
|
+
}
|
|
1824
|
+
get cellCount() {
|
|
1825
|
+
return this.cells.size;
|
|
1826
|
+
}
|
|
1827
|
+
get itemCount() {
|
|
1828
|
+
let count2 = 0;
|
|
1829
|
+
for (const cell of this.cells.values()) {
|
|
1830
|
+
count2 += cell.length;
|
|
1831
|
+
}
|
|
1832
|
+
return count2;
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
var PriorityQueue = class _PriorityQueue {
|
|
1836
|
+
constructor(compareFn = (a, b) => a - b) {
|
|
1837
|
+
this.compareFn = compareFn;
|
|
1838
|
+
}
|
|
1839
|
+
heap = [];
|
|
1840
|
+
static minHeap() {
|
|
1841
|
+
return new _PriorityQueue((a, b) => a - b);
|
|
1842
|
+
}
|
|
1843
|
+
static maxHeap() {
|
|
1844
|
+
return new _PriorityQueue((a, b) => b - a);
|
|
1845
|
+
}
|
|
1846
|
+
get size() {
|
|
1847
|
+
return this.heap.length;
|
|
1848
|
+
}
|
|
1849
|
+
get isEmpty() {
|
|
1850
|
+
return this.heap.length === 0;
|
|
1851
|
+
}
|
|
1852
|
+
enqueue(item, priority) {
|
|
1853
|
+
this.heap.push({ item, priority });
|
|
1854
|
+
this.bubbleUp(this.heap.length - 1);
|
|
1855
|
+
}
|
|
1856
|
+
dequeue() {
|
|
1857
|
+
if (this.isEmpty) return void 0;
|
|
1858
|
+
const result = this.heap[0].item;
|
|
1859
|
+
const last = this.heap.pop();
|
|
1860
|
+
if (this.heap.length > 0) {
|
|
1861
|
+
this.heap[0] = last;
|
|
1862
|
+
this.bubbleDown(0);
|
|
1863
|
+
}
|
|
1864
|
+
return result;
|
|
1865
|
+
}
|
|
1866
|
+
peek() {
|
|
1867
|
+
return this.heap[0]?.item;
|
|
1868
|
+
}
|
|
1869
|
+
peekPriority() {
|
|
1870
|
+
return this.heap[0]?.priority;
|
|
1871
|
+
}
|
|
1872
|
+
clear() {
|
|
1873
|
+
this.heap = [];
|
|
1874
|
+
}
|
|
1875
|
+
bubbleUp(index) {
|
|
1876
|
+
while (index > 0) {
|
|
1877
|
+
const parentIndex = Math.floor((index - 1) / 2);
|
|
1878
|
+
if (this.compareFn(this.heap[index].priority, this.heap[parentIndex].priority) >= 0) break;
|
|
1879
|
+
[this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]];
|
|
1880
|
+
index = parentIndex;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
bubbleDown(index) {
|
|
1884
|
+
while (true) {
|
|
1885
|
+
const leftChild = 2 * index + 1;
|
|
1886
|
+
const rightChild = 2 * index + 2;
|
|
1887
|
+
let smallest = index;
|
|
1888
|
+
if (leftChild < this.heap.length && this.compareFn(this.heap[leftChild].priority, this.heap[smallest].priority) < 0) {
|
|
1889
|
+
smallest = leftChild;
|
|
1890
|
+
}
|
|
1891
|
+
if (rightChild < this.heap.length && this.compareFn(this.heap[rightChild].priority, this.heap[smallest].priority) < 0) {
|
|
1892
|
+
smallest = rightChild;
|
|
1893
|
+
}
|
|
1894
|
+
if (smallest === index) break;
|
|
1895
|
+
[this.heap[index], this.heap[smallest]] = [this.heap[smallest], this.heap[index]];
|
|
1896
|
+
index = smallest;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1901
|
+
// src/string.ts
|
|
1902
|
+
function isBlank(s) {
|
|
1903
|
+
return s.trim().length === 0;
|
|
1904
|
+
}
|
|
1905
|
+
function isNotBlank(s) {
|
|
1906
|
+
return s.trim().length > 0;
|
|
1907
|
+
}
|
|
1908
|
+
function capitalize(s) {
|
|
1909
|
+
if (s.length === 0) return s;
|
|
1910
|
+
return s[0].toUpperCase() + s.slice(1);
|
|
1911
|
+
}
|
|
1912
|
+
function titleCase(s) {
|
|
1913
|
+
return s.split(/\s+/).map(capitalize).join(" ");
|
|
1914
|
+
}
|
|
1915
|
+
function camelCase(s) {
|
|
1916
|
+
return s.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
1917
|
+
}
|
|
1918
|
+
function pascalCase(s) {
|
|
1919
|
+
const camel = camelCase(s);
|
|
1920
|
+
return camel.length > 0 ? camel[0].toUpperCase() + camel.slice(1) : camel;
|
|
1921
|
+
}
|
|
1922
|
+
function snakeCase(s) {
|
|
1923
|
+
return s.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
|
|
1924
|
+
}
|
|
1925
|
+
function kebabCase(s) {
|
|
1926
|
+
return s.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").toLowerCase();
|
|
1927
|
+
}
|
|
1928
|
+
function constantCase(s) {
|
|
1929
|
+
return snakeCase(s).toUpperCase();
|
|
1930
|
+
}
|
|
1931
|
+
function padLeft(s, length, char = " ") {
|
|
1932
|
+
return s.padStart(length, char);
|
|
1933
|
+
}
|
|
1934
|
+
function padRight(s, length, char = " ") {
|
|
1935
|
+
return s.padEnd(length, char);
|
|
1936
|
+
}
|
|
1937
|
+
function center(s, length, char = " ") {
|
|
1938
|
+
if (s.length >= length) return s;
|
|
1939
|
+
const totalPadding = length - s.length;
|
|
1940
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
1941
|
+
const rightPadding = totalPadding - leftPadding;
|
|
1942
|
+
return char.repeat(leftPadding) + s + char.repeat(rightPadding);
|
|
1943
|
+
}
|
|
1944
|
+
function truncate(s, maxLength, ellipsis = "...") {
|
|
1945
|
+
if (s.length <= maxLength) return s;
|
|
1946
|
+
return s.slice(0, maxLength - ellipsis.length) + ellipsis;
|
|
1947
|
+
}
|
|
1948
|
+
function truncateMiddle(s, maxLength, ellipsis = "...") {
|
|
1949
|
+
if (s.length <= maxLength) return s;
|
|
1950
|
+
const availableLength = maxLength - ellipsis.length;
|
|
1951
|
+
const leftLength = Math.floor(availableLength / 2);
|
|
1952
|
+
const rightLength = Math.ceil(availableLength / 2);
|
|
1953
|
+
return s.slice(0, leftLength) + ellipsis + s.slice(-rightLength);
|
|
1954
|
+
}
|
|
1955
|
+
function repeat(s, count2) {
|
|
1956
|
+
return s.repeat(count2);
|
|
1957
|
+
}
|
|
1958
|
+
function reverse(s) {
|
|
1959
|
+
return [...s].reverse().join("");
|
|
1960
|
+
}
|
|
1961
|
+
function count(s, substring) {
|
|
1962
|
+
if (substring.length === 0) return 0;
|
|
1963
|
+
let count2 = 0;
|
|
1964
|
+
let pos = 0;
|
|
1965
|
+
while ((pos = s.indexOf(substring, pos)) !== -1) {
|
|
1966
|
+
count2++;
|
|
1967
|
+
pos += 1;
|
|
1968
|
+
}
|
|
1969
|
+
return count2;
|
|
1970
|
+
}
|
|
1971
|
+
function containsIgnoreCase(s, substring) {
|
|
1972
|
+
return s.toLowerCase().includes(substring.toLowerCase());
|
|
1973
|
+
}
|
|
1974
|
+
function startsWithIgnoreCase(s, prefix) {
|
|
1975
|
+
return s.toLowerCase().startsWith(prefix.toLowerCase());
|
|
1976
|
+
}
|
|
1977
|
+
function endsWithIgnoreCase(s, suffix) {
|
|
1978
|
+
return s.toLowerCase().endsWith(suffix.toLowerCase());
|
|
1979
|
+
}
|
|
1980
|
+
function removeWhitespace(s) {
|
|
1981
|
+
return s.replace(/\s+/g, "");
|
|
1982
|
+
}
|
|
1983
|
+
function collapseWhitespace(s) {
|
|
1984
|
+
return s.replace(/\s+/g, " ").trim();
|
|
1985
|
+
}
|
|
1986
|
+
function removePrefix(s, prefix) {
|
|
1987
|
+
return s.startsWith(prefix) ? s.slice(prefix.length) : s;
|
|
1988
|
+
}
|
|
1989
|
+
function removeSuffix(s, suffix) {
|
|
1990
|
+
return s.endsWith(suffix) ? s.slice(0, -suffix.length) : s;
|
|
1991
|
+
}
|
|
1992
|
+
function wrap(s, prefixOrWrapper, suffix) {
|
|
1993
|
+
if (suffix === void 0) {
|
|
1994
|
+
return prefixOrWrapper + s + prefixOrWrapper;
|
|
1995
|
+
}
|
|
1996
|
+
return prefixOrWrapper + s + suffix;
|
|
1997
|
+
}
|
|
1998
|
+
function unwrap(s, prefixOrWrapper, suffix) {
|
|
1999
|
+
const prefix = prefixOrWrapper;
|
|
2000
|
+
const actualSuffix = suffix ?? prefixOrWrapper;
|
|
2001
|
+
if (s.startsWith(prefix) && s.endsWith(actualSuffix)) {
|
|
2002
|
+
return s.slice(prefix.length, -actualSuffix.length || void 0);
|
|
2003
|
+
}
|
|
2004
|
+
return s;
|
|
2005
|
+
}
|
|
2006
|
+
function lines(s) {
|
|
2007
|
+
return s.split(/\r?\n/);
|
|
2008
|
+
}
|
|
2009
|
+
function words(s) {
|
|
2010
|
+
return s.split(/\s+/).filter((w) => w.length > 0);
|
|
2011
|
+
}
|
|
2012
|
+
function chars(s) {
|
|
2013
|
+
return [...s];
|
|
2014
|
+
}
|
|
2015
|
+
function join(strings, separator = "") {
|
|
2016
|
+
return strings.join(separator);
|
|
2017
|
+
}
|
|
2018
|
+
function format(template, values) {
|
|
2019
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
2020
|
+
return key in values ? String(values[key]) : match;
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
function formatNumber(n, options = {}) {
|
|
2024
|
+
return n.toLocaleString("en-US", options);
|
|
2025
|
+
}
|
|
2026
|
+
function numberWithCommas(n) {
|
|
2027
|
+
return n.toLocaleString("en-US");
|
|
2028
|
+
}
|
|
2029
|
+
function formatBytes(bytes, decimals = 2) {
|
|
2030
|
+
if (bytes === 0) return "0 B";
|
|
2031
|
+
const k = 1024;
|
|
2032
|
+
if (bytes < 0) return `${bytes} B`;
|
|
2033
|
+
const sizes = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
2034
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
2035
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + " " + sizes[i];
|
|
2036
|
+
}
|
|
2037
|
+
function formatDuration(ms) {
|
|
2038
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
2039
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2040
|
+
if (ms < 36e5) {
|
|
2041
|
+
return `${(ms / 6e4).toFixed(1)}m`;
|
|
2042
|
+
}
|
|
2043
|
+
const hours = Math.floor(ms / 36e5);
|
|
2044
|
+
const mins = Math.floor(ms % 36e5 / 6e4);
|
|
2045
|
+
return `${(ms / 36e5).toFixed(1)}h`;
|
|
2046
|
+
}
|
|
2047
|
+
function escapeHtml(s) {
|
|
2048
|
+
const htmlEntities = {
|
|
2049
|
+
"&": "&",
|
|
2050
|
+
"<": "<",
|
|
2051
|
+
">": ">",
|
|
2052
|
+
'"': """,
|
|
2053
|
+
"'": "'"
|
|
2054
|
+
};
|
|
2055
|
+
return s.replace(/[&<>"']/g, (c) => htmlEntities[c]);
|
|
2056
|
+
}
|
|
2057
|
+
function unescapeHtml(s) {
|
|
2058
|
+
const htmlEntities = {
|
|
2059
|
+
"&": "&",
|
|
2060
|
+
"<": "<",
|
|
2061
|
+
">": ">",
|
|
2062
|
+
""": '"',
|
|
2063
|
+
"'": "'",
|
|
2064
|
+
"'": "'",
|
|
2065
|
+
"/": "/"
|
|
2066
|
+
};
|
|
2067
|
+
return s.replace(
|
|
2068
|
+
/&(?:amp|lt|gt|quot|#39|#x27|#x2F);/g,
|
|
2069
|
+
(entity) => htmlEntities[entity] || entity
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
function escapeRegex(s) {
|
|
2073
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2074
|
+
}
|
|
2075
|
+
function slugify(s) {
|
|
2076
|
+
return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2077
|
+
}
|
|
2078
|
+
function isValidIdentifier(s) {
|
|
2079
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);
|
|
2080
|
+
}
|
|
2081
|
+
function isNumeric(s) {
|
|
2082
|
+
return !isNaN(parseFloat(s)) && isFinite(Number(s));
|
|
2083
|
+
}
|
|
2084
|
+
function isAlphanumeric(s) {
|
|
2085
|
+
return /^[a-zA-Z0-9]+$/.test(s);
|
|
2086
|
+
}
|
|
2087
|
+
function isAlpha(s) {
|
|
2088
|
+
return /^[a-zA-Z]+$/.test(s);
|
|
2089
|
+
}
|
|
2090
|
+
function randomString(length, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
|
|
2091
|
+
let result = "";
|
|
2092
|
+
for (let i = 0; i < length; i++) {
|
|
2093
|
+
result += charset[Math.floor(Math.random() * charset.length)];
|
|
2094
|
+
}
|
|
2095
|
+
return result;
|
|
2096
|
+
}
|
|
2097
|
+
function uuid() {
|
|
2098
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2099
|
+
const r = Math.random() * 16 | 0;
|
|
2100
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
2101
|
+
return v.toString(16);
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
function indent(s, spaces, char = " ") {
|
|
2105
|
+
const prefix = char.repeat(spaces);
|
|
2106
|
+
return s.split("\n").map((line) => prefix + line).join("\n");
|
|
2107
|
+
}
|
|
2108
|
+
function dedent(s) {
|
|
2109
|
+
const linesList = lines(s);
|
|
2110
|
+
const nonEmptyLines = linesList.filter((l) => l.trim().length > 0);
|
|
2111
|
+
if (nonEmptyLines.length === 0) return s;
|
|
2112
|
+
const minIndent = Math.min(
|
|
2113
|
+
...nonEmptyLines.map((line) => {
|
|
2114
|
+
const match = line.match(/^(\s*)/);
|
|
2115
|
+
return match ? match[1].length : 0;
|
|
2116
|
+
})
|
|
2117
|
+
);
|
|
2118
|
+
return linesList.map((line) => line.slice(minIndent)).join("\n");
|
|
2119
|
+
}
|
|
2120
|
+
function wordWrap(s, maxWidth) {
|
|
2121
|
+
const wordsList = words(s);
|
|
2122
|
+
const linesList = [];
|
|
2123
|
+
let currentLine = "";
|
|
2124
|
+
for (const word of wordsList) {
|
|
2125
|
+
if (currentLine.length === 0) {
|
|
2126
|
+
currentLine = word;
|
|
2127
|
+
} else if (currentLine.length + 1 + word.length <= maxWidth) {
|
|
2128
|
+
currentLine += " " + word;
|
|
2129
|
+
} else {
|
|
2130
|
+
linesList.push(currentLine);
|
|
2131
|
+
currentLine = word;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
if (currentLine.length > 0) {
|
|
2135
|
+
linesList.push(currentLine);
|
|
2136
|
+
}
|
|
2137
|
+
return linesList.join("\n");
|
|
2138
|
+
}
|
|
2139
|
+
function levenshtein(a, b) {
|
|
2140
|
+
if (a.length === 0) return b.length;
|
|
2141
|
+
if (b.length === 0) return a.length;
|
|
2142
|
+
const matrix = [];
|
|
2143
|
+
for (let i = 0; i <= b.length; i++) {
|
|
2144
|
+
matrix[i] = [i];
|
|
2145
|
+
}
|
|
2146
|
+
for (let j = 0; j <= a.length; j++) {
|
|
2147
|
+
matrix[0][j] = j;
|
|
2148
|
+
}
|
|
2149
|
+
for (let i = 1; i <= b.length; i++) {
|
|
2150
|
+
for (let j = 1; j <= a.length; j++) {
|
|
2151
|
+
if (b[i - 1] === a[j - 1]) {
|
|
2152
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
2153
|
+
} else {
|
|
2154
|
+
matrix[i][j] = Math.min(
|
|
2155
|
+
matrix[i - 1][j - 1] + 1,
|
|
2156
|
+
// substitution
|
|
2157
|
+
matrix[i][j - 1] + 1,
|
|
2158
|
+
// insertion
|
|
2159
|
+
matrix[i - 1][j] + 1
|
|
2160
|
+
// deletion
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return matrix[b.length][a.length];
|
|
2166
|
+
}
|
|
2167
|
+
function lcsLength(a, b) {
|
|
2168
|
+
const n = b.length;
|
|
2169
|
+
const dp = new Array(n + 1).fill(0);
|
|
2170
|
+
for (let i = 0; i < a.length; i++) {
|
|
2171
|
+
let prev = 0;
|
|
2172
|
+
for (let j = 0; j < n; j++) {
|
|
2173
|
+
const tmp = dp[j + 1];
|
|
2174
|
+
dp[j + 1] = a[i] === b[j] ? prev + 1 : Math.max(dp[j + 1], dp[j]);
|
|
2175
|
+
prev = tmp;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
return dp[n];
|
|
2179
|
+
}
|
|
2180
|
+
function similarity(a, b) {
|
|
2181
|
+
const total = a.length + b.length;
|
|
2182
|
+
if (total === 0) return 1;
|
|
2183
|
+
return 2 * lcsLength(a, b) / total;
|
|
2184
|
+
}
|
|
2185
|
+
function extractTraits(code) {
|
|
2186
|
+
const traits = /* @__PURE__ */ new Set();
|
|
2187
|
+
let m;
|
|
2188
|
+
const atRe = /@([a-zA-Z_]\w*)/g;
|
|
2189
|
+
while ((m = atRe.exec(code)) !== null) traits.add(`@${m[1]}`);
|
|
2190
|
+
const tagRe = /<([A-Z][a-zA-Z0-9]*)\b/g;
|
|
2191
|
+
while ((m = tagRe.exec(code)) !== null) traits.add(m[1]);
|
|
2192
|
+
return [...traits];
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
// src/traits/EconomicPrimitives.ts
|
|
2196
|
+
function generateTxHash(from, to, price, timestamp) {
|
|
2197
|
+
let hash = 2166136261;
|
|
2198
|
+
const input = `${from}:${to}:${price}:${timestamp}`;
|
|
2199
|
+
for (let i = 0; i < input.length; i++) {
|
|
2200
|
+
hash ^= input.charCodeAt(i);
|
|
2201
|
+
hash = Math.imul(hash, 16777619);
|
|
2202
|
+
}
|
|
2203
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
2204
|
+
}
|
|
2205
|
+
function executeTrade(record, buyer, price, timestamp) {
|
|
2206
|
+
if (record.locked) {
|
|
2207
|
+
return { ok: false, error: "TRANSFER_BLOCKED" };
|
|
2208
|
+
}
|
|
2209
|
+
if (price < 0) {
|
|
2210
|
+
return { ok: false, error: "INVALID_AMOUNT" };
|
|
2211
|
+
}
|
|
2212
|
+
const txHash = generateTxHash(record.owner, buyer, price, timestamp);
|
|
2213
|
+
const transfer = {
|
|
2214
|
+
from: record.owner,
|
|
2215
|
+
to: buyer,
|
|
2216
|
+
price,
|
|
2217
|
+
timestamp,
|
|
2218
|
+
txHash
|
|
2219
|
+
};
|
|
2220
|
+
const history = [transfer, ...record.history].slice(0, 50);
|
|
2221
|
+
return {
|
|
2222
|
+
ok: true,
|
|
2223
|
+
value: {
|
|
2224
|
+
owner: buyer,
|
|
2225
|
+
history,
|
|
2226
|
+
locked: false
|
|
2227
|
+
}
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
var DEFAULT_DEPRECIATION = {
|
|
2231
|
+
decayRate: 1e-4,
|
|
2232
|
+
// 0.01% per second (~36% per hour)
|
|
2233
|
+
condition: 1,
|
|
2234
|
+
destroyThreshold: 0.01,
|
|
2235
|
+
repairable: true,
|
|
2236
|
+
repairCostMultiplier: 0.5
|
|
2237
|
+
};
|
|
2238
|
+
function calculateDepreciation(initialCondition, decayRate, elapsedSeconds) {
|
|
2239
|
+
if (decayRate <= 0) return initialCondition;
|
|
2240
|
+
if (elapsedSeconds <= 0) return initialCondition;
|
|
2241
|
+
return initialCondition * Math.exp(-decayRate * elapsedSeconds);
|
|
2242
|
+
}
|
|
2243
|
+
function depreciatedValue(baseValue, condition) {
|
|
2244
|
+
return baseValue * Math.max(0, Math.min(1, condition));
|
|
2245
|
+
}
|
|
2246
|
+
function isDestroyed(condition, threshold) {
|
|
2247
|
+
return condition <= threshold;
|
|
2248
|
+
}
|
|
2249
|
+
function calculateRepairCost(baseValue, currentCondition, repairCostMultiplier) {
|
|
2250
|
+
const conditionDeficit = 1 - Math.max(0, Math.min(1, currentCondition));
|
|
2251
|
+
return baseValue * conditionDeficit * repairCostMultiplier;
|
|
2252
|
+
}
|
|
2253
|
+
var DEFAULT_BONDING_CURVE = {
|
|
2254
|
+
curveType: "exponential",
|
|
2255
|
+
reserveRatio: 1,
|
|
2256
|
+
curveSteepness: 2,
|
|
2257
|
+
currentSupply: 0,
|
|
2258
|
+
reserveBalance: 0,
|
|
2259
|
+
transactionFee: 0.02,
|
|
2260
|
+
// 2% hard sink
|
|
2261
|
+
sigmoidK: 100,
|
|
2262
|
+
spatialDistanceFactor: 0
|
|
2263
|
+
};
|
|
2264
|
+
function bondingCurvePrice(supply, reserveRatio, curveSteepness, curveType = "exponential", sigmoidK = 100) {
|
|
2265
|
+
if (supply <= 0) return 0;
|
|
2266
|
+
if (reserveRatio <= 0) return 0;
|
|
2267
|
+
switch (curveType) {
|
|
2268
|
+
case "linear":
|
|
2269
|
+
return reserveRatio * supply;
|
|
2270
|
+
case "exponential":
|
|
2271
|
+
return reserveRatio * Math.pow(supply, 1 / curveSteepness);
|
|
2272
|
+
case "logarithmic":
|
|
2273
|
+
return reserveRatio * Math.log(supply + 1);
|
|
2274
|
+
case "sigmoid": {
|
|
2275
|
+
const sn = Math.pow(supply, curveSteepness);
|
|
2276
|
+
const kn = Math.pow(sigmoidK, curveSteepness);
|
|
2277
|
+
return reserveRatio * (sn / (kn + sn));
|
|
2278
|
+
}
|
|
2279
|
+
default:
|
|
2280
|
+
return reserveRatio * Math.pow(supply, 1 / curveSteepness);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
function bondingCurveBuyCost(config, amount) {
|
|
2284
|
+
if (amount <= 0) return 0;
|
|
2285
|
+
const n = Math.max(10, Math.ceil(amount));
|
|
2286
|
+
const h = amount / n;
|
|
2287
|
+
let sum = 0;
|
|
2288
|
+
const s0 = config.currentSupply;
|
|
2289
|
+
for (let i = 0; i <= n; i++) {
|
|
2290
|
+
const s = s0 + i * h;
|
|
2291
|
+
const p = bondingCurvePrice(
|
|
2292
|
+
s,
|
|
2293
|
+
config.reserveRatio,
|
|
2294
|
+
config.curveSteepness,
|
|
2295
|
+
config.curveType,
|
|
2296
|
+
config.sigmoidK
|
|
2297
|
+
);
|
|
2298
|
+
if (i === 0 || i === n) {
|
|
2299
|
+
sum += p;
|
|
2300
|
+
} else if (i % 2 === 1) {
|
|
2301
|
+
sum += 4 * p;
|
|
2302
|
+
} else {
|
|
2303
|
+
sum += 2 * p;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
const cost = h / 3 * sum;
|
|
2307
|
+
return cost * (1 + config.transactionFee);
|
|
2308
|
+
}
|
|
2309
|
+
function bondingCurveSellRefund(config, amount) {
|
|
2310
|
+
if (amount <= 0) return 0;
|
|
2311
|
+
if (amount > config.currentSupply) return 0;
|
|
2312
|
+
const n = Math.max(10, Math.ceil(amount));
|
|
2313
|
+
const h = amount / n;
|
|
2314
|
+
let sum = 0;
|
|
2315
|
+
const s0 = config.currentSupply - amount;
|
|
2316
|
+
for (let i = 0; i <= n; i++) {
|
|
2317
|
+
const s = s0 + i * h;
|
|
2318
|
+
const p = bondingCurvePrice(
|
|
2319
|
+
s,
|
|
2320
|
+
config.reserveRatio,
|
|
2321
|
+
config.curveSteepness,
|
|
2322
|
+
config.curveType,
|
|
2323
|
+
config.sigmoidK
|
|
2324
|
+
);
|
|
2325
|
+
if (i === 0 || i === n) {
|
|
2326
|
+
sum += p;
|
|
2327
|
+
} else if (i % 2 === 1) {
|
|
2328
|
+
sum += 4 * p;
|
|
2329
|
+
} else {
|
|
2330
|
+
sum += 2 * p;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
const refund = h / 3 * sum;
|
|
2334
|
+
return refund * (1 - config.transactionFee);
|
|
2335
|
+
}
|
|
2336
|
+
function spatialPrice(basePrice, distanceToHub, distanceFactor) {
|
|
2337
|
+
if (distanceFactor <= 0 || distanceToHub <= 0) return basePrice;
|
|
2338
|
+
return basePrice * (1 + distanceFactor * distanceToHub);
|
|
2339
|
+
}
|
|
2340
|
+
var DEFAULT_WEALTH_TAX = {
|
|
2341
|
+
threshold: 1e4,
|
|
2342
|
+
baseRate: 0.01,
|
|
2343
|
+
// 1% base
|
|
2344
|
+
maxEffectiveRate: 0.05,
|
|
2345
|
+
// 5% cap
|
|
2346
|
+
collectionInterval: "daily",
|
|
2347
|
+
enableRedistribution: true,
|
|
2348
|
+
redistributionFraction: 0.7
|
|
2349
|
+
// 70% redistributed, 30% burned as hard sink
|
|
2350
|
+
};
|
|
2351
|
+
function calculateTaxRate(wealth, threshold, baseRate, maxEffectiveRate) {
|
|
2352
|
+
if (wealth <= threshold) return 0;
|
|
2353
|
+
if (threshold <= 0) return 0;
|
|
2354
|
+
const rate = Math.log(wealth / threshold) * baseRate;
|
|
2355
|
+
return Math.min(rate, maxEffectiveRate);
|
|
2356
|
+
}
|
|
2357
|
+
function calculateTaxAmount(wealth, config) {
|
|
2358
|
+
const rate = calculateTaxRate(wealth, config.threshold, config.baseRate, config.maxEffectiveRate);
|
|
2359
|
+
return wealth * rate;
|
|
2360
|
+
}
|
|
2361
|
+
function calculateRedistribution(totalTaxCollected, recipientCount, redistributionFraction) {
|
|
2362
|
+
if (recipientCount <= 0 || totalTaxCollected <= 0) {
|
|
2363
|
+
return { perRecipient: 0, burned: totalTaxCollected };
|
|
2364
|
+
}
|
|
2365
|
+
const redistributed = totalTaxCollected * redistributionFraction;
|
|
2366
|
+
const burned = totalTaxCollected - redistributed;
|
|
2367
|
+
const perRecipient = redistributed / recipientCount;
|
|
2368
|
+
return { perRecipient, burned };
|
|
2369
|
+
}
|
|
2370
|
+
var DEFAULT_PID = {
|
|
2371
|
+
kp: 0.5,
|
|
2372
|
+
ki: 0.01,
|
|
2373
|
+
kd: 0.1,
|
|
2374
|
+
setpoint: 0,
|
|
2375
|
+
outputMin: -1,
|
|
2376
|
+
outputMax: 1,
|
|
2377
|
+
integralLimit: 10
|
|
2378
|
+
};
|
|
2379
|
+
function createPIDState() {
|
|
2380
|
+
return {
|
|
2381
|
+
previousError: 0,
|
|
2382
|
+
integral: 0,
|
|
2383
|
+
lastOutput: 0,
|
|
2384
|
+
initialized: false
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
function updatePID(config, state, measurement, dt) {
|
|
2388
|
+
if (dt <= 0) return state.lastOutput;
|
|
2389
|
+
const error = config.setpoint - measurement;
|
|
2390
|
+
const p = config.kp * error;
|
|
2391
|
+
state.integral += error * dt;
|
|
2392
|
+
state.integral = Math.max(-config.integralLimit, Math.min(config.integralLimit, state.integral));
|
|
2393
|
+
const i = config.ki * state.integral;
|
|
2394
|
+
let d = 0;
|
|
2395
|
+
if (state.initialized) {
|
|
2396
|
+
d = config.kd * ((error - state.previousError) / dt);
|
|
2397
|
+
}
|
|
2398
|
+
let output = p + i + d;
|
|
2399
|
+
output = Math.max(config.outputMin, Math.min(config.outputMax, output));
|
|
2400
|
+
state.previousError = error;
|
|
2401
|
+
state.lastOutput = output;
|
|
2402
|
+
state.initialized = true;
|
|
2403
|
+
return output;
|
|
2404
|
+
}
|
|
2405
|
+
function createDualLoopPIDState() {
|
|
2406
|
+
return {
|
|
2407
|
+
innerState: createPIDState(),
|
|
2408
|
+
outerState: createPIDState(),
|
|
2409
|
+
faucetMultiplier: 1
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
function updateDualLoopPID(config, state, totalMoneySupply, currentFaucetRate, dt) {
|
|
2413
|
+
const targetSupply = config.perCapitaTarget * config.activePlayerCount;
|
|
2414
|
+
const outerConfig = { ...config.outerLoop, setpoint: targetSupply };
|
|
2415
|
+
const outerOutput = updatePID(outerConfig, state.outerState, totalMoneySupply, dt);
|
|
2416
|
+
const desiredFaucetRate = currentFaucetRate * (1 + outerOutput * 0.1);
|
|
2417
|
+
const innerConfig = { ...config.innerLoop, setpoint: desiredFaucetRate };
|
|
2418
|
+
const innerOutput = updatePID(innerConfig, state.innerState, currentFaucetRate, dt);
|
|
2419
|
+
state.faucetMultiplier = Math.max(0, Math.min(2, 1 + innerOutput));
|
|
2420
|
+
return state.faucetMultiplier;
|
|
2421
|
+
}
|
|
2422
|
+
function createFaucetSinkMetrics(timestamp) {
|
|
2423
|
+
return {
|
|
2424
|
+
totalFaucet: 0,
|
|
2425
|
+
totalSink: 0,
|
|
2426
|
+
ratio: 1,
|
|
2427
|
+
velocity: 0,
|
|
2428
|
+
periodStart: timestamp,
|
|
2429
|
+
ratioHistory: []
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
function recordFaucet(metrics, amount) {
|
|
2433
|
+
metrics.totalFaucet += amount;
|
|
2434
|
+
metrics.ratio = metrics.totalSink > 0 ? metrics.totalFaucet / metrics.totalSink : metrics.totalFaucet > 0 ? Infinity : 1;
|
|
2435
|
+
}
|
|
2436
|
+
function recordSink(metrics, amount) {
|
|
2437
|
+
metrics.totalSink += amount;
|
|
2438
|
+
metrics.ratio = metrics.totalSink > 0 ? metrics.totalFaucet / metrics.totalSink : metrics.totalFaucet > 0 ? Infinity : 1;
|
|
2439
|
+
}
|
|
2440
|
+
function resetMetricsPeriod(metrics, timestamp) {
|
|
2441
|
+
if (metrics.totalFaucet > 0 || metrics.totalSink > 0) {
|
|
2442
|
+
metrics.ratioHistory.push(metrics.ratio);
|
|
2443
|
+
if (metrics.ratioHistory.length > 100) {
|
|
2444
|
+
metrics.ratioHistory.shift();
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
metrics.totalFaucet = 0;
|
|
2448
|
+
metrics.totalSink = 0;
|
|
2449
|
+
metrics.ratio = 1;
|
|
2450
|
+
metrics.velocity = 0;
|
|
2451
|
+
metrics.periodStart = timestamp;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// src/traits/EconomicTraits.ts
|
|
2455
|
+
var EconomicTraits = {
|
|
2456
|
+
// ---------------------------------------------------------------------------
|
|
2457
|
+
// 1. TRADEABLE
|
|
2458
|
+
// ---------------------------------------------------------------------------
|
|
2459
|
+
tradeable: {
|
|
2460
|
+
name: "@tradeable",
|
|
2461
|
+
description: "Makes a composition or node transferable between agents. Tracks ownership history with transaction hashes for integrity verification (P.030.04). Supports lock/unlock for escrow patterns and auction integration.",
|
|
2462
|
+
params: {
|
|
2463
|
+
initial_owner: {
|
|
2464
|
+
type: "AgentID",
|
|
2465
|
+
required: true,
|
|
2466
|
+
description: "The agent who initially owns this entity."
|
|
2467
|
+
},
|
|
2468
|
+
transferable: {
|
|
2469
|
+
type: "boolean",
|
|
2470
|
+
required: false,
|
|
2471
|
+
default: true,
|
|
2472
|
+
description: "Whether transfers are currently allowed."
|
|
2473
|
+
},
|
|
2474
|
+
min_price: {
|
|
2475
|
+
type: "number",
|
|
2476
|
+
required: false,
|
|
2477
|
+
default: 0,
|
|
2478
|
+
description: "Minimum trade price (prevents value manipulation)."
|
|
2479
|
+
},
|
|
2480
|
+
max_history: {
|
|
2481
|
+
type: "number",
|
|
2482
|
+
required: false,
|
|
2483
|
+
default: 50,
|
|
2484
|
+
description: "Maximum ownership history entries retained."
|
|
2485
|
+
},
|
|
2486
|
+
requires_presence: {
|
|
2487
|
+
type: "boolean",
|
|
2488
|
+
required: false,
|
|
2489
|
+
default: false,
|
|
2490
|
+
description: "VR-specific: require spatial co-location for trade (anti-exploit, Layer 7)."
|
|
2491
|
+
},
|
|
2492
|
+
presence_radius: {
|
|
2493
|
+
type: "number",
|
|
2494
|
+
required: false,
|
|
2495
|
+
default: 5,
|
|
2496
|
+
description: "Maximum distance in meters for presence-based trades."
|
|
2497
|
+
}
|
|
2498
|
+
},
|
|
2499
|
+
validator: (params) => {
|
|
2500
|
+
if (!params.initial_owner || typeof params.initial_owner !== "string") return false;
|
|
2501
|
+
if (params.min_price !== void 0 && params.min_price < 0) return false;
|
|
2502
|
+
return true;
|
|
2503
|
+
},
|
|
2504
|
+
requiredPermissions: ["economy.trade"],
|
|
2505
|
+
economicLayer: 0,
|
|
2506
|
+
// Layer 0: Integrity
|
|
2507
|
+
composesWith: ["@depreciating", "@bonding_curved", "@taxable_wealth"],
|
|
2508
|
+
compiler_hints: {
|
|
2509
|
+
requires_runtime: ["EconomicRuntime.executeTrade", "EconomicRuntime.verifyOwnership"],
|
|
2510
|
+
thread_safety: "any"
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
// ---------------------------------------------------------------------------
|
|
2514
|
+
// 2. DEPRECIATING
|
|
2515
|
+
// ---------------------------------------------------------------------------
|
|
2516
|
+
depreciating: {
|
|
2517
|
+
name: "@depreciating",
|
|
2518
|
+
description: "Applies time-based value decay to an entity (hard sink mechanic). Uses exponential decay: condition(t) = condition(0) * e^(-rate * t). When condition reaches destroy_threshold, the entity is consumed -- removing currency/value from circulation permanently (W.031).",
|
|
2519
|
+
params: {
|
|
2520
|
+
decay_rate: {
|
|
2521
|
+
type: "number",
|
|
2522
|
+
required: true,
|
|
2523
|
+
description: "Per-second decay rate (0.0 - 1.0). E.g., 0.001 = 0.1% per second."
|
|
2524
|
+
},
|
|
2525
|
+
initial_condition: {
|
|
2526
|
+
type: "number",
|
|
2527
|
+
required: false,
|
|
2528
|
+
default: 1,
|
|
2529
|
+
description: "Starting condition (1.0 = mint, 0.0 = destroyed)."
|
|
2530
|
+
},
|
|
2531
|
+
destroy_threshold: {
|
|
2532
|
+
type: "number",
|
|
2533
|
+
required: false,
|
|
2534
|
+
default: 0.01,
|
|
2535
|
+
description: "Condition level at which the entity is destroyed."
|
|
2536
|
+
},
|
|
2537
|
+
repairable: {
|
|
2538
|
+
type: "boolean",
|
|
2539
|
+
required: false,
|
|
2540
|
+
default: true,
|
|
2541
|
+
description: "Whether the entity can be repaired to restore condition."
|
|
2542
|
+
},
|
|
2543
|
+
repair_cost_multiplier: {
|
|
2544
|
+
type: "number",
|
|
2545
|
+
required: false,
|
|
2546
|
+
default: 0.5,
|
|
2547
|
+
description: "Repair cost as a fraction of base value per unit condition."
|
|
2548
|
+
},
|
|
2549
|
+
emit_on_destroy: {
|
|
2550
|
+
type: "string",
|
|
2551
|
+
required: false,
|
|
2552
|
+
default: "on_entity_destroyed",
|
|
2553
|
+
description: "Event emitted when condition reaches destroy threshold."
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2556
|
+
validator: (params) => {
|
|
2557
|
+
if (params.decay_rate === void 0 || params.decay_rate < 0 || params.decay_rate > 1)
|
|
2558
|
+
return false;
|
|
2559
|
+
if (params.initial_condition !== void 0 && (params.initial_condition < 0 || params.initial_condition > 1))
|
|
2560
|
+
return false;
|
|
2561
|
+
if (params.destroy_threshold !== void 0 && (params.destroy_threshold < 0 || params.destroy_threshold > 1))
|
|
2562
|
+
return false;
|
|
2563
|
+
return true;
|
|
2564
|
+
},
|
|
2565
|
+
requiredPermissions: ["economy.burn"],
|
|
2566
|
+
economicLayer: 1,
|
|
2567
|
+
// Layer 1: Flow Control (sink side)
|
|
2568
|
+
composesWith: ["@tradeable", "@bonding_curved"],
|
|
2569
|
+
compiler_hints: {
|
|
2570
|
+
requires_runtime: ["EconomicRuntime.updateDepreciation", "EconomicRuntime.destroyEntity"],
|
|
2571
|
+
thread_safety: "worker_thread",
|
|
2572
|
+
performance_budget_ms: 0.1
|
|
2573
|
+
// Must be fast -- called every tick
|
|
2574
|
+
}
|
|
2575
|
+
},
|
|
2576
|
+
// ---------------------------------------------------------------------------
|
|
2577
|
+
// 3. BONDING_CURVED
|
|
2578
|
+
// ---------------------------------------------------------------------------
|
|
2579
|
+
bonding_curved: {
|
|
2580
|
+
name: "@bonding_curved",
|
|
2581
|
+
description: "Attaches a bonding curve to a marketplace or item category for autonomous price discovery. Price is determined by P = f(supply) where f depends on curve_type. Provides continuous liquidity via reserve pool without order books (W.035, P.030.02). Supports spatial pricing for VR trade geography.",
|
|
2582
|
+
params: {
|
|
2583
|
+
curve_type: {
|
|
2584
|
+
type: "string",
|
|
2585
|
+
required: false,
|
|
2586
|
+
default: "exponential",
|
|
2587
|
+
description: "Curve shape: linear | exponential | logarithmic | sigmoid.",
|
|
2588
|
+
validation: (val) => ["linear", "exponential", "logarithmic", "sigmoid"].includes(val)
|
|
2589
|
+
},
|
|
2590
|
+
reserve_ratio: {
|
|
2591
|
+
type: "number",
|
|
2592
|
+
required: true,
|
|
2593
|
+
description: "Base price multiplier (R in the price formula)."
|
|
2594
|
+
},
|
|
2595
|
+
curve_steepness: {
|
|
2596
|
+
type: "number",
|
|
2597
|
+
required: false,
|
|
2598
|
+
default: 2,
|
|
2599
|
+
description: "Controls price sensitivity to supply changes (n in P = R * S^(1/n))."
|
|
2600
|
+
},
|
|
2601
|
+
transaction_fee: {
|
|
2602
|
+
type: "number",
|
|
2603
|
+
required: false,
|
|
2604
|
+
default: 0.02,
|
|
2605
|
+
description: "Fee percentage per transaction (hard sink). Range: 0.0 - 0.5."
|
|
2606
|
+
},
|
|
2607
|
+
initial_supply: {
|
|
2608
|
+
type: "number",
|
|
2609
|
+
required: false,
|
|
2610
|
+
default: 0,
|
|
2611
|
+
description: "Initial token/item supply on the curve."
|
|
2612
|
+
},
|
|
2613
|
+
initial_reserve: {
|
|
2614
|
+
type: "number",
|
|
2615
|
+
required: false,
|
|
2616
|
+
default: 0,
|
|
2617
|
+
description: "Initial reserve pool balance backing the curve."
|
|
2618
|
+
},
|
|
2619
|
+
sigmoid_k: {
|
|
2620
|
+
type: "number",
|
|
2621
|
+
required: false,
|
|
2622
|
+
default: 100,
|
|
2623
|
+
description: "Half-saturation constant for sigmoid curves."
|
|
2624
|
+
},
|
|
2625
|
+
spatial_distance_factor: {
|
|
2626
|
+
type: "number",
|
|
2627
|
+
required: false,
|
|
2628
|
+
default: 0,
|
|
2629
|
+
description: "VR spatial pricing: price multiplier per unit distance from hub. 0.0 = no spatial effect. Creates emergent trade routes (P.030.02 ENRICHED)."
|
|
2630
|
+
}
|
|
2631
|
+
},
|
|
2632
|
+
validator: (params) => {
|
|
2633
|
+
if (params.reserve_ratio === void 0 || params.reserve_ratio <= 0) return false;
|
|
2634
|
+
if (params.curve_steepness !== void 0 && params.curve_steepness <= 0) return false;
|
|
2635
|
+
if (params.transaction_fee !== void 0 && (params.transaction_fee < 0 || params.transaction_fee > 0.5))
|
|
2636
|
+
return false;
|
|
2637
|
+
if (params.curve_type !== void 0) {
|
|
2638
|
+
if (!["linear", "exponential", "logarithmic", "sigmoid"].includes(params.curve_type))
|
|
2639
|
+
return false;
|
|
2640
|
+
}
|
|
2641
|
+
return true;
|
|
2642
|
+
},
|
|
2643
|
+
requiredPermissions: ["economy.set_price", "economy.trade"],
|
|
2644
|
+
economicLayer: 2,
|
|
2645
|
+
// Layer 2: Price Discovery
|
|
2646
|
+
composesWith: ["@tradeable", "@depreciating", "@pid_controlled"],
|
|
2647
|
+
compiler_hints: {
|
|
2648
|
+
requires_runtime: [
|
|
2649
|
+
"EconomicRuntime.bondingCurveBuy",
|
|
2650
|
+
"EconomicRuntime.bondingCurveSell",
|
|
2651
|
+
"EconomicRuntime.getSpotPrice"
|
|
2652
|
+
],
|
|
2653
|
+
thread_safety: "any",
|
|
2654
|
+
performance_budget_ms: 1
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2657
|
+
// ---------------------------------------------------------------------------
|
|
2658
|
+
// 4. TAXABLE_WEALTH
|
|
2659
|
+
// ---------------------------------------------------------------------------
|
|
2660
|
+
taxable_wealth: {
|
|
2661
|
+
name: "@taxable_wealth",
|
|
2662
|
+
description: "Applies progressive logarithmic wealth taxation with optional redistribution to the lowest-wealth quintile (P.030.03, W.034). Tax = 0 below threshold, then rate = min(max_rate, log(wealth/threshold) * base_rate). Collected taxes are partially redistributed (UBI) and partially burned (hard sink).",
|
|
2663
|
+
params: {
|
|
2664
|
+
threshold: {
|
|
2665
|
+
type: "number",
|
|
2666
|
+
required: true,
|
|
2667
|
+
description: "Wealth level below which no tax is levied."
|
|
2668
|
+
},
|
|
2669
|
+
base_rate: {
|
|
2670
|
+
type: "number",
|
|
2671
|
+
required: false,
|
|
2672
|
+
default: 0.01,
|
|
2673
|
+
description: "Base tax rate multiplied by log(wealth/threshold). Range: 0.0 - 0.1."
|
|
2674
|
+
},
|
|
2675
|
+
max_effective_rate: {
|
|
2676
|
+
type: "number",
|
|
2677
|
+
required: false,
|
|
2678
|
+
default: 0.05,
|
|
2679
|
+
description: "Maximum effective tax rate cap (prevents punishing engagement)."
|
|
2680
|
+
},
|
|
2681
|
+
collection_interval: {
|
|
2682
|
+
type: "string",
|
|
2683
|
+
required: false,
|
|
2684
|
+
default: "daily",
|
|
2685
|
+
description: "How often taxes are collected: hourly | daily | weekly.",
|
|
2686
|
+
validation: (val) => ["hourly", "daily", "weekly"].includes(val)
|
|
2687
|
+
},
|
|
2688
|
+
enable_redistribution: {
|
|
2689
|
+
type: "boolean",
|
|
2690
|
+
required: false,
|
|
2691
|
+
default: true,
|
|
2692
|
+
description: "Whether collected tax is redistributed to lowest-wealth players."
|
|
2693
|
+
},
|
|
2694
|
+
redistribution_fraction: {
|
|
2695
|
+
type: "number",
|
|
2696
|
+
required: false,
|
|
2697
|
+
default: 0.7,
|
|
2698
|
+
description: "Fraction of tax redistributed (remainder burned as hard sink). Range: 0.0 - 1.0."
|
|
2699
|
+
},
|
|
2700
|
+
emit_on_collection: {
|
|
2701
|
+
type: "string",
|
|
2702
|
+
required: false,
|
|
2703
|
+
default: "on_tax_collected",
|
|
2704
|
+
description: "Event emitted when taxes are collected."
|
|
2705
|
+
},
|
|
2706
|
+
public_ledger: {
|
|
2707
|
+
type: "boolean",
|
|
2708
|
+
required: false,
|
|
2709
|
+
default: true,
|
|
2710
|
+
description: "Whether tax collections and redistribution are visible in the economy dashboard (Layer 6: Transparency, W.038)."
|
|
2711
|
+
}
|
|
2712
|
+
},
|
|
2713
|
+
validator: (params) => {
|
|
2714
|
+
if (params.threshold === void 0 || params.threshold < 0) return false;
|
|
2715
|
+
if (params.base_rate !== void 0 && (params.base_rate < 0 || params.base_rate > 0.1))
|
|
2716
|
+
return false;
|
|
2717
|
+
if (params.max_effective_rate !== void 0 && (params.max_effective_rate < 0 || params.max_effective_rate > 1))
|
|
2718
|
+
return false;
|
|
2719
|
+
if (params.redistribution_fraction !== void 0 && (params.redistribution_fraction < 0 || params.redistribution_fraction > 1))
|
|
2720
|
+
return false;
|
|
2721
|
+
return true;
|
|
2722
|
+
},
|
|
2723
|
+
requiredPermissions: ["economy.tax", "economy.redistribute"],
|
|
2724
|
+
economicLayer: 4,
|
|
2725
|
+
// Layer 4: Redistribution
|
|
2726
|
+
composesWith: ["@tradeable", "@pid_controlled"],
|
|
2727
|
+
compiler_hints: {
|
|
2728
|
+
requires_runtime: [
|
|
2729
|
+
"EconomicRuntime.collectTax",
|
|
2730
|
+
"EconomicRuntime.redistribute",
|
|
2731
|
+
"EconomicRuntime.getGiniCoefficient"
|
|
2732
|
+
],
|
|
2733
|
+
thread_safety: "worker_thread",
|
|
2734
|
+
performance_budget_ms: 5
|
|
2735
|
+
// Runs on collection interval, not per-frame
|
|
2736
|
+
}
|
|
2737
|
+
},
|
|
2738
|
+
// ---------------------------------------------------------------------------
|
|
2739
|
+
// 5. PID_CONTROLLED
|
|
2740
|
+
// ---------------------------------------------------------------------------
|
|
2741
|
+
pid_controlled: {
|
|
2742
|
+
name: "@pid_controlled",
|
|
2743
|
+
description: "Attaches a dual-loop PID feedback controller to regulate economic flow (P.030.01, W.032). Inner loop adjusts per-source faucet rates (fast). Outer loop adjusts inner setpoint based on total money supply (slow). Achieves <10% currency variance long-term while preserving gameplay variance.",
|
|
2744
|
+
params: {
|
|
2745
|
+
target_per_capita: {
|
|
2746
|
+
type: "number",
|
|
2747
|
+
required: true,
|
|
2748
|
+
description: "Target currency per active player (outer loop setpoint)."
|
|
2749
|
+
},
|
|
2750
|
+
inner_kp: {
|
|
2751
|
+
type: "number",
|
|
2752
|
+
required: false,
|
|
2753
|
+
default: 0.5,
|
|
2754
|
+
description: "Inner loop proportional gain."
|
|
2755
|
+
},
|
|
2756
|
+
inner_ki: {
|
|
2757
|
+
type: "number",
|
|
2758
|
+
required: false,
|
|
2759
|
+
default: 0.01,
|
|
2760
|
+
description: "Inner loop integral gain."
|
|
2761
|
+
},
|
|
2762
|
+
inner_kd: {
|
|
2763
|
+
type: "number",
|
|
2764
|
+
required: false,
|
|
2765
|
+
default: 0.1,
|
|
2766
|
+
description: "Inner loop derivative gain."
|
|
2767
|
+
},
|
|
2768
|
+
outer_kp: {
|
|
2769
|
+
type: "number",
|
|
2770
|
+
required: false,
|
|
2771
|
+
default: 0.3,
|
|
2772
|
+
description: "Outer loop proportional gain."
|
|
2773
|
+
},
|
|
2774
|
+
outer_ki: {
|
|
2775
|
+
type: "number",
|
|
2776
|
+
required: false,
|
|
2777
|
+
default: 5e-3,
|
|
2778
|
+
description: "Outer loop integral gain."
|
|
2779
|
+
},
|
|
2780
|
+
outer_kd: {
|
|
2781
|
+
type: "number",
|
|
2782
|
+
required: false,
|
|
2783
|
+
default: 0.05,
|
|
2784
|
+
description: "Outer loop derivative gain."
|
|
2785
|
+
},
|
|
2786
|
+
update_interval: {
|
|
2787
|
+
type: "number",
|
|
2788
|
+
required: false,
|
|
2789
|
+
default: 1,
|
|
2790
|
+
description: "Controller update interval in seconds."
|
|
2791
|
+
},
|
|
2792
|
+
faucet_min_multiplier: {
|
|
2793
|
+
type: "number",
|
|
2794
|
+
required: false,
|
|
2795
|
+
default: 0.1,
|
|
2796
|
+
description: "Minimum faucet rate multiplier (never fully stop rewards)."
|
|
2797
|
+
},
|
|
2798
|
+
faucet_max_multiplier: {
|
|
2799
|
+
type: "number",
|
|
2800
|
+
required: false,
|
|
2801
|
+
default: 2,
|
|
2802
|
+
description: "Maximum faucet rate multiplier."
|
|
2803
|
+
},
|
|
2804
|
+
track_faucet_sink_ratio: {
|
|
2805
|
+
type: "boolean",
|
|
2806
|
+
required: false,
|
|
2807
|
+
default: true,
|
|
2808
|
+
description: "Whether to track and emit faucet/sink ratio metrics (W.031)."
|
|
2809
|
+
},
|
|
2810
|
+
emit_on_adjustment: {
|
|
2811
|
+
type: "string",
|
|
2812
|
+
required: false,
|
|
2813
|
+
default: "on_faucet_adjusted",
|
|
2814
|
+
description: "Event emitted when faucet rate is adjusted."
|
|
2815
|
+
}
|
|
2816
|
+
},
|
|
2817
|
+
validator: (params) => {
|
|
2818
|
+
if (params.target_per_capita === void 0 || params.target_per_capita <= 0) return false;
|
|
2819
|
+
const gains = [
|
|
2820
|
+
params.inner_kp,
|
|
2821
|
+
params.inner_ki,
|
|
2822
|
+
params.inner_kd,
|
|
2823
|
+
params.outer_kp,
|
|
2824
|
+
params.outer_ki,
|
|
2825
|
+
params.outer_kd
|
|
2826
|
+
];
|
|
2827
|
+
for (const g of gains) {
|
|
2828
|
+
if (g !== void 0 && (typeof g !== "number" || isNaN(g))) return false;
|
|
2829
|
+
}
|
|
2830
|
+
if (params.update_interval !== void 0 && params.update_interval <= 0) return false;
|
|
2831
|
+
return true;
|
|
2832
|
+
},
|
|
2833
|
+
requiredPermissions: ["economy.tune_pid", "economy.mint", "economy.burn"],
|
|
2834
|
+
economicLayer: 1,
|
|
2835
|
+
// Layer 1: Flow Control
|
|
2836
|
+
composesWith: ["@bonding_curved", "@taxable_wealth"],
|
|
2837
|
+
compiler_hints: {
|
|
2838
|
+
requires_runtime: [
|
|
2839
|
+
"EconomicRuntime.updatePID",
|
|
2840
|
+
"EconomicRuntime.getFaucetMultiplier",
|
|
2841
|
+
"EconomicRuntime.getFaucetSinkMetrics"
|
|
2842
|
+
],
|
|
2843
|
+
thread_safety: "worker_thread",
|
|
2844
|
+
performance_budget_ms: 0.5
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
function getEconomicTraitNames() {
|
|
2849
|
+
return Object.keys(EconomicTraits).map((k) => EconomicTraits[k].name);
|
|
2850
|
+
}
|
|
2851
|
+
function getEconomicTrait(name) {
|
|
2852
|
+
const key = name.startsWith("@") ? name.slice(1) : name;
|
|
2853
|
+
return EconomicTraits[key];
|
|
2854
|
+
}
|
|
2855
|
+
function validateTraitComposition(traitNames) {
|
|
2856
|
+
for (const name of traitNames) {
|
|
2857
|
+
const trait = getEconomicTrait(name);
|
|
2858
|
+
if (!trait) {
|
|
2859
|
+
return `Unknown economic trait: ${name}`;
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
for (const name of traitNames) {
|
|
2863
|
+
const trait = getEconomicTrait(name);
|
|
2864
|
+
for (const other of traitNames) {
|
|
2865
|
+
if (other === name) continue;
|
|
2866
|
+
const otherPrefixed = other.startsWith("@") ? other : `@${other}`;
|
|
2867
|
+
if (!trait.composesWith.includes(otherPrefixed)) {
|
|
2868
|
+
return `Trait ${trait.name} does not compose with ${otherPrefixed}`;
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
return null;
|
|
2873
|
+
}
|
|
2874
|
+
function getRequiredPermissions(traitNames) {
|
|
2875
|
+
const permissions = /* @__PURE__ */ new Set();
|
|
2876
|
+
for (const name of traitNames) {
|
|
2877
|
+
const trait = getEconomicTrait(name);
|
|
2878
|
+
if (trait) {
|
|
2879
|
+
for (const perm2 of trait.requiredPermissions) {
|
|
2880
|
+
permissions.add(perm2);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
return Array.from(permissions).sort();
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
// src/traits/SimulationLabPrimitives.ts
|
|
2888
|
+
function isParameterRange(sweep) {
|
|
2889
|
+
return "min" in sweep && "max" in sweep && "step" in sweep;
|
|
2890
|
+
}
|
|
2891
|
+
function expandSweep(sweep) {
|
|
2892
|
+
if (!isParameterRange(sweep)) {
|
|
2893
|
+
return sweep.values.slice();
|
|
2894
|
+
}
|
|
2895
|
+
const range = sweep;
|
|
2896
|
+
if (range.step <= 0) return [range.min];
|
|
2897
|
+
if (range.min > range.max) return [];
|
|
2898
|
+
const values = [];
|
|
2899
|
+
for (let v = range.min; v <= range.max + range.step * 1e-3; v += range.step) {
|
|
2900
|
+
values.push(Math.round(v * 1e10) / 1e10);
|
|
2901
|
+
}
|
|
2902
|
+
return values;
|
|
2903
|
+
}
|
|
2904
|
+
function generateSweepCombinations(sweeps) {
|
|
2905
|
+
if (sweeps.length === 0) return [{}];
|
|
2906
|
+
const expanded = sweeps.map((s) => ({
|
|
2907
|
+
name: s.name,
|
|
2908
|
+
values: expandSweep(s)
|
|
2909
|
+
}));
|
|
2910
|
+
let combinations = [{}];
|
|
2911
|
+
for (const { name, values } of expanded) {
|
|
2912
|
+
const newCombinations = [];
|
|
2913
|
+
for (const combo of combinations) {
|
|
2914
|
+
for (const val of values) {
|
|
2915
|
+
newCombinations.push({ ...combo, [name]: val });
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
combinations = newCombinations;
|
|
2919
|
+
}
|
|
2920
|
+
return combinations;
|
|
2921
|
+
}
|
|
2922
|
+
function mean(data) {
|
|
2923
|
+
if (data.length === 0) return 0;
|
|
2924
|
+
return data.reduce((sum, v) => sum + v, 0) / data.length;
|
|
2925
|
+
}
|
|
2926
|
+
function variance(data) {
|
|
2927
|
+
if (data.length < 2) return 0;
|
|
2928
|
+
const m = mean(data);
|
|
2929
|
+
const squaredDiffs = data.reduce((sum, v) => sum + (v - m) ** 2, 0);
|
|
2930
|
+
return squaredDiffs / (data.length - 1);
|
|
2931
|
+
}
|
|
2932
|
+
function standardDeviation(data) {
|
|
2933
|
+
return Math.sqrt(variance(data));
|
|
2934
|
+
}
|
|
2935
|
+
function standardError(data) {
|
|
2936
|
+
if (data.length === 0) return 0;
|
|
2937
|
+
return standardDeviation(data) / Math.sqrt(data.length);
|
|
2938
|
+
}
|
|
2939
|
+
function cohensD(group1, group2) {
|
|
2940
|
+
if (group1.length < 2 || group2.length < 2) return 0;
|
|
2941
|
+
const m1 = mean(group1);
|
|
2942
|
+
const m2 = mean(group2);
|
|
2943
|
+
const v1 = variance(group1);
|
|
2944
|
+
const v2 = variance(group2);
|
|
2945
|
+
const n1 = group1.length;
|
|
2946
|
+
const n2 = group2.length;
|
|
2947
|
+
const pooledVar = ((n1 - 1) * v1 + (n2 - 1) * v2) / (n1 + n2 - 2);
|
|
2948
|
+
const pooledStd = Math.sqrt(pooledVar);
|
|
2949
|
+
if (pooledStd === 0) return 0;
|
|
2950
|
+
return (m1 - m2) / pooledStd;
|
|
2951
|
+
}
|
|
2952
|
+
function tTest(group1, group2, alpha = 0.05, direction = "different") {
|
|
2953
|
+
const n1 = group1.length;
|
|
2954
|
+
const n2 = group2.length;
|
|
2955
|
+
if (n1 < 2 || n2 < 2) {
|
|
2956
|
+
return {
|
|
2957
|
+
statistic: 0,
|
|
2958
|
+
pValue: 1,
|
|
2959
|
+
significant: false,
|
|
2960
|
+
confidenceInterval: [0, 0],
|
|
2961
|
+
effectSize: 0,
|
|
2962
|
+
testName: "welch_t_test",
|
|
2963
|
+
sampleSize: n1 + n2
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
const m1 = mean(group1);
|
|
2967
|
+
const m2 = mean(group2);
|
|
2968
|
+
const v1 = variance(group1);
|
|
2969
|
+
const v2 = variance(group2);
|
|
2970
|
+
const se = Math.sqrt(v1 / n1 + v2 / n2);
|
|
2971
|
+
if (se === 0) {
|
|
2972
|
+
return {
|
|
2973
|
+
statistic: 0,
|
|
2974
|
+
pValue: m1 === m2 ? 1 : 0,
|
|
2975
|
+
significant: m1 !== m2,
|
|
2976
|
+
confidenceInterval: [m1 - m2, m1 - m2],
|
|
2977
|
+
effectSize: 0,
|
|
2978
|
+
testName: "welch_t_test",
|
|
2979
|
+
sampleSize: n1 + n2
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
const t = (m1 - m2) / se;
|
|
2983
|
+
const num = (v1 / n1 + v2 / n2) ** 2;
|
|
2984
|
+
const den = (v1 / n1) ** 2 / (n1 - 1) + (v2 / n2) ** 2 / (n2 - 1);
|
|
2985
|
+
const df = num / den;
|
|
2986
|
+
const pTwoTailed = tDistPValue(Math.abs(t), df);
|
|
2987
|
+
let pValue;
|
|
2988
|
+
switch (direction) {
|
|
2989
|
+
case "greater":
|
|
2990
|
+
pValue = t > 0 ? pTwoTailed / 2 : 1 - pTwoTailed / 2;
|
|
2991
|
+
break;
|
|
2992
|
+
case "less":
|
|
2993
|
+
pValue = t < 0 ? pTwoTailed / 2 : 1 - pTwoTailed / 2;
|
|
2994
|
+
break;
|
|
2995
|
+
case "equal":
|
|
2996
|
+
pValue = 1 - pTwoTailed;
|
|
2997
|
+
break;
|
|
2998
|
+
default:
|
|
2999
|
+
pValue = pTwoTailed;
|
|
3000
|
+
}
|
|
3001
|
+
const tCrit = tDistCritical(alpha, df);
|
|
3002
|
+
const diff = m1 - m2;
|
|
3003
|
+
const ci = [diff - tCrit * se, diff + tCrit * se];
|
|
3004
|
+
return {
|
|
3005
|
+
statistic: t,
|
|
3006
|
+
pValue,
|
|
3007
|
+
significant: pValue < alpha,
|
|
3008
|
+
confidenceInterval: ci,
|
|
3009
|
+
effectSize: cohensD(group1, group2),
|
|
3010
|
+
testName: "welch_t_test",
|
|
3011
|
+
sampleSize: n1 + n2
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
function oneSampleTTest(data, populationMean, alpha = 0.05, direction = "different") {
|
|
3015
|
+
const n = data.length;
|
|
3016
|
+
if (n < 2) {
|
|
3017
|
+
return {
|
|
3018
|
+
statistic: 0,
|
|
3019
|
+
pValue: 1,
|
|
3020
|
+
significant: false,
|
|
3021
|
+
confidenceInterval: [0, 0],
|
|
3022
|
+
effectSize: 0,
|
|
3023
|
+
testName: "one_sample_t_test",
|
|
3024
|
+
sampleSize: n
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
const m = mean(data);
|
|
3028
|
+
const se = standardError(data);
|
|
3029
|
+
if (se === 0) {
|
|
3030
|
+
return {
|
|
3031
|
+
statistic: 0,
|
|
3032
|
+
pValue: m === populationMean ? 1 : 0,
|
|
3033
|
+
significant: m !== populationMean,
|
|
3034
|
+
confidenceInterval: [m, m],
|
|
3035
|
+
effectSize: 0,
|
|
3036
|
+
testName: "one_sample_t_test",
|
|
3037
|
+
sampleSize: n
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
const t = (m - populationMean) / se;
|
|
3041
|
+
const df = n - 1;
|
|
3042
|
+
const pTwoTailed = tDistPValue(Math.abs(t), df);
|
|
3043
|
+
let pValue;
|
|
3044
|
+
switch (direction) {
|
|
3045
|
+
case "greater":
|
|
3046
|
+
pValue = t > 0 ? pTwoTailed / 2 : 1 - pTwoTailed / 2;
|
|
3047
|
+
break;
|
|
3048
|
+
case "less":
|
|
3049
|
+
pValue = t < 0 ? pTwoTailed / 2 : 1 - pTwoTailed / 2;
|
|
3050
|
+
break;
|
|
3051
|
+
default:
|
|
3052
|
+
pValue = pTwoTailed;
|
|
3053
|
+
}
|
|
3054
|
+
const tCrit = tDistCritical(alpha, df);
|
|
3055
|
+
const ci = [m - tCrit * se, m + tCrit * se];
|
|
3056
|
+
const sd = standardDeviation(data);
|
|
3057
|
+
const effectSize = sd > 0 ? (m - populationMean) / sd : 0;
|
|
3058
|
+
return {
|
|
3059
|
+
statistic: t,
|
|
3060
|
+
pValue,
|
|
3061
|
+
significant: pValue < alpha,
|
|
3062
|
+
confidenceInterval: ci,
|
|
3063
|
+
effectSize,
|
|
3064
|
+
testName: "one_sample_t_test",
|
|
3065
|
+
sampleSize: n
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
function mannWhitneyU(group1, group2, alpha = 0.05) {
|
|
3069
|
+
const n1 = group1.length;
|
|
3070
|
+
const n2 = group2.length;
|
|
3071
|
+
if (n1 === 0 || n2 === 0) {
|
|
3072
|
+
return {
|
|
3073
|
+
statistic: 0,
|
|
3074
|
+
pValue: 1,
|
|
3075
|
+
significant: false,
|
|
3076
|
+
confidenceInterval: [0, 0],
|
|
3077
|
+
effectSize: 0,
|
|
3078
|
+
testName: "mann_whitney_u",
|
|
3079
|
+
sampleSize: n1 + n2
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
let u1 = 0;
|
|
3083
|
+
for (const a of group1) {
|
|
3084
|
+
for (const b of group2) {
|
|
3085
|
+
if (a > b) u1++;
|
|
3086
|
+
else if (a === b) u1 += 0.5;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
const u2 = n1 * n2 - u1;
|
|
3090
|
+
const u = Math.min(u1, u2);
|
|
3091
|
+
const meanU = n1 * n2 / 2;
|
|
3092
|
+
const sigmaU = Math.sqrt(n1 * n2 * (n1 + n2 + 1) / 12);
|
|
3093
|
+
if (sigmaU === 0) {
|
|
3094
|
+
return {
|
|
3095
|
+
statistic: u,
|
|
3096
|
+
pValue: 1,
|
|
3097
|
+
significant: false,
|
|
3098
|
+
confidenceInterval: [0, 0],
|
|
3099
|
+
effectSize: 0,
|
|
3100
|
+
testName: "mann_whitney_u",
|
|
3101
|
+
sampleSize: n1 + n2
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
const z = (u - meanU) / sigmaU;
|
|
3105
|
+
const pValue = 2 * normalCDF(-Math.abs(z));
|
|
3106
|
+
const effectSize = 1 - 2 * u / (n1 * n2);
|
|
3107
|
+
return {
|
|
3108
|
+
statistic: u,
|
|
3109
|
+
pValue,
|
|
3110
|
+
significant: pValue < alpha,
|
|
3111
|
+
confidenceInterval: [0, 0],
|
|
3112
|
+
// CI not standard for U-test
|
|
3113
|
+
effectSize,
|
|
3114
|
+
testName: "mann_whitney_u",
|
|
3115
|
+
sampleSize: n1 + n2
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3118
|
+
function chiSquaredTest(observed, expected, alpha = 0.05) {
|
|
3119
|
+
if (observed.length !== expected.length || observed.length === 0) {
|
|
3120
|
+
return {
|
|
3121
|
+
statistic: 0,
|
|
3122
|
+
pValue: 1,
|
|
3123
|
+
significant: false,
|
|
3124
|
+
confidenceInterval: [0, 0],
|
|
3125
|
+
effectSize: 0,
|
|
3126
|
+
testName: "chi_squared",
|
|
3127
|
+
sampleSize: 0
|
|
3128
|
+
};
|
|
3129
|
+
}
|
|
3130
|
+
let chiSq = 0;
|
|
3131
|
+
let totalObserved = 0;
|
|
3132
|
+
for (let i = 0; i < observed.length; i++) {
|
|
3133
|
+
if (expected[i] <= 0) continue;
|
|
3134
|
+
chiSq += (observed[i] - expected[i]) ** 2 / expected[i];
|
|
3135
|
+
totalObserved += observed[i];
|
|
3136
|
+
}
|
|
3137
|
+
const df = observed.length - 1;
|
|
3138
|
+
if (df <= 0) {
|
|
3139
|
+
return {
|
|
3140
|
+
statistic: chiSq,
|
|
3141
|
+
pValue: 1,
|
|
3142
|
+
significant: false,
|
|
3143
|
+
confidenceInterval: [0, 0],
|
|
3144
|
+
effectSize: 0,
|
|
3145
|
+
testName: "chi_squared",
|
|
3146
|
+
sampleSize: totalObserved
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
const pValue = chiSquaredPValue(chiSq, df);
|
|
3150
|
+
const k = observed.length;
|
|
3151
|
+
const effectSize = totalObserved > 0 ? Math.sqrt(chiSq / (totalObserved * (k - 1))) : 0;
|
|
3152
|
+
return {
|
|
3153
|
+
statistic: chiSq,
|
|
3154
|
+
pValue,
|
|
3155
|
+
significant: pValue < alpha,
|
|
3156
|
+
confidenceInterval: [0, 0],
|
|
3157
|
+
effectSize,
|
|
3158
|
+
testName: "chi_squared",
|
|
3159
|
+
sampleSize: totalObserved
|
|
3160
|
+
};
|
|
3161
|
+
}
|
|
3162
|
+
function extractMetric(results, metricName) {
|
|
3163
|
+
return results.filter((r) => metricName in r.values).map((r) => r.values[metricName]);
|
|
3164
|
+
}
|
|
3165
|
+
function groupByParameter(results, paramName) {
|
|
3166
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3167
|
+
for (const r of results) {
|
|
3168
|
+
const val = r.params[paramName];
|
|
3169
|
+
if (val === void 0) continue;
|
|
3170
|
+
if (!groups.has(val)) groups.set(val, []);
|
|
3171
|
+
groups.get(val).push(r);
|
|
3172
|
+
}
|
|
3173
|
+
return groups;
|
|
3174
|
+
}
|
|
3175
|
+
function summarizeMetrics(results, metricName) {
|
|
3176
|
+
const values = extractMetric(results, metricName);
|
|
3177
|
+
if (values.length === 0) {
|
|
3178
|
+
return { mean: 0, std: 0, min: 0, max: 0, n: 0 };
|
|
3179
|
+
}
|
|
3180
|
+
return {
|
|
3181
|
+
mean: mean(values),
|
|
3182
|
+
std: standardDeviation(values),
|
|
3183
|
+
min: Math.min(...values),
|
|
3184
|
+
max: Math.max(...values),
|
|
3185
|
+
n: values.length
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
function normalCDF(x) {
|
|
3189
|
+
if (x < -8) return 0;
|
|
3190
|
+
if (x > 8) return 1;
|
|
3191
|
+
const a1 = 0.254829592;
|
|
3192
|
+
const a2 = -0.284496736;
|
|
3193
|
+
const a3 = 1.421413741;
|
|
3194
|
+
const a4 = -1.453152027;
|
|
3195
|
+
const a5 = 1.061405429;
|
|
3196
|
+
const p = 0.3275911;
|
|
3197
|
+
const sign2 = x < 0 ? -1 : 1;
|
|
3198
|
+
const absX = Math.abs(x);
|
|
3199
|
+
const t = 1 / (1 + p * absX);
|
|
3200
|
+
const y = 1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-absX * absX / 2);
|
|
3201
|
+
return 0.5 * (1 + sign2 * y);
|
|
3202
|
+
}
|
|
3203
|
+
function tDistPValue(absT, df) {
|
|
3204
|
+
if (df <= 0) return 1;
|
|
3205
|
+
const z = absT * (1 - 1 / (4 * df)) / Math.sqrt(1 + absT * absT / (2 * df));
|
|
3206
|
+
return 2 * (1 - normalCDF(z));
|
|
3207
|
+
}
|
|
3208
|
+
function tDistCritical(alpha, df) {
|
|
3209
|
+
const p = 1 - alpha / 2;
|
|
3210
|
+
const a = p - 0.5;
|
|
3211
|
+
const r = a * a;
|
|
3212
|
+
let z = a * (2.50662823884 + r * (-18.61500062529 + r * (41.39119773534 + r * -25.44106049637))) / (1 + r * (-8.4735109309 + r * (23.08336743743 + r * (-21.06224101826 + r * 3.13082909833))));
|
|
3213
|
+
const g1 = (z ** 3 + z) / (4 * df);
|
|
3214
|
+
const g2 = (5 * z ** 5 + 16 * z ** 3 + 3 * z) / (96 * df * df);
|
|
3215
|
+
z = z + g1 + g2;
|
|
3216
|
+
return Math.abs(z);
|
|
3217
|
+
}
|
|
3218
|
+
function chiSquaredPValue(chiSq, df) {
|
|
3219
|
+
if (df <= 0) return 1;
|
|
3220
|
+
if (chiSq <= 0) return 1;
|
|
3221
|
+
const z = Math.pow(chiSq / df, 1 / 3) - (1 - 2 / (9 * df));
|
|
3222
|
+
const denom = Math.sqrt(2 / (9 * df));
|
|
3223
|
+
return 1 - normalCDF(z / denom);
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
// src/traits/SimulationLabTraits.ts
|
|
3227
|
+
var SimulationLabTraits = {
|
|
3228
|
+
simulation_lab: {
|
|
3229
|
+
name: "@simulation_lab",
|
|
3230
|
+
description: "Attaches scientific hypothesis testing to any HoloScript composition. Enables parameter sweeps, multi-epoch execution, statistical analysis, and exportable results. The universe gets one sample \u2014 HoloScript gives you infinite. Results are marketplace-sellable as verified simulation data.",
|
|
3231
|
+
params: {
|
|
3232
|
+
hypothesis: {
|
|
3233
|
+
type: "string",
|
|
3234
|
+
required: true,
|
|
3235
|
+
description: "Human-readable hypothesis description."
|
|
3236
|
+
},
|
|
3237
|
+
null_hypothesis: {
|
|
3238
|
+
type: "string",
|
|
3239
|
+
required: false,
|
|
3240
|
+
default: "",
|
|
3241
|
+
description: "Formal null hypothesis (H0). Auto-generated if empty."
|
|
3242
|
+
},
|
|
3243
|
+
alternative_hypothesis: {
|
|
3244
|
+
type: "string",
|
|
3245
|
+
required: false,
|
|
3246
|
+
default: "",
|
|
3247
|
+
description: "Formal alternative hypothesis (H1). Auto-generated if empty."
|
|
3248
|
+
},
|
|
3249
|
+
direction: {
|
|
3250
|
+
type: "string",
|
|
3251
|
+
required: false,
|
|
3252
|
+
default: "different",
|
|
3253
|
+
description: "Expected direction of effect: greater | less | different | equal."
|
|
3254
|
+
},
|
|
3255
|
+
epochs: {
|
|
3256
|
+
type: "number",
|
|
3257
|
+
required: false,
|
|
3258
|
+
default: 1e3,
|
|
3259
|
+
description: "Number of simulation runs per parameter combination. More = higher power."
|
|
3260
|
+
},
|
|
3261
|
+
seed_start: {
|
|
3262
|
+
type: "number",
|
|
3263
|
+
required: false,
|
|
3264
|
+
default: 0,
|
|
3265
|
+
description: "Starting seed for reproducibility. Seeds increment per epoch."
|
|
3266
|
+
},
|
|
3267
|
+
confidence_level: {
|
|
3268
|
+
type: "number",
|
|
3269
|
+
required: false,
|
|
3270
|
+
default: 0.95,
|
|
3271
|
+
description: "Confidence level for statistical tests (0.0 - 1.0). Default 0.95 = \u03B1=0.05."
|
|
3272
|
+
},
|
|
3273
|
+
metrics: {
|
|
3274
|
+
type: "string[]",
|
|
3275
|
+
required: true,
|
|
3276
|
+
description: "List of metric names to collect each epoch. Must match emitted metric events."
|
|
3277
|
+
},
|
|
3278
|
+
statistical_test: {
|
|
3279
|
+
type: "string",
|
|
3280
|
+
required: false,
|
|
3281
|
+
default: "auto",
|
|
3282
|
+
description: "Statistical test to use: t_test | mann_whitney_u | chi_squared | auto. Auto selects based on data distribution."
|
|
3283
|
+
},
|
|
3284
|
+
export_training_data: {
|
|
3285
|
+
type: "boolean",
|
|
3286
|
+
required: false,
|
|
3287
|
+
default: false,
|
|
3288
|
+
description: "Whether to export results as training data for DataForge pipeline."
|
|
3289
|
+
},
|
|
3290
|
+
marketplace_publishable: {
|
|
3291
|
+
type: "boolean",
|
|
3292
|
+
required: false,
|
|
3293
|
+
default: false,
|
|
3294
|
+
description: "Whether to package results for marketplace listing as verified simulation data."
|
|
3295
|
+
},
|
|
3296
|
+
max_duration_ms: {
|
|
3297
|
+
type: "number",
|
|
3298
|
+
required: false,
|
|
3299
|
+
default: 3e5,
|
|
3300
|
+
description: "Maximum wall-clock time for the entire experiment (5 min default). Prevents runaway simulations."
|
|
3301
|
+
},
|
|
3302
|
+
worker_count: {
|
|
3303
|
+
type: "number",
|
|
3304
|
+
required: false,
|
|
3305
|
+
default: 4,
|
|
3306
|
+
description: "Number of parallel workers for epoch execution."
|
|
3307
|
+
}
|
|
3308
|
+
},
|
|
3309
|
+
validator: (params) => {
|
|
3310
|
+
if (!params.hypothesis || typeof params.hypothesis !== "string") return false;
|
|
3311
|
+
if (!params.metrics || !Array.isArray(params.metrics) || params.metrics.length === 0)
|
|
3312
|
+
return false;
|
|
3313
|
+
if (params.epochs !== void 0 && (params.epochs < 1 || params.epochs > 1e6))
|
|
3314
|
+
return false;
|
|
3315
|
+
if (params.confidence_level !== void 0 && (params.confidence_level <= 0 || params.confidence_level >= 1))
|
|
3316
|
+
return false;
|
|
3317
|
+
if (params.direction !== void 0) {
|
|
3318
|
+
if (!["greater", "less", "different", "equal"].includes(params.direction)) return false;
|
|
3319
|
+
}
|
|
3320
|
+
if (params.statistical_test !== void 0) {
|
|
3321
|
+
if (!["t_test", "mann_whitney_u", "chi_squared", "auto"].includes(params.statistical_test))
|
|
3322
|
+
return false;
|
|
3323
|
+
}
|
|
3324
|
+
return true;
|
|
3325
|
+
},
|
|
3326
|
+
composesWith: [
|
|
3327
|
+
"@tradeable",
|
|
3328
|
+
"@depreciating",
|
|
3329
|
+
"@bonding_curved",
|
|
3330
|
+
"@taxable_wealth",
|
|
3331
|
+
"@pid_controlled"
|
|
3332
|
+
],
|
|
3333
|
+
compiler_hints: {
|
|
3334
|
+
requires_runtime: [
|
|
3335
|
+
"SimulationRuntime.runExperiment",
|
|
3336
|
+
"SimulationRuntime.collectMetrics",
|
|
3337
|
+
"SimulationRuntime.runStatisticalTest",
|
|
3338
|
+
"SimulationRuntime.exportResults"
|
|
3339
|
+
],
|
|
3340
|
+
thread_safety: "worker_thread",
|
|
3341
|
+
performance_budget_ms: 3e5,
|
|
3342
|
+
// Experiment budget, not per-frame
|
|
3343
|
+
batch_mode: true
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
};
|
|
3347
|
+
function getSimulationTraitNames() {
|
|
3348
|
+
return Object.keys(SimulationLabTraits).map((k) => SimulationLabTraits[k].name);
|
|
3349
|
+
}
|
|
3350
|
+
function getSimulationTrait(name) {
|
|
3351
|
+
const key = name.startsWith("@") ? name.slice(1) : name;
|
|
3352
|
+
return SimulationLabTraits[key];
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
// src/time.ts
|
|
3356
|
+
function now() {
|
|
3357
|
+
return Date.now();
|
|
3358
|
+
}
|
|
3359
|
+
function hrTime() {
|
|
3360
|
+
if (typeof performance !== "undefined") {
|
|
3361
|
+
return performance.now();
|
|
3362
|
+
}
|
|
3363
|
+
return Date.now();
|
|
3364
|
+
}
|
|
3365
|
+
function sleep(ms) {
|
|
3366
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3367
|
+
}
|
|
3368
|
+
async function waitUntil(condition, options = {}) {
|
|
3369
|
+
const { timeout = 3e4, interval = 100 } = options;
|
|
3370
|
+
const startTime = now();
|
|
3371
|
+
while (true) {
|
|
3372
|
+
if (await condition()) return;
|
|
3373
|
+
if (now() - startTime > timeout) {
|
|
3374
|
+
throw new Error("waitUntil timeout");
|
|
3375
|
+
}
|
|
3376
|
+
await sleep(interval);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
async function withTimeout(fn, ms) {
|
|
3380
|
+
return Promise.race([
|
|
3381
|
+
fn(),
|
|
3382
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), ms))
|
|
3383
|
+
]);
|
|
3384
|
+
}
|
|
3385
|
+
async function retry(fn, options = {}) {
|
|
3386
|
+
const { maxAttempts = 3, initialDelay = 1e3, maxDelay = 3e4, factor = 2, onRetry } = options;
|
|
3387
|
+
let lastError;
|
|
3388
|
+
let delay = initialDelay;
|
|
3389
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3390
|
+
try {
|
|
3391
|
+
return await fn();
|
|
3392
|
+
} catch (error) {
|
|
3393
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
3394
|
+
if (attempt === maxAttempts) break;
|
|
3395
|
+
onRetry?.(lastError, attempt);
|
|
3396
|
+
await sleep(delay);
|
|
3397
|
+
delay = Math.min(delay * factor, maxDelay);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
throw lastError;
|
|
3401
|
+
}
|
|
3402
|
+
function debounce(fn, ms) {
|
|
3403
|
+
let timeoutId = null;
|
|
3404
|
+
return ((...args) => {
|
|
3405
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
3406
|
+
timeoutId = setTimeout(() => {
|
|
3407
|
+
timeoutId = null;
|
|
3408
|
+
fn(...args);
|
|
3409
|
+
}, ms);
|
|
3410
|
+
});
|
|
3411
|
+
}
|
|
3412
|
+
function throttle(fn, ms) {
|
|
3413
|
+
let lastCall = 0;
|
|
3414
|
+
let timeoutId = null;
|
|
3415
|
+
return ((...args) => {
|
|
3416
|
+
const elapsed = now() - lastCall;
|
|
3417
|
+
if (elapsed >= ms) {
|
|
3418
|
+
lastCall = now();
|
|
3419
|
+
fn(...args);
|
|
3420
|
+
} else if (!timeoutId) {
|
|
3421
|
+
timeoutId = setTimeout(() => {
|
|
3422
|
+
lastCall = now();
|
|
3423
|
+
timeoutId = null;
|
|
3424
|
+
fn(...args);
|
|
3425
|
+
}, ms - elapsed);
|
|
3426
|
+
}
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
async function measure(fn) {
|
|
3430
|
+
const start = hrTime();
|
|
3431
|
+
const result = await fn();
|
|
3432
|
+
const duration = hrTime() - start;
|
|
3433
|
+
return { result, duration };
|
|
3434
|
+
}
|
|
3435
|
+
async function timed(label, fn) {
|
|
3436
|
+
const start = hrTime();
|
|
3437
|
+
try {
|
|
3438
|
+
return await fn();
|
|
3439
|
+
} finally {
|
|
3440
|
+
const duration = hrTime() - start;
|
|
3441
|
+
void duration;
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
var Stopwatch = class {
|
|
3445
|
+
startTime = 0;
|
|
3446
|
+
pausedTime = 0;
|
|
3447
|
+
running = false;
|
|
3448
|
+
start() {
|
|
3449
|
+
if (!this.running) {
|
|
3450
|
+
this.startTime = hrTime() - this.pausedTime;
|
|
3451
|
+
this.running = true;
|
|
3452
|
+
}
|
|
3453
|
+
return this;
|
|
3454
|
+
}
|
|
3455
|
+
stop() {
|
|
3456
|
+
if (this.running) {
|
|
3457
|
+
this.pausedTime = hrTime() - this.startTime;
|
|
3458
|
+
this.running = false;
|
|
3459
|
+
}
|
|
3460
|
+
return this.pausedTime;
|
|
3461
|
+
}
|
|
3462
|
+
reset() {
|
|
3463
|
+
this.startTime = 0;
|
|
3464
|
+
this.pausedTime = 0;
|
|
3465
|
+
this.running = false;
|
|
3466
|
+
return this;
|
|
3467
|
+
}
|
|
3468
|
+
restart() {
|
|
3469
|
+
return this.reset().start();
|
|
3470
|
+
}
|
|
3471
|
+
get elapsed() {
|
|
3472
|
+
if (this.running) {
|
|
3473
|
+
return hrTime() - this.startTime;
|
|
3474
|
+
}
|
|
3475
|
+
return this.pausedTime;
|
|
3476
|
+
}
|
|
3477
|
+
get isRunning() {
|
|
3478
|
+
return this.running;
|
|
3479
|
+
}
|
|
3480
|
+
lap() {
|
|
3481
|
+
const elapsed = this.elapsed;
|
|
3482
|
+
this.restart();
|
|
3483
|
+
return elapsed;
|
|
3484
|
+
}
|
|
3485
|
+
};
|
|
3486
|
+
var IntervalTimer = class {
|
|
3487
|
+
intervalId = null;
|
|
3488
|
+
callback;
|
|
3489
|
+
intervalMs;
|
|
3490
|
+
isPaused = false;
|
|
3491
|
+
constructor(callback, intervalMs) {
|
|
3492
|
+
this.callback = callback;
|
|
3493
|
+
this.intervalMs = intervalMs;
|
|
3494
|
+
}
|
|
3495
|
+
start() {
|
|
3496
|
+
if (!this.intervalId && !this.isPaused) {
|
|
3497
|
+
this.intervalId = setInterval(this.callback, this.intervalMs);
|
|
3498
|
+
}
|
|
3499
|
+
return this;
|
|
3500
|
+
}
|
|
3501
|
+
stop() {
|
|
3502
|
+
if (this.intervalId) {
|
|
3503
|
+
clearInterval(this.intervalId);
|
|
3504
|
+
this.intervalId = null;
|
|
3505
|
+
}
|
|
3506
|
+
this.isPaused = false;
|
|
3507
|
+
return this;
|
|
3508
|
+
}
|
|
3509
|
+
pause() {
|
|
3510
|
+
if (this.intervalId) {
|
|
3511
|
+
clearInterval(this.intervalId);
|
|
3512
|
+
this.intervalId = null;
|
|
3513
|
+
this.isPaused = true;
|
|
3514
|
+
}
|
|
3515
|
+
return this;
|
|
3516
|
+
}
|
|
3517
|
+
resume() {
|
|
3518
|
+
if (this.isPaused) {
|
|
3519
|
+
this.isPaused = false;
|
|
3520
|
+
this.intervalId = setInterval(this.callback, this.intervalMs);
|
|
3521
|
+
}
|
|
3522
|
+
return this;
|
|
3523
|
+
}
|
|
3524
|
+
get running() {
|
|
3525
|
+
return this.intervalId !== null;
|
|
3526
|
+
}
|
|
3527
|
+
get paused() {
|
|
3528
|
+
return this.isPaused;
|
|
3529
|
+
}
|
|
3530
|
+
};
|
|
3531
|
+
var CountdownTimer = class {
|
|
3532
|
+
remainingMs;
|
|
3533
|
+
startTime = 0;
|
|
3534
|
+
running = false;
|
|
3535
|
+
onTick;
|
|
3536
|
+
onComplete;
|
|
3537
|
+
tickIntervalId = null;
|
|
3538
|
+
constructor(durationMs, options = {}) {
|
|
3539
|
+
this.remainingMs = durationMs;
|
|
3540
|
+
this.onTick = options.onTick;
|
|
3541
|
+
this.onComplete = options.onComplete;
|
|
3542
|
+
if (options.onTick && options.tickInterval) {
|
|
3543
|
+
this.tickIntervalId = setInterval(() => {
|
|
3544
|
+
if (this.running) {
|
|
3545
|
+
this.onTick?.(this.remaining);
|
|
3546
|
+
}
|
|
3547
|
+
}, options.tickInterval);
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
start() {
|
|
3551
|
+
if (!this.running && this.remainingMs > 0) {
|
|
3552
|
+
this.startTime = hrTime();
|
|
3553
|
+
this.running = true;
|
|
3554
|
+
this.scheduleCompletion();
|
|
3555
|
+
}
|
|
3556
|
+
return this;
|
|
3557
|
+
}
|
|
3558
|
+
pause() {
|
|
3559
|
+
if (this.running) {
|
|
3560
|
+
this.remainingMs = Math.max(0, this.remainingMs - (hrTime() - this.startTime));
|
|
3561
|
+
this.running = false;
|
|
3562
|
+
}
|
|
3563
|
+
return this;
|
|
3564
|
+
}
|
|
3565
|
+
resume() {
|
|
3566
|
+
return this.start();
|
|
3567
|
+
}
|
|
3568
|
+
reset(durationMs) {
|
|
3569
|
+
this.running = false;
|
|
3570
|
+
this.remainingMs = durationMs ?? this.remainingMs;
|
|
3571
|
+
return this;
|
|
3572
|
+
}
|
|
3573
|
+
stop() {
|
|
3574
|
+
this.running = false;
|
|
3575
|
+
if (this.tickIntervalId) {
|
|
3576
|
+
clearInterval(this.tickIntervalId);
|
|
3577
|
+
this.tickIntervalId = null;
|
|
3578
|
+
}
|
|
3579
|
+
return this;
|
|
3580
|
+
}
|
|
3581
|
+
get remaining() {
|
|
3582
|
+
if (this.running) {
|
|
3583
|
+
return Math.max(0, this.remainingMs - (hrTime() - this.startTime));
|
|
3584
|
+
}
|
|
3585
|
+
return this.remainingMs;
|
|
3586
|
+
}
|
|
3587
|
+
get isRunning() {
|
|
3588
|
+
return this.running;
|
|
3589
|
+
}
|
|
3590
|
+
get isComplete() {
|
|
3591
|
+
return this.remaining <= 0;
|
|
3592
|
+
}
|
|
3593
|
+
scheduleCompletion() {
|
|
3594
|
+
setTimeout(() => {
|
|
3595
|
+
if (this.running && this.remaining <= 0) {
|
|
3596
|
+
this.running = false;
|
|
3597
|
+
this.onComplete?.();
|
|
3598
|
+
}
|
|
3599
|
+
}, this.remainingMs);
|
|
3600
|
+
}
|
|
3601
|
+
};
|
|
3602
|
+
var FrameTimer = class {
|
|
3603
|
+
lastFrameTime = 0;
|
|
3604
|
+
deltaTime = 0;
|
|
3605
|
+
frameCount = 0;
|
|
3606
|
+
fps = 0;
|
|
3607
|
+
fpsUpdateInterval = 1e3;
|
|
3608
|
+
lastFpsUpdate = 0;
|
|
3609
|
+
framesSinceLastFpsUpdate = 0;
|
|
3610
|
+
/**
|
|
3611
|
+
* Call at the start of each frame
|
|
3612
|
+
*/
|
|
3613
|
+
update() {
|
|
3614
|
+
const currentTime = hrTime();
|
|
3615
|
+
if (this.lastFrameTime > 0) {
|
|
3616
|
+
this.deltaTime = (currentTime - this.lastFrameTime) / 1e3;
|
|
3617
|
+
}
|
|
3618
|
+
this.lastFrameTime = currentTime;
|
|
3619
|
+
this.frameCount++;
|
|
3620
|
+
this.framesSinceLastFpsUpdate++;
|
|
3621
|
+
if (currentTime - this.lastFpsUpdate >= this.fpsUpdateInterval) {
|
|
3622
|
+
this.fps = this.framesSinceLastFpsUpdate * 1e3 / (currentTime - this.lastFpsUpdate);
|
|
3623
|
+
this.lastFpsUpdate = currentTime;
|
|
3624
|
+
this.framesSinceLastFpsUpdate = 0;
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Get delta time in seconds since last frame
|
|
3629
|
+
*/
|
|
3630
|
+
get delta() {
|
|
3631
|
+
return this.deltaTime;
|
|
3632
|
+
}
|
|
3633
|
+
/**
|
|
3634
|
+
* Get delta time in milliseconds since last frame
|
|
3635
|
+
*/
|
|
3636
|
+
get deltaMs() {
|
|
3637
|
+
return this.deltaTime * 1e3;
|
|
3638
|
+
}
|
|
3639
|
+
/**
|
|
3640
|
+
* Get total frame count
|
|
3641
|
+
*/
|
|
3642
|
+
get frames() {
|
|
3643
|
+
return this.frameCount;
|
|
3644
|
+
}
|
|
3645
|
+
/**
|
|
3646
|
+
* Get current FPS (frames per second)
|
|
3647
|
+
*/
|
|
3648
|
+
get currentFps() {
|
|
3649
|
+
return this.fps;
|
|
3650
|
+
}
|
|
3651
|
+
/**
|
|
3652
|
+
* Get total elapsed time in seconds
|
|
3653
|
+
*/
|
|
3654
|
+
get totalTime() {
|
|
3655
|
+
return this.lastFrameTime / 1e3;
|
|
3656
|
+
}
|
|
3657
|
+
/**
|
|
3658
|
+
* Reset the timer
|
|
3659
|
+
*/
|
|
3660
|
+
reset() {
|
|
3661
|
+
this.lastFrameTime = 0;
|
|
3662
|
+
this.deltaTime = 0;
|
|
3663
|
+
this.frameCount = 0;
|
|
3664
|
+
this.fps = 0;
|
|
3665
|
+
this.lastFpsUpdate = 0;
|
|
3666
|
+
this.framesSinceLastFpsUpdate = 0;
|
|
3667
|
+
}
|
|
3668
|
+
};
|
|
3669
|
+
function schedule(callback, delayMs) {
|
|
3670
|
+
const timeoutId = setTimeout(callback, delayMs);
|
|
3671
|
+
return () => clearTimeout(timeoutId);
|
|
3672
|
+
}
|
|
3673
|
+
function scheduleInterval(callback, intervalMs) {
|
|
3674
|
+
const intervalId = setInterval(callback, intervalMs);
|
|
3675
|
+
return () => clearInterval(intervalId);
|
|
3676
|
+
}
|
|
3677
|
+
function scheduleFrame(callback) {
|
|
3678
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
3679
|
+
const id2 = requestAnimationFrame(callback);
|
|
3680
|
+
return () => cancelAnimationFrame(id2);
|
|
3681
|
+
}
|
|
3682
|
+
const id = setTimeout(() => callback(hrTime()), 16);
|
|
3683
|
+
return () => clearTimeout(id);
|
|
3684
|
+
}
|
|
3685
|
+
function createTicker(callback, targetFps = 60) {
|
|
3686
|
+
const frameTime = 1e3 / targetFps;
|
|
3687
|
+
let lastTime = 0;
|
|
3688
|
+
let running = false;
|
|
3689
|
+
let animationId;
|
|
3690
|
+
const tick = (currentTime) => {
|
|
3691
|
+
if (!running) return;
|
|
3692
|
+
if (lastTime > 0) {
|
|
3693
|
+
const delta = (currentTime - lastTime) / 1e3;
|
|
3694
|
+
callback(delta);
|
|
3695
|
+
}
|
|
3696
|
+
lastTime = currentTime;
|
|
3697
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
3698
|
+
animationId = requestAnimationFrame(tick);
|
|
3699
|
+
} else {
|
|
3700
|
+
animationId = setTimeout(() => tick(hrTime()), frameTime);
|
|
3701
|
+
}
|
|
3702
|
+
};
|
|
3703
|
+
return {
|
|
3704
|
+
start: () => {
|
|
3705
|
+
if (!running) {
|
|
3706
|
+
running = true;
|
|
3707
|
+
lastTime = 0;
|
|
3708
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
3709
|
+
animationId = requestAnimationFrame(tick);
|
|
3710
|
+
} else {
|
|
3711
|
+
tick(hrTime());
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
},
|
|
3715
|
+
stop: () => {
|
|
3716
|
+
running = false;
|
|
3717
|
+
if (typeof cancelAnimationFrame !== "undefined") {
|
|
3718
|
+
cancelAnimationFrame(animationId);
|
|
3719
|
+
} else {
|
|
3720
|
+
clearTimeout(animationId);
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
var DateTime = {
|
|
3726
|
+
/**
|
|
3727
|
+
* Format a date as ISO string
|
|
3728
|
+
*/
|
|
3729
|
+
toISO(date = /* @__PURE__ */ new Date()) {
|
|
3730
|
+
return date.toISOString();
|
|
3731
|
+
},
|
|
3732
|
+
/**
|
|
3733
|
+
* Format a date as local string
|
|
3734
|
+
*/
|
|
3735
|
+
toLocal(date = /* @__PURE__ */ new Date()) {
|
|
3736
|
+
return date.toLocaleString();
|
|
3737
|
+
},
|
|
3738
|
+
/**
|
|
3739
|
+
* Format a date using a pattern
|
|
3740
|
+
* Supports: YYYY, MM, DD, HH, mm, ss, SSS
|
|
3741
|
+
*/
|
|
3742
|
+
format(date, pattern) {
|
|
3743
|
+
const pad = (n, width) => String(n).padStart(width, "0");
|
|
3744
|
+
return pattern.replace("YYYY", String(date.getFullYear())).replace("MM", pad(date.getMonth() + 1, 2)).replace("DD", pad(date.getDate(), 2)).replace("HH", pad(date.getHours(), 2)).replace("mm", pad(date.getMinutes(), 2)).replace("ss", pad(date.getSeconds(), 2)).replace("SSS", pad(date.getMilliseconds(), 3));
|
|
3745
|
+
},
|
|
3746
|
+
/**
|
|
3747
|
+
* Parse a date string
|
|
3748
|
+
*/
|
|
3749
|
+
parse(dateString) {
|
|
3750
|
+
return new Date(dateString);
|
|
3751
|
+
},
|
|
3752
|
+
/**
|
|
3753
|
+
* Get difference between two dates in various units
|
|
3754
|
+
*/
|
|
3755
|
+
diff(date1, date2, unit = "ms") {
|
|
3756
|
+
const diffMs = date1.getTime() - date2.getTime();
|
|
3757
|
+
switch (unit) {
|
|
3758
|
+
case "ms":
|
|
3759
|
+
return diffMs;
|
|
3760
|
+
case "s":
|
|
3761
|
+
return diffMs / 1e3;
|
|
3762
|
+
case "m":
|
|
3763
|
+
return diffMs / 6e4;
|
|
3764
|
+
case "h":
|
|
3765
|
+
return diffMs / 36e5;
|
|
3766
|
+
case "d":
|
|
3767
|
+
return diffMs / 864e5;
|
|
3768
|
+
}
|
|
3769
|
+
},
|
|
3770
|
+
/**
|
|
3771
|
+
* Add time to a date
|
|
3772
|
+
*/
|
|
3773
|
+
add(date, amount, unit) {
|
|
3774
|
+
const _ms = DateTime.diff(new Date(amount), /* @__PURE__ */ new Date(0), "ms");
|
|
3775
|
+
const multipliers = { ms: 1, s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
3776
|
+
return new Date(date.getTime() + amount * multipliers[unit]);
|
|
3777
|
+
},
|
|
3778
|
+
/**
|
|
3779
|
+
* Check if a date is today
|
|
3780
|
+
*/
|
|
3781
|
+
isToday(date) {
|
|
3782
|
+
const today = /* @__PURE__ */ new Date();
|
|
3783
|
+
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
3784
|
+
},
|
|
3785
|
+
/**
|
|
3786
|
+
* Get start of day
|
|
3787
|
+
*/
|
|
3788
|
+
startOfDay(date = /* @__PURE__ */ new Date()) {
|
|
3789
|
+
const result = new Date(date);
|
|
3790
|
+
result.setHours(0, 0, 0, 0);
|
|
3791
|
+
return result;
|
|
3792
|
+
},
|
|
3793
|
+
/**
|
|
3794
|
+
* Get end of day
|
|
3795
|
+
*/
|
|
3796
|
+
endOfDay(date = /* @__PURE__ */ new Date()) {
|
|
3797
|
+
const result = new Date(date);
|
|
3798
|
+
result.setHours(23, 59, 59, 999);
|
|
3799
|
+
return result;
|
|
3800
|
+
}
|
|
3801
|
+
};
|
|
3802
|
+
|
|
3803
|
+
// src/index.ts
|
|
3804
|
+
function print(...args) {
|
|
3805
|
+
console.log(...args);
|
|
3806
|
+
}
|
|
3807
|
+
function printError(...args) {
|
|
3808
|
+
console.error(...args);
|
|
3809
|
+
}
|
|
3810
|
+
function printWarn(...args) {
|
|
3811
|
+
console.warn(...args);
|
|
3812
|
+
}
|
|
3813
|
+
function assert(condition, message) {
|
|
3814
|
+
if (!condition) {
|
|
3815
|
+
throw new Error(message ?? "Assertion failed");
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
function assertDefined(value, message) {
|
|
3819
|
+
if (value === null || value === void 0) {
|
|
3820
|
+
throw new Error(message ?? "Value is null or undefined");
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
function unreachable(message) {
|
|
3824
|
+
throw new Error(message ?? "Unreachable code reached");
|
|
3825
|
+
}
|
|
3826
|
+
function todo(message) {
|
|
3827
|
+
throw new Error(`TODO: ${message ?? "Not implemented"}`);
|
|
3828
|
+
}
|
|
3829
|
+
function clone(value) {
|
|
3830
|
+
if (value === null || typeof value !== "object") {
|
|
3831
|
+
return value;
|
|
3832
|
+
}
|
|
3833
|
+
if (Array.isArray(value)) {
|
|
3834
|
+
return value.map(clone);
|
|
3835
|
+
}
|
|
3836
|
+
if (value instanceof Date) {
|
|
3837
|
+
return new Date(value.getTime());
|
|
3838
|
+
}
|
|
3839
|
+
if (value instanceof Map) {
|
|
3840
|
+
return new Map([...value].map(([k, v]) => [clone(k), clone(v)]));
|
|
3841
|
+
}
|
|
3842
|
+
if (value instanceof Set) {
|
|
3843
|
+
return new Set([...value].map(clone));
|
|
3844
|
+
}
|
|
3845
|
+
const result = {};
|
|
3846
|
+
for (const key in value) {
|
|
3847
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
3848
|
+
result[key] = clone(value[key]);
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
return result;
|
|
3852
|
+
}
|
|
3853
|
+
function equals(a, b) {
|
|
3854
|
+
if (a === b) return true;
|
|
3855
|
+
if (a === null || b === null) return false;
|
|
3856
|
+
if (typeof a !== typeof b) return false;
|
|
3857
|
+
if (typeof a !== "object") return false;
|
|
3858
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
3859
|
+
if (a.length !== b.length) return false;
|
|
3860
|
+
return a.every((item, i) => equals(item, b[i]));
|
|
3861
|
+
}
|
|
3862
|
+
if (a instanceof Date && b instanceof Date) {
|
|
3863
|
+
return a.getTime() === b.getTime();
|
|
3864
|
+
}
|
|
3865
|
+
if (a instanceof Map && b instanceof Map) {
|
|
3866
|
+
if (a.size !== b.size) return false;
|
|
3867
|
+
for (const [key, value] of a) {
|
|
3868
|
+
if (!b.has(key) || !equals(value, b.get(key))) return false;
|
|
3869
|
+
}
|
|
3870
|
+
return true;
|
|
3871
|
+
}
|
|
3872
|
+
if (a instanceof Set && b instanceof Set) {
|
|
3873
|
+
if (a.size !== b.size) return false;
|
|
3874
|
+
for (const value of a) {
|
|
3875
|
+
if (!b.has(value)) return false;
|
|
3876
|
+
}
|
|
3877
|
+
return true;
|
|
3878
|
+
}
|
|
3879
|
+
const keysA = Object.keys(a);
|
|
3880
|
+
const keysB = Object.keys(b);
|
|
3881
|
+
if (keysA.length !== keysB.length) return false;
|
|
3882
|
+
return keysA.every(
|
|
3883
|
+
(key) => equals(a[key], b[key])
|
|
3884
|
+
);
|
|
3885
|
+
}
|
|
3886
|
+
function pipe(value, ...fns) {
|
|
3887
|
+
return fns.reduce((acc, fn) => fn(acc), value);
|
|
3888
|
+
}
|
|
3889
|
+
function compose(...fns) {
|
|
3890
|
+
return (value) => fns.reduceRight((acc, fn) => fn(acc), value);
|
|
3891
|
+
}
|
|
3892
|
+
function identity(value) {
|
|
3893
|
+
return value;
|
|
3894
|
+
}
|
|
3895
|
+
function noop() {
|
|
3896
|
+
}
|
|
3897
|
+
function constant(value) {
|
|
3898
|
+
return () => value;
|
|
3899
|
+
}
|
|
3900
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3901
|
+
0 && (module.exports = {
|
|
3902
|
+
AABB,
|
|
3903
|
+
CountdownTimer,
|
|
3904
|
+
DEFAULT_BONDING_CURVE,
|
|
3905
|
+
DEFAULT_DEPRECIATION,
|
|
3906
|
+
DEFAULT_PID,
|
|
3907
|
+
DEFAULT_WEALTH_TAX,
|
|
3908
|
+
DEG_TO_RAD,
|
|
3909
|
+
DateTime,
|
|
3910
|
+
EPSILON,
|
|
3911
|
+
EconomicTraits,
|
|
3912
|
+
EventBus,
|
|
3913
|
+
FrameTimer,
|
|
3914
|
+
HALF_PI,
|
|
3915
|
+
HoloMap,
|
|
3916
|
+
HoloSet,
|
|
3917
|
+
IntervalTimer,
|
|
3918
|
+
List,
|
|
3919
|
+
MATERIAL_PRESETS,
|
|
3920
|
+
Map,
|
|
3921
|
+
PI,
|
|
3922
|
+
PriorityQueue,
|
|
3923
|
+
Quaternion,
|
|
3924
|
+
RAD_TO_DEG,
|
|
3925
|
+
Ray,
|
|
3926
|
+
Set,
|
|
3927
|
+
SimulationLabTraits,
|
|
3928
|
+
SpatialGrid,
|
|
3929
|
+
Stopwatch,
|
|
3930
|
+
TAU,
|
|
3931
|
+
Transform,
|
|
3932
|
+
Vec3,
|
|
3933
|
+
aabb,
|
|
3934
|
+
aabbMath,
|
|
3935
|
+
arrayToQuat,
|
|
3936
|
+
arrayToVec3,
|
|
3937
|
+
assert,
|
|
3938
|
+
assertDefined,
|
|
3939
|
+
bondingCurveBuyCost,
|
|
3940
|
+
bondingCurvePrice,
|
|
3941
|
+
bondingCurveSellRefund,
|
|
3942
|
+
calculateDepreciation,
|
|
3943
|
+
calculateRedistribution,
|
|
3944
|
+
calculateRepairCost,
|
|
3945
|
+
calculateTaxAmount,
|
|
3946
|
+
calculateTaxRate,
|
|
3947
|
+
camelCase,
|
|
3948
|
+
capitalize,
|
|
3949
|
+
center,
|
|
3950
|
+
chars,
|
|
3951
|
+
chiSquaredTest,
|
|
3952
|
+
clamp,
|
|
3953
|
+
clone,
|
|
3954
|
+
cohensD,
|
|
3955
|
+
collapseWhitespace,
|
|
3956
|
+
colorToHex,
|
|
3957
|
+
compose,
|
|
3958
|
+
constant,
|
|
3959
|
+
constantCase,
|
|
3960
|
+
containsIgnoreCase,
|
|
3961
|
+
count,
|
|
3962
|
+
createBoxCollider,
|
|
3963
|
+
createCapsuleCollider,
|
|
3964
|
+
createDualLoopPIDState,
|
|
3965
|
+
createFaucetSinkMetrics,
|
|
3966
|
+
createPBRMaterial,
|
|
3967
|
+
createPIDState,
|
|
3968
|
+
createRigidbody,
|
|
3969
|
+
createSphereCollider,
|
|
3970
|
+
createTicker,
|
|
3971
|
+
debounce,
|
|
3972
|
+
dedent,
|
|
3973
|
+
degToRad,
|
|
3974
|
+
depreciatedValue,
|
|
3975
|
+
distance,
|
|
3976
|
+
endsWithIgnoreCase,
|
|
3977
|
+
equals,
|
|
3978
|
+
escapeHtml,
|
|
3979
|
+
escapeRegex,
|
|
3980
|
+
executeTrade,
|
|
3981
|
+
expandSweep,
|
|
3982
|
+
extractMetric,
|
|
3983
|
+
extractTraits,
|
|
3984
|
+
format,
|
|
3985
|
+
formatBytes,
|
|
3986
|
+
formatDuration,
|
|
3987
|
+
formatNumber,
|
|
3988
|
+
fract,
|
|
3989
|
+
generateSweepCombinations,
|
|
3990
|
+
generateTxHash,
|
|
3991
|
+
getEconomicTrait,
|
|
3992
|
+
getEconomicTraitNames,
|
|
3993
|
+
getRequiredPermissions,
|
|
3994
|
+
getSimulationTrait,
|
|
3995
|
+
getSimulationTraitNames,
|
|
3996
|
+
groupByParameter,
|
|
3997
|
+
hrTime,
|
|
3998
|
+
hsl,
|
|
3999
|
+
identity,
|
|
4000
|
+
indent,
|
|
4001
|
+
inverseLerp,
|
|
4002
|
+
isAlpha,
|
|
4003
|
+
isAlphanumeric,
|
|
4004
|
+
isBlank,
|
|
4005
|
+
isDestroyed,
|
|
4006
|
+
isNotBlank,
|
|
4007
|
+
isNumeric,
|
|
4008
|
+
isParameterRange,
|
|
4009
|
+
isValidIdentifier,
|
|
4010
|
+
join,
|
|
4011
|
+
kebabCase,
|
|
4012
|
+
lerp,
|
|
4013
|
+
levenshtein,
|
|
4014
|
+
lines,
|
|
4015
|
+
mannWhitneyU,
|
|
4016
|
+
mean,
|
|
4017
|
+
measure,
|
|
4018
|
+
mod,
|
|
4019
|
+
noise,
|
|
4020
|
+
noop,
|
|
4021
|
+
now,
|
|
4022
|
+
numberWithCommas,
|
|
4023
|
+
oneSampleTTest,
|
|
4024
|
+
padLeft,
|
|
4025
|
+
padRight,
|
|
4026
|
+
parseColor,
|
|
4027
|
+
pascalCase,
|
|
4028
|
+
pipe,
|
|
4029
|
+
print,
|
|
4030
|
+
printError,
|
|
4031
|
+
printWarn,
|
|
4032
|
+
quat,
|
|
4033
|
+
quatMath,
|
|
4034
|
+
quatToArray,
|
|
4035
|
+
radToDeg,
|
|
4036
|
+
random,
|
|
4037
|
+
randomString,
|
|
4038
|
+
ray,
|
|
4039
|
+
recordFaucet,
|
|
4040
|
+
recordSink,
|
|
4041
|
+
remap,
|
|
4042
|
+
removePrefix,
|
|
4043
|
+
removeSuffix,
|
|
4044
|
+
removeWhitespace,
|
|
4045
|
+
repeat,
|
|
4046
|
+
resetMetricsPeriod,
|
|
4047
|
+
retry,
|
|
4048
|
+
reverse,
|
|
4049
|
+
rgb,
|
|
4050
|
+
rgba,
|
|
4051
|
+
schedule,
|
|
4052
|
+
scheduleFrame,
|
|
4053
|
+
scheduleInterval,
|
|
4054
|
+
sign,
|
|
4055
|
+
similarity,
|
|
4056
|
+
sleep,
|
|
4057
|
+
slugify,
|
|
4058
|
+
smootherstep,
|
|
4059
|
+
smoothstep,
|
|
4060
|
+
snakeCase,
|
|
4061
|
+
spatialClamp,
|
|
4062
|
+
spatialDegToRad,
|
|
4063
|
+
spatialLerp,
|
|
4064
|
+
spatialPrice,
|
|
4065
|
+
spatialRadToDeg,
|
|
4066
|
+
standardDeviation,
|
|
4067
|
+
standardError,
|
|
4068
|
+
startsWithIgnoreCase,
|
|
4069
|
+
step,
|
|
4070
|
+
summarizeMetrics,
|
|
4071
|
+
tTest,
|
|
4072
|
+
throttle,
|
|
4073
|
+
timed,
|
|
4074
|
+
titleCase,
|
|
4075
|
+
toDegrees,
|
|
4076
|
+
toRadians,
|
|
4077
|
+
todo,
|
|
4078
|
+
transform,
|
|
4079
|
+
truncate,
|
|
4080
|
+
truncateMiddle,
|
|
4081
|
+
unescapeHtml,
|
|
4082
|
+
unreachable,
|
|
4083
|
+
unwrap,
|
|
4084
|
+
updateDualLoopPID,
|
|
4085
|
+
updatePID,
|
|
4086
|
+
uuid,
|
|
4087
|
+
validateTraitComposition,
|
|
4088
|
+
variance,
|
|
4089
|
+
vec2,
|
|
4090
|
+
vec2Math,
|
|
4091
|
+
vec3,
|
|
4092
|
+
vec3Math,
|
|
4093
|
+
vec3ToArray,
|
|
4094
|
+
vec4,
|
|
4095
|
+
waitUntil,
|
|
4096
|
+
withTimeout,
|
|
4097
|
+
wordWrap,
|
|
4098
|
+
words,
|
|
4099
|
+
wrapString
|
|
4100
|
+
});
|
|
4101
|
+
//# sourceMappingURL=index.cjs.map
|