@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/src/materials.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/std — Materials Module
|
|
3
|
+
*
|
|
4
|
+
* Provides PBR material types, texture map definitions, and material presets.
|
|
5
|
+
* Maps directly to HoloScript material_block grammar constructs.
|
|
6
|
+
*
|
|
7
|
+
* @version 0.2.0
|
|
8
|
+
* @module @holoscript/std/materials
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// PBR Material
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export interface PBRMaterial {
|
|
16
|
+
name: string;
|
|
17
|
+
baseColor: string; // hex color
|
|
18
|
+
roughness: number; // 0.0 - 1.0
|
|
19
|
+
metallic: number; // 0.0 - 1.0
|
|
20
|
+
emissiveColor?: string;
|
|
21
|
+
emissiveIntensity?: number;
|
|
22
|
+
opacity?: number; // 0.0 - 1.0
|
|
23
|
+
ior?: number; // Index of refraction (glass ~1.5, water ~1.33)
|
|
24
|
+
subsurface?: number;
|
|
25
|
+
clearcoat?: number;
|
|
26
|
+
clearcoatRoughness?: number;
|
|
27
|
+
// Texture maps
|
|
28
|
+
albedoMap?: string;
|
|
29
|
+
normalMap?: string;
|
|
30
|
+
roughnessMap?: string;
|
|
31
|
+
metallicMap?: string;
|
|
32
|
+
emissionMap?: string;
|
|
33
|
+
aoMap?: string;
|
|
34
|
+
heightMap?: string;
|
|
35
|
+
opacityMap?: string;
|
|
36
|
+
displacementMap?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface UnlitMaterial {
|
|
40
|
+
name: string;
|
|
41
|
+
color: string;
|
|
42
|
+
emissiveColor?: string;
|
|
43
|
+
emissiveIntensity?: number;
|
|
44
|
+
opacity?: number;
|
|
45
|
+
texture?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// =============================================================================
|
|
49
|
+
// Material Presets
|
|
50
|
+
// =============================================================================
|
|
51
|
+
|
|
52
|
+
export const MATERIAL_PRESETS: Record<string, Partial<PBRMaterial>> = {
|
|
53
|
+
metal: { roughness: 0.2, metallic: 1.0 },
|
|
54
|
+
wood: { roughness: 0.85, metallic: 0.0, baseColor: '#8B6914' },
|
|
55
|
+
glass: { roughness: 0.05, metallic: 0.0, opacity: 0.3, ior: 1.5 },
|
|
56
|
+
plastic: { roughness: 0.4, metallic: 0.0 },
|
|
57
|
+
concrete: { roughness: 0.95, metallic: 0.0, baseColor: '#808080' },
|
|
58
|
+
fabric: { roughness: 0.9, metallic: 0.0, subsurface: 0.2 },
|
|
59
|
+
water: { roughness: 0.05, metallic: 0.0, opacity: 0.7, ior: 1.33 },
|
|
60
|
+
rubber: { roughness: 0.95, metallic: 0.0, baseColor: '#222222' },
|
|
61
|
+
marble: { roughness: 0.3, metallic: 0.0, baseColor: '#f0f0f0' },
|
|
62
|
+
skin: { roughness: 0.6, metallic: 0.0, subsurface: 0.5 },
|
|
63
|
+
foliage: { roughness: 0.8, metallic: 0.0, baseColor: '#2d5a27', subsurface: 0.3 },
|
|
64
|
+
chrome: { roughness: 0.05, metallic: 1.0, baseColor: '#cccccc' },
|
|
65
|
+
gold: { roughness: 0.2, metallic: 1.0, baseColor: '#ffd700' },
|
|
66
|
+
copper: { roughness: 0.3, metallic: 1.0, baseColor: '#b87333' },
|
|
67
|
+
ice: { roughness: 0.1, metallic: 0.0, opacity: 0.8, ior: 1.31 },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function createPBRMaterial(
|
|
71
|
+
name: string,
|
|
72
|
+
preset?: keyof typeof MATERIAL_PRESETS,
|
|
73
|
+
overrides?: Partial<PBRMaterial>
|
|
74
|
+
): PBRMaterial {
|
|
75
|
+
const base = preset ? MATERIAL_PRESETS[preset] : {};
|
|
76
|
+
return {
|
|
77
|
+
name,
|
|
78
|
+
baseColor: '#ffffff',
|
|
79
|
+
roughness: 0.5,
|
|
80
|
+
metallic: 0.0,
|
|
81
|
+
...base,
|
|
82
|
+
...overrides,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// Texture Map Types
|
|
88
|
+
// =============================================================================
|
|
89
|
+
|
|
90
|
+
export type TextureMapType =
|
|
91
|
+
| 'albedo_map'
|
|
92
|
+
| 'normal_map'
|
|
93
|
+
| 'roughness_map'
|
|
94
|
+
| 'metallic_map'
|
|
95
|
+
| 'emission_map'
|
|
96
|
+
| 'ao_map'
|
|
97
|
+
| 'height_map'
|
|
98
|
+
| 'opacity_map'
|
|
99
|
+
| 'displacement_map'
|
|
100
|
+
| 'specular_map'
|
|
101
|
+
| 'clearcoat_map';
|
|
102
|
+
|
|
103
|
+
export interface TextureConfig {
|
|
104
|
+
source: string;
|
|
105
|
+
tiling?: [number, number];
|
|
106
|
+
offset?: [number, number];
|
|
107
|
+
filter?: 'linear' | 'nearest';
|
|
108
|
+
wrap?: 'repeat' | 'clamp' | 'mirror';
|
|
109
|
+
}
|
package/src/math.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Vector math, quaternion operations, interpolation, and noise functions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { Vec2, Vec3,
|
|
7
|
+
import type { Vec2, Vec3, Quat, EulerAngles, AABB } from './types.js';
|
|
8
8
|
import { vec3, quat } from './types.js';
|
|
9
9
|
|
|
10
10
|
// =============================================================================
|
|
@@ -34,7 +34,13 @@ export function inverseLerp(a: number, b: number, value: number): number {
|
|
|
34
34
|
return (value - a) / (b - a);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function remap(
|
|
37
|
+
export function remap(
|
|
38
|
+
value: number,
|
|
39
|
+
inMin: number,
|
|
40
|
+
inMax: number,
|
|
41
|
+
outMin: number,
|
|
42
|
+
outMax: number
|
|
43
|
+
): number {
|
|
38
44
|
return lerp(outMin, outMax, inverseLerp(inMin, inMax, value));
|
|
39
45
|
}
|
|
40
46
|
|
|
@@ -153,10 +159,7 @@ export const vec3Math = {
|
|
|
153
159
|
const dot = clamp(vec3Math.dot(a, b), -1, 1);
|
|
154
160
|
const theta = Math.acos(dot) * t;
|
|
155
161
|
const relative = vec3Math.normalize(vec3Math.sub(b, vec3Math.mul(a, dot)));
|
|
156
|
-
return vec3Math.add(
|
|
157
|
-
vec3Math.mul(a, Math.cos(theta)),
|
|
158
|
-
vec3Math.mul(relative, Math.sin(theta))
|
|
159
|
-
);
|
|
162
|
+
return vec3Math.add(vec3Math.mul(a, Math.cos(theta)), vec3Math.mul(relative, Math.sin(theta)));
|
|
160
163
|
},
|
|
161
164
|
|
|
162
165
|
negate: (v: Vec3): Vec3 => ({ x: -v.x, y: -v.y, z: -v.z }),
|
|
@@ -406,17 +409,23 @@ export const quatMath = {
|
|
|
406
409
|
export const aabbMath = {
|
|
407
410
|
contains: (aabb: AABB, point: Vec3): boolean => {
|
|
408
411
|
return (
|
|
409
|
-
point.x >= aabb.min.x &&
|
|
410
|
-
point.
|
|
411
|
-
point.
|
|
412
|
+
point.x >= aabb.min.x &&
|
|
413
|
+
point.x <= aabb.max.x &&
|
|
414
|
+
point.y >= aabb.min.y &&
|
|
415
|
+
point.y <= aabb.max.y &&
|
|
416
|
+
point.z >= aabb.min.z &&
|
|
417
|
+
point.z <= aabb.max.z
|
|
412
418
|
);
|
|
413
419
|
},
|
|
414
420
|
|
|
415
421
|
intersects: (a: AABB, b: AABB): boolean => {
|
|
416
422
|
return (
|
|
417
|
-
a.min.x <= b.max.x &&
|
|
418
|
-
a.
|
|
419
|
-
a.min.
|
|
423
|
+
a.min.x <= b.max.x &&
|
|
424
|
+
a.max.x >= b.min.x &&
|
|
425
|
+
a.min.y <= b.max.y &&
|
|
426
|
+
a.max.y >= b.min.y &&
|
|
427
|
+
a.min.z <= b.max.z &&
|
|
428
|
+
a.max.z >= b.min.z
|
|
420
429
|
);
|
|
421
430
|
},
|
|
422
431
|
|
|
@@ -508,7 +517,14 @@ export const noise = {
|
|
|
508
517
|
/**
|
|
509
518
|
* Fractal Brownian Motion (fBm)
|
|
510
519
|
*/
|
|
511
|
-
fbm: (
|
|
520
|
+
fbm: (
|
|
521
|
+
x: number,
|
|
522
|
+
y: number,
|
|
523
|
+
z: number,
|
|
524
|
+
octaves: number = 6,
|
|
525
|
+
lacunarity: number = 2,
|
|
526
|
+
gain: number = 0.5
|
|
527
|
+
): number => {
|
|
512
528
|
let value = 0;
|
|
513
529
|
let amplitude = 1;
|
|
514
530
|
let frequency = 1;
|
|
@@ -550,7 +566,7 @@ export const noise = {
|
|
|
550
566
|
|
|
551
567
|
// Pseudo-random point in cell
|
|
552
568
|
const hash = perm[(perm[(perm[cellX & 255] + cellY) & 255] + cellZ) & 255];
|
|
553
|
-
const px = cellX +
|
|
569
|
+
const px = cellX + hash / 255;
|
|
554
570
|
const py = cellY + ((hash * 7) % 255) / 255;
|
|
555
571
|
const pz = cellZ + ((hash * 13) % 255) / 255;
|
|
556
572
|
|
package/src/physics.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/std — Physics Module
|
|
3
|
+
*
|
|
4
|
+
* Provides physics primitives: ColliderShape, RigidbodyConfig, ForceField, Joint.
|
|
5
|
+
* Runtime representations for HoloScript physics blocks.
|
|
6
|
+
*
|
|
7
|
+
* @version 0.2.0
|
|
8
|
+
* @module @holoscript/std/physics
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Vec3 } from './spatial.js';
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Collider Shapes
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
export type ColliderShapeType =
|
|
18
|
+
| 'box'
|
|
19
|
+
| 'sphere'
|
|
20
|
+
| 'capsule'
|
|
21
|
+
| 'mesh'
|
|
22
|
+
| 'convex'
|
|
23
|
+
| 'cylinder'
|
|
24
|
+
| 'heightfield';
|
|
25
|
+
|
|
26
|
+
export interface ColliderConfig {
|
|
27
|
+
shape: ColliderShapeType;
|
|
28
|
+
isTrigger: boolean;
|
|
29
|
+
friction: number;
|
|
30
|
+
restitution: number;
|
|
31
|
+
// Shape-specific
|
|
32
|
+
radius?: number;
|
|
33
|
+
halfExtents?: Vec3;
|
|
34
|
+
height?: number;
|
|
35
|
+
meshAsset?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createBoxCollider(
|
|
39
|
+
halfExtents: Vec3,
|
|
40
|
+
options?: Partial<ColliderConfig>
|
|
41
|
+
): ColliderConfig {
|
|
42
|
+
return {
|
|
43
|
+
shape: 'box',
|
|
44
|
+
isTrigger: false,
|
|
45
|
+
friction: 0.5,
|
|
46
|
+
restitution: 0.0,
|
|
47
|
+
halfExtents,
|
|
48
|
+
...options,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createSphereCollider(
|
|
53
|
+
radius: number,
|
|
54
|
+
options?: Partial<ColliderConfig>
|
|
55
|
+
): ColliderConfig {
|
|
56
|
+
return { shape: 'sphere', isTrigger: false, friction: 0.5, restitution: 0.0, radius, ...options };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createCapsuleCollider(
|
|
60
|
+
radius: number,
|
|
61
|
+
height: number,
|
|
62
|
+
options?: Partial<ColliderConfig>
|
|
63
|
+
): ColliderConfig {
|
|
64
|
+
return {
|
|
65
|
+
shape: 'capsule',
|
|
66
|
+
isTrigger: false,
|
|
67
|
+
friction: 0.5,
|
|
68
|
+
restitution: 0.0,
|
|
69
|
+
radius,
|
|
70
|
+
height,
|
|
71
|
+
...options,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Rigidbody
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
export interface RigidbodyConfig {
|
|
80
|
+
mass: number;
|
|
81
|
+
useGravity: boolean;
|
|
82
|
+
linearDamping: number;
|
|
83
|
+
angularDamping: number;
|
|
84
|
+
freezePosition: [boolean, boolean, boolean];
|
|
85
|
+
freezeRotation: [boolean, boolean, boolean];
|
|
86
|
+
isKinematic: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function createRigidbody(mass: number, options?: Partial<RigidbodyConfig>): RigidbodyConfig {
|
|
90
|
+
return {
|
|
91
|
+
mass,
|
|
92
|
+
useGravity: true,
|
|
93
|
+
linearDamping: 0.0,
|
|
94
|
+
angularDamping: 0.05,
|
|
95
|
+
freezePosition: [false, false, false],
|
|
96
|
+
freezeRotation: [false, false, false],
|
|
97
|
+
isKinematic: false,
|
|
98
|
+
...options,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// =============================================================================
|
|
103
|
+
// Force Fields
|
|
104
|
+
// =============================================================================
|
|
105
|
+
|
|
106
|
+
export type ForceFieldType = 'directional' | 'radial' | 'vortex' | 'buoyancy';
|
|
107
|
+
|
|
108
|
+
export interface ForceFieldConfig {
|
|
109
|
+
type: ForceFieldType;
|
|
110
|
+
strength: number;
|
|
111
|
+
direction?: Vec3;
|
|
112
|
+
falloff: 'none' | 'linear' | 'quadratic';
|
|
113
|
+
turbulence: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =============================================================================
|
|
117
|
+
// Joints
|
|
118
|
+
// =============================================================================
|
|
119
|
+
|
|
120
|
+
export type JointType = 'hinge' | 'slider' | 'ball_socket' | 'fixed' | 'd6' | 'spring';
|
|
121
|
+
|
|
122
|
+
export interface JointConfig {
|
|
123
|
+
type: JointType;
|
|
124
|
+
connectedBody?: string;
|
|
125
|
+
axis?: Vec3;
|
|
126
|
+
limits?: [number, number];
|
|
127
|
+
damping: number;
|
|
128
|
+
springForce?: number;
|
|
129
|
+
breakForce?: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Raycast Utility
|
|
134
|
+
// =============================================================================
|
|
135
|
+
|
|
136
|
+
export interface RaycastHit {
|
|
137
|
+
point: Vec3;
|
|
138
|
+
normal: Vec3;
|
|
139
|
+
distance: number;
|
|
140
|
+
objectName: string;
|
|
141
|
+
}
|
package/src/spatial.ts
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/std — Spatial Module
|
|
3
|
+
*
|
|
4
|
+
* Provides spatial math utilities: Vec3, Quaternion, Transform, Ray, AABB, distance.
|
|
5
|
+
* Used by the HoloScript runtime for spatial computing operations.
|
|
6
|
+
*
|
|
7
|
+
* @version 0.2.0
|
|
8
|
+
* @module @holoscript/std/spatial
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Vec3
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export class Vec3 {
|
|
16
|
+
constructor(
|
|
17
|
+
public x: number = 0,
|
|
18
|
+
public y: number = 0,
|
|
19
|
+
public z: number = 0
|
|
20
|
+
) {}
|
|
21
|
+
|
|
22
|
+
static zero(): Vec3 {
|
|
23
|
+
return new Vec3(0, 0, 0);
|
|
24
|
+
}
|
|
25
|
+
static one(): Vec3 {
|
|
26
|
+
return new Vec3(1, 1, 1);
|
|
27
|
+
}
|
|
28
|
+
static up(): Vec3 {
|
|
29
|
+
return new Vec3(0, 1, 0);
|
|
30
|
+
}
|
|
31
|
+
static down(): Vec3 {
|
|
32
|
+
return new Vec3(0, -1, 0);
|
|
33
|
+
}
|
|
34
|
+
static forward(): Vec3 {
|
|
35
|
+
return new Vec3(0, 0, -1);
|
|
36
|
+
}
|
|
37
|
+
static right(): Vec3 {
|
|
38
|
+
return new Vec3(1, 0, 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
add(other: Vec3): Vec3 {
|
|
42
|
+
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
|
|
43
|
+
}
|
|
44
|
+
sub(other: Vec3): Vec3 {
|
|
45
|
+
return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z);
|
|
46
|
+
}
|
|
47
|
+
mul(s: number): Vec3 {
|
|
48
|
+
return new Vec3(this.x * s, this.y * s, this.z * s);
|
|
49
|
+
}
|
|
50
|
+
div(s: number): Vec3 {
|
|
51
|
+
return new Vec3(this.x / s, this.y / s, this.z / s);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
dot(other: Vec3): number {
|
|
55
|
+
return this.x * other.x + this.y * other.y + this.z * other.z;
|
|
56
|
+
}
|
|
57
|
+
cross(other: Vec3): Vec3 {
|
|
58
|
+
return new Vec3(
|
|
59
|
+
this.y * other.z - this.z * other.y,
|
|
60
|
+
this.z * other.x - this.x * other.z,
|
|
61
|
+
this.x * other.y - this.y * other.x
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
length(): number {
|
|
66
|
+
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
|
67
|
+
}
|
|
68
|
+
lengthSquared(): number {
|
|
69
|
+
return this.x * this.x + this.y * this.y + this.z * this.z;
|
|
70
|
+
}
|
|
71
|
+
normalize(): Vec3 {
|
|
72
|
+
const l = this.length();
|
|
73
|
+
return l > 0 ? this.div(l) : Vec3.zero();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
distanceTo(other: Vec3): number {
|
|
77
|
+
return this.sub(other).length();
|
|
78
|
+
}
|
|
79
|
+
lerp(other: Vec3, t: number): Vec3 {
|
|
80
|
+
return this.add(other.sub(this).mul(t));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toArray(): [number, number, number] {
|
|
84
|
+
return [this.x, this.y, this.z];
|
|
85
|
+
}
|
|
86
|
+
static fromArray(arr: number[]): Vec3 {
|
|
87
|
+
return new Vec3(arr[0] || 0, arr[1] || 0, arr[2] || 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
equals(other: Vec3, epsilon = 1e-6): boolean {
|
|
91
|
+
return (
|
|
92
|
+
Math.abs(this.x - other.x) < epsilon &&
|
|
93
|
+
Math.abs(this.y - other.y) < epsilon &&
|
|
94
|
+
Math.abs(this.z - other.z) < epsilon
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
toString(): string {
|
|
99
|
+
return `Vec3(${this.x}, ${this.y}, ${this.z})`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Quaternion
|
|
105
|
+
// =============================================================================
|
|
106
|
+
|
|
107
|
+
export class Quaternion {
|
|
108
|
+
constructor(
|
|
109
|
+
public x = 0,
|
|
110
|
+
public y = 0,
|
|
111
|
+
public z = 0,
|
|
112
|
+
public w = 1
|
|
113
|
+
) {}
|
|
114
|
+
|
|
115
|
+
static identity(): Quaternion {
|
|
116
|
+
return new Quaternion(0, 0, 0, 1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static fromEuler(x: number, y: number, z: number): Quaternion {
|
|
120
|
+
const cx = Math.cos(x * 0.5),
|
|
121
|
+
sx = Math.sin(x * 0.5);
|
|
122
|
+
const cy = Math.cos(y * 0.5),
|
|
123
|
+
sy = Math.sin(y * 0.5);
|
|
124
|
+
const cz = Math.cos(z * 0.5),
|
|
125
|
+
sz = Math.sin(z * 0.5);
|
|
126
|
+
return new Quaternion(
|
|
127
|
+
sx * cy * cz - cx * sy * sz,
|
|
128
|
+
cx * sy * cz + sx * cy * sz,
|
|
129
|
+
cx * cy * sz - sx * sy * cz,
|
|
130
|
+
cx * cy * cz + sx * sy * sz
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static fromAxisAngle(axis: Vec3, angle: number): Quaternion {
|
|
135
|
+
const half = angle * 0.5;
|
|
136
|
+
const s = Math.sin(half);
|
|
137
|
+
const n = axis.normalize();
|
|
138
|
+
return new Quaternion(n.x * s, n.y * s, n.z * s, Math.cos(half));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
multiply(other: Quaternion): Quaternion {
|
|
142
|
+
return new Quaternion(
|
|
143
|
+
this.w * other.x + this.x * other.w + this.y * other.z - this.z * other.y,
|
|
144
|
+
this.w * other.y - this.x * other.z + this.y * other.w + this.z * other.x,
|
|
145
|
+
this.w * other.z + this.x * other.y - this.y * other.x + this.z * other.w,
|
|
146
|
+
this.w * other.w - this.x * other.x - this.y * other.y - this.z * other.z
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
rotateVec3(v: Vec3): Vec3 {
|
|
151
|
+
const u = new Vec3(this.x, this.y, this.z);
|
|
152
|
+
const s = this.w;
|
|
153
|
+
return u
|
|
154
|
+
.mul(2 * u.dot(v))
|
|
155
|
+
.add(v.mul(s * s - u.dot(u)))
|
|
156
|
+
.add(u.cross(v).mul(2 * s));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
slerp(other: Quaternion, t: number): Quaternion {
|
|
160
|
+
let dot = this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w;
|
|
161
|
+
const neg = dot < 0;
|
|
162
|
+
if (neg) dot = -dot;
|
|
163
|
+
if (dot > 0.9995) {
|
|
164
|
+
// Linear interpolation for very close quaternions
|
|
165
|
+
const r = new Quaternion(
|
|
166
|
+
this.x + t * ((neg ? -other.x : other.x) - this.x),
|
|
167
|
+
this.y + t * ((neg ? -other.y : other.y) - this.y),
|
|
168
|
+
this.z + t * ((neg ? -other.z : other.z) - this.z),
|
|
169
|
+
this.w + t * ((neg ? -other.w : other.w) - this.w)
|
|
170
|
+
);
|
|
171
|
+
const len = Math.sqrt(r.x * r.x + r.y * r.y + r.z * r.z + r.w * r.w);
|
|
172
|
+
return new Quaternion(r.x / len, r.y / len, r.z / len, r.w / len);
|
|
173
|
+
}
|
|
174
|
+
const theta = Math.acos(dot);
|
|
175
|
+
const sinTheta = Math.sin(theta);
|
|
176
|
+
const a = Math.sin((1 - t) * theta) / sinTheta;
|
|
177
|
+
const b = (Math.sin(t * theta) / sinTheta) * (neg ? -1 : 1);
|
|
178
|
+
return new Quaternion(
|
|
179
|
+
a * this.x + b * other.x,
|
|
180
|
+
a * this.y + b * other.y,
|
|
181
|
+
a * this.z + b * other.z,
|
|
182
|
+
a * this.w + b * other.w
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
normalize(): Quaternion {
|
|
187
|
+
const len = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
|
|
188
|
+
return new Quaternion(this.x / len, this.y / len, this.z / len, this.w / len);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
toEuler(): Vec3 {
|
|
192
|
+
const sinr = 2 * (this.w * this.x + this.y * this.z);
|
|
193
|
+
const cosr = 1 - 2 * (this.x * this.x + this.y * this.y);
|
|
194
|
+
const sinp = 2 * (this.w * this.y - this.z * this.x);
|
|
195
|
+
const siny = 2 * (this.w * this.z + this.x * this.y);
|
|
196
|
+
const cosy = 1 - 2 * (this.y * this.y + this.z * this.z);
|
|
197
|
+
return new Vec3(
|
|
198
|
+
Math.atan2(sinr, cosr),
|
|
199
|
+
Math.abs(sinp) >= 1 ? (Math.sign(sinp) * Math.PI) / 2 : Math.asin(sinp),
|
|
200
|
+
Math.atan2(siny, cosy)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// =============================================================================
|
|
206
|
+
// Transform
|
|
207
|
+
// =============================================================================
|
|
208
|
+
|
|
209
|
+
export class Transform {
|
|
210
|
+
constructor(
|
|
211
|
+
public position: Vec3 = Vec3.zero(),
|
|
212
|
+
public rotation: Quaternion = Quaternion.identity(),
|
|
213
|
+
public scale: Vec3 = Vec3.one()
|
|
214
|
+
) {}
|
|
215
|
+
|
|
216
|
+
static identity(): Transform {
|
|
217
|
+
return new Transform();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
transformPoint(point: Vec3): Vec3 {
|
|
221
|
+
const scaled = new Vec3(point.x * this.scale.x, point.y * this.scale.y, point.z * this.scale.z);
|
|
222
|
+
return this.rotation.rotateVec3(scaled).add(this.position);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
transformDirection(dir: Vec3): Vec3 {
|
|
226
|
+
return this.rotation.rotateVec3(dir);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// =============================================================================
|
|
231
|
+
// Ray & AABB
|
|
232
|
+
// =============================================================================
|
|
233
|
+
|
|
234
|
+
export class Ray {
|
|
235
|
+
constructor(
|
|
236
|
+
public origin: Vec3,
|
|
237
|
+
public direction: Vec3
|
|
238
|
+
) {}
|
|
239
|
+
|
|
240
|
+
pointAt(t: number): Vec3 {
|
|
241
|
+
return this.origin.add(this.direction.mul(t));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export class AABB {
|
|
246
|
+
constructor(
|
|
247
|
+
public min: Vec3,
|
|
248
|
+
public max: Vec3
|
|
249
|
+
) {}
|
|
250
|
+
|
|
251
|
+
contains(point: Vec3): boolean {
|
|
252
|
+
return (
|
|
253
|
+
point.x >= this.min.x &&
|
|
254
|
+
point.x <= this.max.x &&
|
|
255
|
+
point.y >= this.min.y &&
|
|
256
|
+
point.y <= this.max.y &&
|
|
257
|
+
point.z >= this.min.z &&
|
|
258
|
+
point.z <= this.max.z
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
intersects(other: AABB): boolean {
|
|
263
|
+
return (
|
|
264
|
+
this.min.x <= other.max.x &&
|
|
265
|
+
this.max.x >= other.min.x &&
|
|
266
|
+
this.min.y <= other.max.y &&
|
|
267
|
+
this.max.y >= other.min.y &&
|
|
268
|
+
this.min.z <= other.max.z &&
|
|
269
|
+
this.max.z >= other.min.z
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
center(): Vec3 {
|
|
274
|
+
return this.min.add(this.max).div(2);
|
|
275
|
+
}
|
|
276
|
+
size(): Vec3 {
|
|
277
|
+
return this.max.sub(this.min);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
intersectsRay(ray: Ray): number | null {
|
|
281
|
+
let tmin = (this.min.x - ray.origin.x) / ray.direction.x;
|
|
282
|
+
let tmax = (this.max.x - ray.origin.x) / ray.direction.x;
|
|
283
|
+
if (tmin > tmax) [tmin, tmax] = [tmax, tmin];
|
|
284
|
+
|
|
285
|
+
let tymin = (this.min.y - ray.origin.y) / ray.direction.y;
|
|
286
|
+
let tymax = (this.max.y - ray.origin.y) / ray.direction.y;
|
|
287
|
+
if (tymin > tymax) [tymin, tymax] = [tymax, tymin];
|
|
288
|
+
|
|
289
|
+
if (tmin > tymax || tymin > tmax) return null;
|
|
290
|
+
tmin = Math.max(tmin, tymin);
|
|
291
|
+
tmax = Math.min(tmax, tymax);
|
|
292
|
+
|
|
293
|
+
let tzmin = (this.min.z - ray.origin.z) / ray.direction.z;
|
|
294
|
+
let tzmax = (this.max.z - ray.origin.z) / ray.direction.z;
|
|
295
|
+
if (tzmin > tzmax) [tzmin, tzmax] = [tzmax, tzmin];
|
|
296
|
+
|
|
297
|
+
if (tmin > tzmax || tzmin > tmax) return null;
|
|
298
|
+
return Math.max(tmin, tzmin);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// =============================================================================
|
|
303
|
+
// Utility Functions
|
|
304
|
+
// =============================================================================
|
|
305
|
+
|
|
306
|
+
export function distance(a: Vec3, b: Vec3): number {
|
|
307
|
+
return a.distanceTo(b);
|
|
308
|
+
}
|
|
309
|
+
export function lerp(a: number, b: number, t: number): number {
|
|
310
|
+
return a + (b - a) * t;
|
|
311
|
+
}
|
|
312
|
+
export function clamp(v: number, min: number, max: number): number {
|
|
313
|
+
return Math.min(Math.max(v, min), max);
|
|
314
|
+
}
|
|
315
|
+
export function degToRad(deg: number): number {
|
|
316
|
+
return deg * (Math.PI / 180);
|
|
317
|
+
}
|
|
318
|
+
export function radToDeg(rad: number): number {
|
|
319
|
+
return rad * (180 / Math.PI);
|
|
320
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Basic test to verify formatBytes function
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { formatBytes } from './string.js';
|
|
7
|
+
|
|
8
|
+
describe('formatBytes basic test', () => {
|
|
9
|
+
it('should format bytes correctly', () => {
|
|
10
|
+
expect(formatBytes(0)).toBe('0 B');
|
|
11
|
+
expect(formatBytes(1024)).toBe('1 KB');
|
|
12
|
+
expect(formatBytes(1048576)).toBe('1 MB');
|
|
13
|
+
});
|
|
14
|
+
});
|