lecodes-cli 0.19.0 → 0.20.0
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/README.md +1 -0
- package/dist/index.js +1069 -396
- package/package.json +14 -13
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
- package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/runtime/web/assets/basis-C64VHDVD.js +1 -0
- package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
- package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
- package/runtime/web/assets/draco-BiISTFcR.js +118 -0
- package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
- package/runtime/web/assets/index-CfNCCBjN.js +2 -0
- package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
- package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
- package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
- package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
- package/runtime/web/embed.html +32 -0
- package/runtime/web/embed.js +171 -0
- package/src/api.ts +302 -0
- package/src/browserAuth.ts +87 -0
- package/src/cmgenTool.ts +104 -0
- package/src/commands/app.ts +896 -0
- package/src/commands/appAndroid.ts +603 -0
- package/src/commands/appDesktop.ts +339 -0
- package/src/commands/appDesktopMac.ts +470 -0
- package/src/commands/appIcon.ts +187 -0
- package/src/commands/appShared.ts +542 -0
- package/src/commands/appTemplates.ts +907 -0
- package/src/commands/appTemplatesAndroid.ts +599 -0
- package/src/commands/appTemplatesGradlew.ts +9 -0
- package/src/commands/assets.ts +28 -0
- package/src/commands/clone.ts +60 -0
- package/src/commands/compile.ts +253 -0
- package/src/commands/create.ts +59 -0
- package/src/commands/design.ts +863 -0
- package/src/commands/designTemplates.ts +11 -0
- package/src/commands/desktop.ts +127 -0
- package/src/commands/dev.ts +236 -0
- package/src/commands/diff.ts +59 -0
- package/src/commands/init.ts +191 -0
- package/src/commands/install.ts +198 -0
- package/src/commands/lightmap.ts +290 -0
- package/src/commands/link.ts +147 -0
- package/src/commands/login.ts +24 -0
- package/src/commands/navmesh.ts +225 -0
- package/src/commands/pn.ts +305 -0
- package/src/commands/projectTemplates.ts +130 -0
- package/src/commands/pull.ts +109 -0
- package/src/commands/push.ts +173 -0
- package/src/commands/render.ts +416 -0
- package/src/commands/scene.ts +148 -0
- package/src/commands/shaders.ts +157 -0
- package/src/commands/status.ts +29 -0
- package/src/commands/test.ts +371 -0
- package/src/commands/thumbs.ts +178 -0
- package/src/commands/types.ts +87 -0
- package/src/commands/update.ts +190 -0
- package/src/compile/assetIcons.ts +214 -0
- package/src/compile/collect.ts +46 -0
- package/src/compile/collectLocal.ts +37 -0
- package/src/compile/designCompile.ts +104 -0
- package/src/compile/fonts.ts +156 -0
- package/src/compile/headlessBundle.ts +129 -0
- package/src/compile/nativeStack.ts +21 -0
- package/src/compile/projectCompile.ts +109 -0
- package/src/compile/sceneCompile.ts +113 -0
- package/src/compile/screenEntry.ts +127 -0
- package/src/compile/shaders.ts +245 -0
- package/src/config.ts +42 -0
- package/src/designMeta.ts +35 -0
- package/src/desktopRenderer.ts +545 -0
- package/src/desktopScript.ts +276 -0
- package/src/dev/clientTemplates.ts +160 -0
- package/src/dev/devServer.ts +333 -0
- package/src/dev/webRunner.ts +22 -0
- package/src/dev/wsServer.ts +144 -0
- package/src/distRoot.ts +20 -0
- package/src/ignore.ts +163 -0
- package/src/index.ts +494 -0
- package/src/lecodes-3d-editor.d.ts +41 -0
- package/src/lecodes-assets.d.ts +7 -0
- package/src/lecodes-design.d.ts +191 -0
- package/src/lecodes-renderer.d.ts +131 -0
- package/src/localFiles.ts +144 -0
- package/src/manifest.ts +46 -0
- package/src/matcTool.ts +137 -0
- package/src/peers.ts +40 -0
- package/src/project.ts +61 -0
- package/src/projectEnv.ts +94 -0
- package/src/qrcode-terminal.d.ts +9 -0
- package/src/releases.ts +125 -0
- package/src/serverDiff.ts +78 -0
- package/src/textDiff.ts +103 -0
- package/src/types.ts +0 -0
- package/src/util.ts +146 -0
- package/runtime/sdk/animate/animate.ts +0 -238
- package/runtime/sdk/animate/bezier.ts +0 -138
- package/runtime/sdk/animate/easings.ts +0 -126
- package/runtime/sdk/canvas/Canvas.ts +0 -305
- package/runtime/sdk/compile/aspectMacro.ts +0 -86
- package/runtime/sdk/compile/assetIconMacro.ts +0 -384
- package/runtime/sdk/compile/assetMacro.ts +0 -146
- package/runtime/sdk/compile/assetName.ts +0 -50
- package/runtime/sdk/compile/bundler.ts +0 -287
- package/runtime/sdk/compile/compileProject.ts +0 -129
- package/runtime/sdk/compile/detectEntry.ts +0 -128
- package/runtime/sdk/compile/fontMacro.ts +0 -459
- package/runtime/sdk/compile/fontRegistry.ts +0 -78
- package/runtime/sdk/compile/header.ts +0 -67
- package/runtime/sdk/compile/index.ts +0 -100
- package/runtime/sdk/compile/libraryImports.ts +0 -52
- package/runtime/sdk/compile/liteMaterial.ts +0 -247
- package/runtime/sdk/compile/sceneEditor.ts +0 -88
- package/runtime/sdk/compile/serverSplit.ts +0 -233
- package/runtime/sdk/compile/serverTypes.ts +0 -227
- package/runtime/sdk/compile/sfnt.ts +0 -98
- package/runtime/sdk/compile/shaderSchema.ts +0 -202
- package/runtime/sdk/compile/shaderTargets.ts +0 -81
- package/runtime/sdk/compile/sourcemap.ts +0 -25
- package/runtime/sdk/core/Aspect.ts +0 -568
- package/runtime/sdk/core/InspectorUI.ts +0 -212
- package/runtime/sdk/core/StateMachine.ts +0 -308
- package/runtime/sdk/core/color.ts +0 -66
- package/runtime/sdk/core/compWrite.ts +0 -42
- package/runtime/sdk/core/events.ts +0 -38
- package/runtime/sdk/core/fields.ts +0 -120
- package/runtime/sdk/core/registry.ts +0 -23
- package/runtime/sdk/core/signals.ts +0 -277
- package/runtime/sdk/core/time.ts +0 -84
- package/runtime/sdk/g2/Camera2D.ts +0 -40
- package/runtime/sdk/g2/CharacterController2D.ts +0 -276
- package/runtime/sdk/g2/Node2D.ts +0 -267
- package/runtime/sdk/g2/OneWay2D.ts +0 -66
- package/runtime/sdk/g2/Physics2D.ts +0 -346
- package/runtime/sdk/g2/Scene2D.ts +0 -209
- package/runtime/sdk/g2/Shape2D.ts +0 -259
- package/runtime/sdk/g2/Sprite.ts +0 -89
- package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
- package/runtime/sdk/g2/SpriteSheet.ts +0 -166
- package/runtime/sdk/g2/Texture2D.ts +0 -47
- package/runtime/sdk/g2/Tilemap.ts +0 -41
- package/runtime/sdk/g2/Tileset.ts +0 -71
- package/runtime/sdk/g2/Trigger2D.ts +0 -77
- package/runtime/sdk/g2/autotile.ts +0 -433
- package/runtime/sdk/g2/cells.ts +0 -91
- package/runtime/sdk/g2/defineScene2d.ts +0 -381
- package/runtime/sdk/g2/groups2d.ts +0 -106
- package/runtime/sdk/g2/loop.ts +0 -50
- package/runtime/sdk/g2/scenarios2d.ts +0 -69
- package/runtime/sdk/g2/touch.ts +0 -83
- package/runtime/sdk/gl/Camera.ts +0 -160
- package/runtime/sdk/gl/CameraPlace.ts +0 -52
- package/runtime/sdk/gl/CharacterController.ts +0 -238
- package/runtime/sdk/gl/Gearbox.ts +0 -212
- package/runtime/sdk/gl/Geometry.ts +0 -279
- package/runtime/sdk/gl/IK.ts +0 -193
- package/runtime/sdk/gl/InstancedMesh.ts +0 -132
- package/runtime/sdk/gl/Light.ts +0 -107
- package/runtime/sdk/gl/Lightmap.ts +0 -256
- package/runtime/sdk/gl/Material.ts +0 -260
- package/runtime/sdk/gl/Mesh.ts +0 -83
- package/runtime/sdk/gl/Model.ts +0 -124
- package/runtime/sdk/gl/NavAgent.ts +0 -337
- package/runtime/sdk/gl/NavMesh.ts +0 -397
- package/runtime/sdk/gl/Node.ts +0 -350
- package/runtime/sdk/gl/Noise.ts +0 -30
- package/runtime/sdk/gl/Particles.ts +0 -676
- package/runtime/sdk/gl/Physics.ts +0 -222
- package/runtime/sdk/gl/Plane.ts +0 -53
- package/runtime/sdk/gl/Ragdoll.ts +0 -270
- package/runtime/sdk/gl/Ray.ts +0 -16
- package/runtime/sdk/gl/Scene.ts +0 -486
- package/runtime/sdk/gl/Shape.ts +0 -409
- package/runtime/sdk/gl/Terrain.ts +0 -1092
- package/runtime/sdk/gl/Texture.ts +0 -63
- package/runtime/sdk/gl/Trigger.ts +0 -45
- package/runtime/sdk/gl/Vehicle.ts +0 -473
- package/runtime/sdk/gl/Wheel.ts +0 -240
- package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
- package/runtime/sdk/gl/animation/Animator.ts +0 -120
- package/runtime/sdk/gl/animation/Layer.ts +0 -31
- package/runtime/sdk/gl/animation/Loop.ts +0 -30
- package/runtime/sdk/gl/animation/Playback.ts +0 -44
- package/runtime/sdk/gl/animation/core.ts +0 -336
- package/runtime/sdk/gl/controls.ts +0 -95
- package/runtime/sdk/gl/physicsEvents.ts +0 -20
- package/runtime/sdk/gl/scenarios.ts +0 -291
- package/runtime/sdk/gl/state.ts +0 -6
- package/runtime/sdk/gl/terrainMesh.ts +0 -219
- package/runtime/sdk/gl/touch.ts +0 -68
- package/runtime/sdk/inject.ts +0 -197
- package/runtime/sdk/math/Mathf.ts +0 -118
- package/runtime/sdk/math/mat4.ts +0 -278
- package/runtime/sdk/math/quat.ts +0 -232
- package/runtime/sdk/math/vec.ts +0 -255
- package/runtime/sdk/net/codec.ts +0 -119
- package/runtime/sdk/net/core.ts +0 -384
- package/runtime/sdk/net/index.ts +0 -181
- package/runtime/sdk/net/replication.ts +0 -622
- package/runtime/sdk/plugins/camera.ts +0 -81
- package/runtime/sdk/plugins/geolocation.ts +0 -123
- package/runtime/sdk/plugins/oauth.ts +0 -61
- package/runtime/sdk/plugins/permission.ts +0 -7
- package/runtime/sdk/plugins/push.ts +0 -132
- package/runtime/sdk/plugins/qr.ts +0 -73
- package/runtime/sdk/plugins/service.ts +0 -47
- package/runtime/sdk/runtime/app.ts +0 -101
- package/runtime/sdk/runtime/appEvents.ts +0 -54
- package/runtime/sdk/runtime/channel.ts +0 -61
- package/runtime/sdk/runtime/clipboard.ts +0 -20
- package/runtime/sdk/runtime/datetime.ts +0 -329
- package/runtime/sdk/runtime/device.ts +0 -293
- package/runtime/sdk/runtime/fetch.ts +0 -77
- package/runtime/sdk/runtime/files.ts +0 -23
- package/runtime/sdk/runtime/input.ts +0 -175
- package/runtime/sdk/runtime/media.ts +0 -111
- package/runtime/sdk/runtime/misc.ts +0 -16
- package/runtime/sdk/runtime/net.ts +0 -36
- package/runtime/sdk/runtime/rpc.ts +0 -218
- package/runtime/sdk/runtime/service.ts +0 -83
- package/runtime/sdk/runtime/share.ts +0 -9
- package/runtime/sdk/runtime/storage.ts +0 -13
- package/runtime/sdk/runtime/touch.ts +0 -76
- package/runtime/sdk/scene/defineScene.ts +0 -1353
- package/runtime/sdk/scene/editorPlugins.ts +0 -110
- package/runtime/sdk/scene/gizmos.ts +0 -148
- package/runtime/sdk/scene/grammar.ts +0 -120
- package/runtime/sdk/scene/material.ts +0 -188
- package/runtime/sdk/server/auth/appConfig.ts +0 -12
- package/runtime/sdk/server/auth/global.ts +0 -80
- package/runtime/sdk/server/auth/host.ts +0 -318
- package/runtime/sdk/server/auth/models.ts +0 -83
- package/runtime/sdk/server/auth/types.ts +0 -50
- package/runtime/sdk/server/channel.ts +0 -56
- package/runtime/sdk/server/context.ts +0 -36
- package/runtime/sdk/server/db/defineDb.ts +0 -237
- package/runtime/sdk/server/db/fields.ts +0 -132
- package/runtime/sdk/server/db/httpTransport.ts +0 -93
- package/runtime/sdk/server/db/index.ts +0 -7
- package/runtime/sdk/server/db/marci/query.ts +0 -412
- package/runtime/sdk/server/db/types.ts +0 -202
- package/runtime/sdk/server/errors.ts +0 -12
- package/runtime/sdk/server/host.ts +0 -74
- package/runtime/sdk/server/inject.ts +0 -13
- package/runtime/sdk/server/runtime.ts +0 -133
- package/runtime/sdk/server/validate.ts +0 -87
- package/runtime/sdk/ui/NativeView.ts +0 -142
- package/runtime/sdk/ui/UI.ts +0 -39
- package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
- package/runtime/sdk/ui/UIButton.ts +0 -101
- package/runtime/sdk/ui/UIContainer.ts +0 -60
- package/runtime/sdk/ui/UIImage.ts +0 -83
- package/runtime/sdk/ui/UIInput.ts +0 -185
- package/runtime/sdk/ui/UIModal.ts +0 -139
- package/runtime/sdk/ui/UINode.ts +0 -830
- package/runtime/sdk/ui/UIPager.ts +0 -362
- package/runtime/sdk/ui/UIPopover.ts +0 -100
- package/runtime/sdk/ui/UIScreen.ts +0 -123
- package/runtime/sdk/ui/UIScrollable.ts +0 -87
- package/runtime/sdk/ui/UISpacer.ts +0 -14
- package/runtime/sdk/ui/UITabs.ts +0 -236
- package/runtime/sdk/ui/UIText.ts +0 -51
- package/runtime/sdk/ui/UIVideo.ts +0 -88
- package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
- package/runtime/sdk/ui/UIWidget.ts +0 -127
- package/runtime/sdk/ui/fonts.ts +0 -13
- package/runtime/sdk/ui/presentable.ts +0 -117
- package/runtime/sdk/ui/router.ts +0 -132
- package/runtime/sdk/ui/theme.ts +0 -84
package/runtime/sdk/math/quat.ts
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
// Fluent quaternion (x, y, z, w) — the rotation type for the 3D node API. Same contract as Vec: mutable
|
|
2
|
-
// fields, PURE methods (each returns a new Quat). Accepts a `Quat` or a raw `[x, y, z, w]` everywhere.
|
|
3
|
-
//
|
|
4
|
-
// Convention: standard right-handed, column-major (matches gl-matrix and three.js). Euler angles are in
|
|
5
|
-
// DEGREES. The euler order default is "YXZ" (yaw-pitch-roll, the usual choice for cameras / characters).
|
|
6
|
-
//
|
|
7
|
-
// NB: this fixes a transpose bug in the old `transform.ts` euler extraction, which read the matrix with
|
|
8
|
-
// row/col swapped and so returned a NEGATED angle for a positive rotation. `eulerFromColMajor` below uses
|
|
9
|
-
// the correct column-major element mapping, so `node.eulerAngles` now round-trips with the setter.
|
|
10
|
-
|
|
11
|
-
import { Vec3, cx, cy, cz, type Vec3Like } from "./vec"
|
|
12
|
-
|
|
13
|
-
export type QuatLike = Quat | readonly [number, number, number, number] | number[] | Float32Array
|
|
14
|
-
export type EulerOrder = "XYZ" | "YXZ" | "ZXY" | "ZYX" | "YZX" | "XZY"
|
|
15
|
-
|
|
16
|
-
/** Read the w / 4th component of any quaternion-like (wrapper `.w`, or array-like `[3]`). */
|
|
17
|
-
export const cw = (q: { w?: number } | ArrayLike<number>): number => (q as { w?: number }).w ?? (q as ArrayLike<number>)[3]
|
|
18
|
-
const DEG2RAD = Math.PI / 180
|
|
19
|
-
const RAD2DEG = 180 / Math.PI
|
|
20
|
-
const EPS = 1e-6
|
|
21
|
-
const approx = (a: number, b: number, eps: number): boolean => Math.abs(a - b) <= eps * Math.max(1, Math.abs(a), Math.abs(b))
|
|
22
|
-
const clamp1 = (v: number): number => (v < -1 ? -1 : v > 1 ? 1 : v)
|
|
23
|
-
|
|
24
|
-
export class Quat {
|
|
25
|
-
x: number
|
|
26
|
-
y: number
|
|
27
|
-
z: number
|
|
28
|
-
w: number
|
|
29
|
-
|
|
30
|
-
constructor(x: number | QuatLike = 0, y = 0, z = 0, w = 1) {
|
|
31
|
-
if (typeof x === "number") { this.x = x; this.y = y; this.z = z; this.w = w }
|
|
32
|
-
else { this.x = cx(x); this.y = cy(x); this.z = cz(x); this.w = cw(x) }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// --- mutation ---
|
|
36
|
-
set(x: number, y: number, z: number, w: number): this { this.x = x; this.y = y; this.z = z; this.w = w; return this }
|
|
37
|
-
copy(q: QuatLike): this { this.x = cx(q); this.y = cy(q); this.z = cz(q); this.w = cw(q); return this }
|
|
38
|
-
|
|
39
|
-
// --- pure ops ---
|
|
40
|
-
/** Hamilton product `this ⊗ b` (apply `b` first, then `this`). */
|
|
41
|
-
mul(b: QuatLike): Quat {
|
|
42
|
-
const ax = this.x, ay = this.y, az = this.z, aw = this.w
|
|
43
|
-
const bx = cx(b), by = cy(b), bz = cz(b), bw = cw(b)
|
|
44
|
-
return new Quat(
|
|
45
|
-
ax * bw + aw * bx + ay * bz - az * by,
|
|
46
|
-
ay * bw + aw * by + az * bx - ax * bz,
|
|
47
|
-
az * bw + aw * bz + ax * by - ay * bx,
|
|
48
|
-
aw * bw - ax * bx - ay * by - az * bz,
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
normalize(): Quat {
|
|
52
|
-
let l = Math.hypot(this.x, this.y, this.z, this.w)
|
|
53
|
-
if (l === 0) return new Quat(0, 0, 0, 1)
|
|
54
|
-
l = 1 / l
|
|
55
|
-
return new Quat(this.x * l, this.y * l, this.z * l, this.w * l)
|
|
56
|
-
}
|
|
57
|
-
conjugate(): Quat { return new Quat(-this.x, -this.y, -this.z, this.w) }
|
|
58
|
-
/** Inverse rotation. For a unit quaternion this equals `conjugate()`. */
|
|
59
|
-
invert(): Quat {
|
|
60
|
-
const d = this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w
|
|
61
|
-
if (d === 0) return new Quat(0, 0, 0, 0)
|
|
62
|
-
const i = 1 / d
|
|
63
|
-
return new Quat(-this.x * i, -this.y * i, -this.z * i, this.w * i)
|
|
64
|
-
}
|
|
65
|
-
dot(b: QuatLike): number { return this.x * cx(b) + this.y * cy(b) + this.z * cz(b) + this.w * cw(b) }
|
|
66
|
-
/** Spherical interpolation toward `b` by `t ∈ [0, 1]`. */
|
|
67
|
-
slerp(b: QuatLike, t: number): Quat {
|
|
68
|
-
const ax = this.x, ay = this.y, az = this.z, aw = this.w
|
|
69
|
-
let bx = cx(b), by = cy(b), bz = cz(b), bw = cw(b)
|
|
70
|
-
let cosom = ax * bx + ay * by + az * bz + aw * bw
|
|
71
|
-
if (cosom < 0) { cosom = -cosom; bx = -bx; by = -by; bz = -bz; bw = -bw }
|
|
72
|
-
let s0: number, s1: number
|
|
73
|
-
if (1 - cosom > EPS) {
|
|
74
|
-
const omega = Math.acos(cosom), sinom = Math.sin(omega)
|
|
75
|
-
s0 = Math.sin((1 - t) * omega) / sinom
|
|
76
|
-
s1 = Math.sin(t * omega) / sinom
|
|
77
|
-
} else { s0 = 1 - t; s1 = t }
|
|
78
|
-
return new Quat(s0 * ax + s1 * bx, s0 * ay + s1 * by, s0 * az + s1 * bz, s0 * aw + s1 * bw)
|
|
79
|
-
}
|
|
80
|
-
/** Angular distance (radians) between two unit quaternions. */
|
|
81
|
-
angle(b: QuatLike): number {
|
|
82
|
-
const d = this.dot(b)
|
|
83
|
-
return Math.acos(clamp1(2 * d * d - 1))
|
|
84
|
-
}
|
|
85
|
-
/** Rotate a vector by this quaternion → a new `Vec3`. */
|
|
86
|
-
rotateVec3(v: Vec3Like): Vec3 { return new Vec3(v).rotate(this) }
|
|
87
|
-
|
|
88
|
-
/** Extract euler angles (DEGREES) in the given order. */
|
|
89
|
-
toEuler(order: EulerOrder = "YXZ"): Vec3 {
|
|
90
|
-
// build the (column-major) rotation matrix entries from the unit quaternion, then extract.
|
|
91
|
-
const x = this.x, y = this.y, z = this.z, w = this.w
|
|
92
|
-
const x2 = x + x, y2 = y + y, z2 = z + z
|
|
93
|
-
const xx = x * x2, xy = x * y2, xz = x * z2
|
|
94
|
-
const yy = y * y2, yz = y * z2, zz = z * z2
|
|
95
|
-
const wx = w * x2, wy = w * y2, wz = w * z2
|
|
96
|
-
const m = [
|
|
97
|
-
1 - (yy + zz), xy + wz, xz - wy, 0,
|
|
98
|
-
xy - wz, 1 - (xx + zz), yz + wx, 0,
|
|
99
|
-
xz + wy, yz - wx, 1 - (xx + yy), 0,
|
|
100
|
-
]
|
|
101
|
-
return eulerFromColMajor(m, order)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// --- interop ---
|
|
105
|
-
equals(b: QuatLike, eps = 1e-6): boolean {
|
|
106
|
-
return approx(this.x, cx(b), eps) && approx(this.y, cy(b), eps) && approx(this.z, cz(b), eps) && approx(this.w, cw(b), eps)
|
|
107
|
-
}
|
|
108
|
-
/** True if the two unit quaternions represent the same rotation (±q are equal). */
|
|
109
|
-
sameRotation(b: QuatLike, eps = 1e-6): boolean { return Math.abs(this.dot(b)) >= 1 - eps }
|
|
110
|
-
clone(): Quat { return new Quat(this.x, this.y, this.z, this.w) }
|
|
111
|
-
toArray(): [number, number, number, number] { return [ this.x, this.y, this.z, this.w ] }
|
|
112
|
-
*[Symbol.iterator](): Iterator<number> { yield this.x; yield this.y; yield this.z; yield this.w }
|
|
113
|
-
|
|
114
|
-
// --- factories ---
|
|
115
|
-
static from(q: QuatLike): Quat { return new Quat(cx(q), cy(q), cz(q), cw(q)) }
|
|
116
|
-
static get identity(): Quat { return new Quat(0, 0, 0, 1) }
|
|
117
|
-
|
|
118
|
-
/** From euler angles in DEGREES (default order "YXZ"). Inverts `toEuler`. */
|
|
119
|
-
static fromEuler(x: number, y: number, z: number, order: EulerOrder = "YXZ"): Quat {
|
|
120
|
-
const c1 = Math.cos(x * DEG2RAD / 2), c2 = Math.cos(y * DEG2RAD / 2), c3 = Math.cos(z * DEG2RAD / 2)
|
|
121
|
-
const s1 = Math.sin(x * DEG2RAD / 2), s2 = Math.sin(y * DEG2RAD / 2), s3 = Math.sin(z * DEG2RAD / 2)
|
|
122
|
-
switch (order) {
|
|
123
|
-
case "XYZ": return new Quat(s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3)
|
|
124
|
-
case "YXZ": return new Quat(s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3)
|
|
125
|
-
case "ZXY": return new Quat(s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3)
|
|
126
|
-
case "ZYX": return new Quat(s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3)
|
|
127
|
-
case "YZX": return new Quat(s1 * c2 * c3 + c1 * s2 * s3, c1 * s2 * c3 + s1 * c2 * s3, c1 * c2 * s3 - s1 * s2 * c3, c1 * c2 * c3 - s1 * s2 * s3)
|
|
128
|
-
case "XZY": return new Quat(s1 * c2 * c3 - c1 * s2 * s3, c1 * s2 * c3 - s1 * c2 * s3, c1 * c2 * s3 + s1 * s2 * c3, c1 * c2 * c3 + s1 * s2 * s3)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** From a rotation `axis` and an angle in RADIANS. The axis is normalized for you. */
|
|
133
|
-
static fromAxisAngle(axis: Vec3Like, rad: number): Quat {
|
|
134
|
-
let ax = cx(axis), ay = cy(axis), az = cz(axis)
|
|
135
|
-
const l = Math.hypot(ax, ay, az)
|
|
136
|
-
if (l === 0) return new Quat(0, 0, 0, 1)
|
|
137
|
-
ax /= l; ay /= l; az /= l
|
|
138
|
-
const s = Math.sin(rad / 2)
|
|
139
|
-
return new Quat(ax * s, ay * s, az * s, Math.cos(rad / 2))
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** The shortest rotation that takes direction `a` onto direction `b`. */
|
|
143
|
-
static fromTo(a: Vec3Like, b: Vec3Like): Quat {
|
|
144
|
-
const A = new Vec3(a).normalize(), B = new Vec3(b).normalize()
|
|
145
|
-
const d = A.dot(B)
|
|
146
|
-
if (d > 0.999999) return new Quat(0, 0, 0, 1)
|
|
147
|
-
if (d < -0.999999) {
|
|
148
|
-
// antiparallel: rotate 180° about any axis ⟂ A.
|
|
149
|
-
let axis = new Vec3(1, 0, 0).cross(A)
|
|
150
|
-
if (axis.lengthSq() < 1e-12) axis = new Vec3(0, 1, 0).cross(A)
|
|
151
|
-
return Quat.fromAxisAngle(axis.normalize(), Math.PI)
|
|
152
|
-
}
|
|
153
|
-
const c = A.cross(B)
|
|
154
|
-
return new Quat(c.x, c.y, c.z, 1 + d).normalize()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** A rotation whose forward (−Z) points along `forward`, with `up` as the reference up axis. */
|
|
158
|
-
static lookRotation(forward: Vec3Like, up: Vec3Like = Vec3.up): Quat {
|
|
159
|
-
const zc = new Vec3(forward).normalize().negate() // local +Z basis (forward is −Z)
|
|
160
|
-
let xc = new Vec3(up).cross(zc)
|
|
161
|
-
if (xc.lengthSq() < 1e-12) xc = new Vec3(up).cross(zc.add([ 1e-4, 0, 0 ]))
|
|
162
|
-
xc = xc.normalize()
|
|
163
|
-
const yc = zc.cross(xc)
|
|
164
|
-
return quatFromColMajor([ xc.x, xc.y, xc.z, 0, yc.x, yc.y, yc.z, 0, zc.x, zc.y, zc.z ])
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** Quaternion from a column-major rotation matrix (≥3×3, indices 0,1,2,4,5,6,8,9,10). gl-matrix's
|
|
169
|
-
* `getRotation` algorithm — the exact inverse of building a matrix from a quaternion. */
|
|
170
|
-
export const quatFromColMajor = (m: ArrayLike<number>): Quat => {
|
|
171
|
-
const m00 = m[0], m01 = m[1], m02 = m[2]
|
|
172
|
-
const m10 = m[4], m11 = m[5], m12 = m[6]
|
|
173
|
-
const m20 = m[8], m21 = m[9], m22 = m[10]
|
|
174
|
-
const trace = m00 + m11 + m22
|
|
175
|
-
let S: number
|
|
176
|
-
if (trace > 0) {
|
|
177
|
-
S = Math.sqrt(trace + 1) * 2
|
|
178
|
-
return new Quat((m12 - m21) / S, (m20 - m02) / S, (m01 - m10) / S, 0.25 * S)
|
|
179
|
-
} else if (m00 > m11 && m00 > m22) {
|
|
180
|
-
S = Math.sqrt(1 + m00 - m11 - m22) * 2
|
|
181
|
-
return new Quat(0.25 * S, (m01 + m10) / S, (m20 + m02) / S, (m12 - m21) / S)
|
|
182
|
-
} else if (m11 > m22) {
|
|
183
|
-
S = Math.sqrt(1 + m11 - m00 - m22) * 2
|
|
184
|
-
return new Quat((m01 + m10) / S, 0.25 * S, (m12 + m21) / S, (m20 - m02) / S)
|
|
185
|
-
} else {
|
|
186
|
-
S = Math.sqrt(1 + m22 - m00 - m11) * 2
|
|
187
|
-
return new Quat((m20 + m02) / S, (m12 + m21) / S, 0.25 * S, (m01 - m10) / S)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** Euler angles (DEGREES) from a column-major rotation matrix, standard three.js extraction.
|
|
192
|
-
* Element naming follows the column-major layout: mRC = m[(C-1)*4 + (R-1)]. */
|
|
193
|
-
export const eulerFromColMajor = (m: ArrayLike<number>, order: EulerOrder = "YXZ"): Vec3 => {
|
|
194
|
-
const m11 = m[0], m21 = m[1], m31 = m[2]
|
|
195
|
-
const m12 = m[4], m22 = m[5], m32 = m[6]
|
|
196
|
-
const m13 = m[8], m23 = m[9], m33 = m[10]
|
|
197
|
-
let x = 0, y = 0, z = 0
|
|
198
|
-
const LIM = 0.9999999
|
|
199
|
-
switch (order) {
|
|
200
|
-
case "XYZ":
|
|
201
|
-
y = Math.asin(clamp1(m13))
|
|
202
|
-
if (Math.abs(m13) < LIM) { x = Math.atan2(-m23, m33); z = Math.atan2(-m12, m11) }
|
|
203
|
-
else { x = Math.atan2(m32, m22); z = 0 }
|
|
204
|
-
break
|
|
205
|
-
case "YXZ":
|
|
206
|
-
x = Math.asin(-clamp1(m23))
|
|
207
|
-
if (Math.abs(m23) < LIM) { y = Math.atan2(m13, m33); z = Math.atan2(m21, m22) }
|
|
208
|
-
else { y = Math.atan2(-m31, m11); z = 0 }
|
|
209
|
-
break
|
|
210
|
-
case "ZXY":
|
|
211
|
-
x = Math.asin(clamp1(m32))
|
|
212
|
-
if (Math.abs(m32) < LIM) { y = Math.atan2(-m31, m33); z = Math.atan2(-m12, m22) }
|
|
213
|
-
else { y = 0; z = Math.atan2(m21, m11) }
|
|
214
|
-
break
|
|
215
|
-
case "ZYX":
|
|
216
|
-
y = Math.asin(-clamp1(m31))
|
|
217
|
-
if (Math.abs(m31) < LIM) { x = Math.atan2(m32, m33); z = Math.atan2(m21, m11) }
|
|
218
|
-
else { x = 0; z = Math.atan2(-m12, m22) }
|
|
219
|
-
break
|
|
220
|
-
case "YZX":
|
|
221
|
-
z = Math.asin(clamp1(m21))
|
|
222
|
-
if (Math.abs(m21) < LIM) { x = Math.atan2(-m23, m22); y = Math.atan2(-m31, m11) }
|
|
223
|
-
else { x = 0; y = Math.atan2(m13, m33) }
|
|
224
|
-
break
|
|
225
|
-
case "XZY":
|
|
226
|
-
z = Math.asin(-clamp1(m12))
|
|
227
|
-
if (Math.abs(m12) < LIM) { x = Math.atan2(m32, m22); y = Math.atan2(m13, m11) }
|
|
228
|
-
else { x = Math.atan2(-m23, m33); y = 0 }
|
|
229
|
-
break
|
|
230
|
-
}
|
|
231
|
-
return new Vec3(x * RAD2DEG, y * RAD2DEG, z * RAD2DEG)
|
|
232
|
-
}
|
package/runtime/sdk/math/vec.ts
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
// Fluent 2D / 3D vectors — the SDK's user-facing vector math (no gl-matrix, no out-params, no imports).
|
|
2
|
-
//
|
|
3
|
-
// Design (see PLAN.md): a vector is a *mutable little struct* — `v.x = 3`, `v.set(x, y, z)` mutate in
|
|
4
|
-
// place — but every *method* is PURE: `a.add(b)` returns a NEW vector and never touches `a`. So chains
|
|
5
|
-
// (`dir.normalize().scale(s)`) can't clobber their source, and the immutable-method style is the most
|
|
6
|
-
// amenable to a future escape-analysis / scalar-replacement compiler pass. Backing is plain number
|
|
7
|
-
// fields (not a Float32Array), which the optimizer can trivially scalar-replace.
|
|
8
|
-
//
|
|
9
|
-
// "Never wrap away the metal": vectors expose `.x/.y/.z`, are iterable (`const [x, y] = v`, `[...v]`),
|
|
10
|
-
// and every method / constructor accepts a raw tuple just as well as a wrapper. Reads off a `*Like`
|
|
11
|
-
// input go through the `cx/cy/cz` helpers below, which work uniformly on a wrapper (`.x`) or an
|
|
12
|
-
// array-like (`[0]`), so callers can always pass `[0, 1, 0]` where a vector is expected.
|
|
13
|
-
|
|
14
|
-
export type Vec2Like = Vec2 | readonly [number, number] | number[] | Float32Array
|
|
15
|
-
export type Vec3Like = Vec3 | readonly [number, number, number] | number[] | Float32Array
|
|
16
|
-
|
|
17
|
-
/** Read the x / first component of any vector-like (wrapper, tuple, or typed array). `?? ` is safe on
|
|
18
|
-
* 0 and NaN — it only falls through on `undefined` (i.e. a non-wrapper that has no `.x`). */
|
|
19
|
-
export const cx = (v: { x?: number } | ArrayLike<number>): number => (v as { x?: number }).x ?? (v as ArrayLike<number>)[0]
|
|
20
|
-
export const cy = (v: { y?: number } | ArrayLike<number>): number => (v as { y?: number }).y ?? (v as ArrayLike<number>)[1]
|
|
21
|
-
export const cz = (v: { z?: number } | ArrayLike<number>): number => (v as { z?: number }).z ?? (v as ArrayLike<number>)[2]
|
|
22
|
-
|
|
23
|
-
/** Relative-epsilon compare (matches gl-matrix's `equals`), so the oracle tests agree to the bit. */
|
|
24
|
-
const approx = (a: number, b: number, eps: number): boolean => Math.abs(a - b) <= eps * Math.max(1, Math.abs(a), Math.abs(b))
|
|
25
|
-
|
|
26
|
-
// ---------------------------------------------------------------------------------------------------
|
|
27
|
-
// Vec2
|
|
28
|
-
// ---------------------------------------------------------------------------------------------------
|
|
29
|
-
|
|
30
|
-
export class Vec2 {
|
|
31
|
-
x: number
|
|
32
|
-
y: number
|
|
33
|
-
|
|
34
|
-
constructor(x: number | Vec2Like = 0, y = 0) {
|
|
35
|
-
if (typeof x === "number") { this.x = x; this.y = y }
|
|
36
|
-
else { this.x = cx(x); this.y = cy(x) }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// --- mutation (the only methods that change `this`) ---
|
|
40
|
-
/** Mutate the components in place. Returns `this`. */
|
|
41
|
-
set(x: number, y: number): this { this.x = x; this.y = y; return this }
|
|
42
|
-
/** Copy another vector's components into this one. Returns `this`. */
|
|
43
|
-
copy(v: Vec2Like): this { this.x = cx(v); this.y = cy(v); return this }
|
|
44
|
-
|
|
45
|
-
// --- pure arithmetic (each returns a NEW Vec2) ---
|
|
46
|
-
add(v: Vec2Like): Vec2 { return new Vec2(this.x + cx(v), this.y + cy(v)) }
|
|
47
|
-
sub(v: Vec2Like): Vec2 { return new Vec2(this.x - cx(v), this.y - cy(v)) }
|
|
48
|
-
mul(v: Vec2Like): Vec2 { return new Vec2(this.x * cx(v), this.y * cy(v)) }
|
|
49
|
-
div(v: Vec2Like): Vec2 { return new Vec2(this.x / cx(v), this.y / cy(v)) }
|
|
50
|
-
scale(s: number): Vec2 { return new Vec2(this.x * s, this.y * s) }
|
|
51
|
-
/** `this + v * s` (fused multiply-add). */
|
|
52
|
-
scaleAndAdd(v: Vec2Like, s: number): Vec2 { return new Vec2(this.x + cx(v) * s, this.y + cy(v) * s) }
|
|
53
|
-
negate(): Vec2 { return new Vec2(-this.x, -this.y) }
|
|
54
|
-
|
|
55
|
-
// --- products / measures ---
|
|
56
|
-
dot(v: Vec2Like): number { return this.x * cx(v) + this.y * cy(v) }
|
|
57
|
-
/** 2D cross product → the scalar z of the 3D cross (signed parallelogram area). */
|
|
58
|
-
cross(v: Vec2Like): number { return this.x * cy(v) - this.y * cx(v) }
|
|
59
|
-
length(): number { return Math.hypot(this.x, this.y) }
|
|
60
|
-
lengthSq(): number { return this.x * this.x + this.y * this.y }
|
|
61
|
-
distanceTo(v: Vec2Like): number { return Math.hypot(this.x - cx(v), this.y - cy(v)) }
|
|
62
|
-
distanceSqTo(v: Vec2Like): number { const dx = this.x - cx(v), dy = this.y - cy(v); return dx * dx + dy * dy }
|
|
63
|
-
|
|
64
|
-
normalize(): Vec2 {
|
|
65
|
-
const l = Math.hypot(this.x, this.y)
|
|
66
|
-
return l === 0 ? new Vec2(0, 0) : new Vec2(this.x / l, this.y / l)
|
|
67
|
-
}
|
|
68
|
-
lerp(v: Vec2Like, t: number): Vec2 { return new Vec2(this.x + (cx(v) - this.x) * t, this.y + (cy(v) - this.y) * t) }
|
|
69
|
-
clamp(min: Vec2Like, max: Vec2Like): Vec2 {
|
|
70
|
-
return new Vec2(clampN(this.x, cx(min), cx(max)), clampN(this.y, cy(min), cy(max)))
|
|
71
|
-
}
|
|
72
|
-
min(v: Vec2Like): Vec2 { return new Vec2(Math.min(this.x, cx(v)), Math.min(this.y, cy(v))) }
|
|
73
|
-
max(v: Vec2Like): Vec2 { return new Vec2(Math.max(this.x, cx(v)), Math.max(this.y, cy(v))) }
|
|
74
|
-
/** Left-hand perpendicular: (-y, x), a 90° CCW turn. */
|
|
75
|
-
perp(): Vec2 { return new Vec2(-this.y, this.x) }
|
|
76
|
-
/** Rotate by `rad` (CCW) around `origin` (default 0). */
|
|
77
|
-
rotate(rad: number, origin: Vec2Like = ZERO2): Vec2 {
|
|
78
|
-
const ox = cx(origin), oy = cy(origin)
|
|
79
|
-
const c = Math.cos(rad), s = Math.sin(rad)
|
|
80
|
-
const dx = this.x - ox, dy = this.y - oy
|
|
81
|
-
return new Vec2(ox + dx * c - dy * s, oy + dx * s + dy * c)
|
|
82
|
-
}
|
|
83
|
-
/** The vector's heading in radians: `atan2(y, x)`. */
|
|
84
|
-
heading(): number { return Math.atan2(this.y, this.x) }
|
|
85
|
-
/** Angle (radians) between this and `v`. */
|
|
86
|
-
angle(v: Vec2Like): number {
|
|
87
|
-
const mag = Math.sqrt(this.lengthSq() * (cx(v) * cx(v) + cy(v) * cy(v)))
|
|
88
|
-
return mag === 0 ? 0 : Math.acos(clampN(this.dot(v) / mag, -1, 1))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
withX(x: number): Vec2 { return new Vec2(x, this.y) }
|
|
92
|
-
withY(y: number): Vec2 { return new Vec2(this.x, y) }
|
|
93
|
-
|
|
94
|
-
// --- interop ---
|
|
95
|
-
equals(v: Vec2Like, eps = 1e-6): boolean { return approx(this.x, cx(v), eps) && approx(this.y, cy(v), eps) }
|
|
96
|
-
clone(): Vec2 { return new Vec2(this.x, this.y) }
|
|
97
|
-
toArray(): [number, number] { return [ this.x, this.y ] }
|
|
98
|
-
*[Symbol.iterator](): Iterator<number> { yield this.x; yield this.y }
|
|
99
|
-
|
|
100
|
-
static from(v: Vec2Like): Vec2 { return new Vec2(cx(v), cy(v)) }
|
|
101
|
-
static get zero(): Vec2 { return new Vec2(0, 0) }
|
|
102
|
-
static get one(): Vec2 { return new Vec2(1, 1) }
|
|
103
|
-
static get up(): Vec2 { return new Vec2(0, 1) }
|
|
104
|
-
static get down(): Vec2 { return new Vec2(0, -1) }
|
|
105
|
-
static get left(): Vec2 { return new Vec2(-1, 0) }
|
|
106
|
-
static get right(): Vec2 { return new Vec2(1, 0) }
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ---------------------------------------------------------------------------------------------------
|
|
110
|
-
// Vec3
|
|
111
|
-
// ---------------------------------------------------------------------------------------------------
|
|
112
|
-
|
|
113
|
-
export class Vec3 {
|
|
114
|
-
x: number
|
|
115
|
-
y: number
|
|
116
|
-
z: number
|
|
117
|
-
|
|
118
|
-
constructor(x: number | Vec3Like = 0, y = 0, z = 0) {
|
|
119
|
-
if (typeof x === "number") { this.x = x; this.y = y; this.z = z }
|
|
120
|
-
else { this.x = cx(x); this.y = cy(x); this.z = cz(x) }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// --- mutation ---
|
|
124
|
-
set(x: number, y: number, z: number): this { this.x = x; this.y = y; this.z = z; return this }
|
|
125
|
-
copy(v: Vec3Like): this { this.x = cx(v); this.y = cy(v); this.z = cz(v); return this }
|
|
126
|
-
|
|
127
|
-
// --- pure arithmetic ---
|
|
128
|
-
add(v: Vec3Like): Vec3 { return new Vec3(this.x + cx(v), this.y + cy(v), this.z + cz(v)) }
|
|
129
|
-
sub(v: Vec3Like): Vec3 { return new Vec3(this.x - cx(v), this.y - cy(v), this.z - cz(v)) }
|
|
130
|
-
mul(v: Vec3Like): Vec3 { return new Vec3(this.x * cx(v), this.y * cy(v), this.z * cz(v)) }
|
|
131
|
-
div(v: Vec3Like): Vec3 { return new Vec3(this.x / cx(v), this.y / cy(v), this.z / cz(v)) }
|
|
132
|
-
scale(s: number): Vec3 { return new Vec3(this.x * s, this.y * s, this.z * s) }
|
|
133
|
-
/** `this + v * s` (fused multiply-add). */
|
|
134
|
-
scaleAndAdd(v: Vec3Like, s: number): Vec3 { return new Vec3(this.x + cx(v) * s, this.y + cy(v) * s, this.z + cz(v) * s) }
|
|
135
|
-
negate(): Vec3 { return new Vec3(-this.x, -this.y, -this.z) }
|
|
136
|
-
|
|
137
|
-
// --- products / measures ---
|
|
138
|
-
dot(v: Vec3Like): number { return this.x * cx(v) + this.y * cy(v) + this.z * cz(v) }
|
|
139
|
-
cross(v: Vec3Like): Vec3 {
|
|
140
|
-
const bx = cx(v), by = cy(v), bz = cz(v)
|
|
141
|
-
return new Vec3(this.y * bz - this.z * by, this.z * bx - this.x * bz, this.x * by - this.y * bx)
|
|
142
|
-
}
|
|
143
|
-
length(): number { return Math.hypot(this.x, this.y, this.z) }
|
|
144
|
-
lengthSq(): number { return this.x * this.x + this.y * this.y + this.z * this.z }
|
|
145
|
-
distanceTo(v: Vec3Like): number { return Math.hypot(this.x - cx(v), this.y - cy(v), this.z - cz(v)) }
|
|
146
|
-
distanceSqTo(v: Vec3Like): number {
|
|
147
|
-
const dx = this.x - cx(v), dy = this.y - cy(v), dz = this.z - cz(v)
|
|
148
|
-
return dx * dx + dy * dy + dz * dz
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
normalize(): Vec3 {
|
|
152
|
-
const l = Math.hypot(this.x, this.y, this.z)
|
|
153
|
-
return l === 0 ? new Vec3(0, 0, 0) : new Vec3(this.x / l, this.y / l, this.z / l)
|
|
154
|
-
}
|
|
155
|
-
lerp(v: Vec3Like, t: number): Vec3 {
|
|
156
|
-
return new Vec3(this.x + (cx(v) - this.x) * t, this.y + (cy(v) - this.y) * t, this.z + (cz(v) - this.z) * t)
|
|
157
|
-
}
|
|
158
|
-
clamp(min: Vec3Like, max: Vec3Like): Vec3 {
|
|
159
|
-
return new Vec3(clampN(this.x, cx(min), cx(max)), clampN(this.y, cy(min), cy(max)), clampN(this.z, cz(min), cz(max)))
|
|
160
|
-
}
|
|
161
|
-
min(v: Vec3Like): Vec3 { return new Vec3(Math.min(this.x, cx(v)), Math.min(this.y, cy(v)), Math.min(this.z, cz(v))) }
|
|
162
|
-
max(v: Vec3Like): Vec3 { return new Vec3(Math.max(this.x, cx(v)), Math.max(this.y, cy(v)), Math.max(this.z, cz(v))) }
|
|
163
|
-
/** Reflect this vector about a (unit) `normal`: `v - 2·(v·n)·n`. */
|
|
164
|
-
reflect(normal: Vec3Like): Vec3 {
|
|
165
|
-
const d = 2 * this.dot(normal)
|
|
166
|
-
return new Vec3(this.x - d * cx(normal), this.y - d * cy(normal), this.z - d * cz(normal))
|
|
167
|
-
}
|
|
168
|
-
/** Vector projection of this onto `onto`. */
|
|
169
|
-
project(onto: Vec3Like): Vec3 {
|
|
170
|
-
const ox = cx(onto), oy = cy(onto), oz = cz(onto)
|
|
171
|
-
const denom = ox * ox + oy * oy + oz * oz
|
|
172
|
-
if (denom === 0) return new Vec3(0, 0, 0)
|
|
173
|
-
const k = this.dot(onto) / denom
|
|
174
|
-
return new Vec3(ox * k, oy * k, oz * k)
|
|
175
|
-
}
|
|
176
|
-
/** Angle (radians) between this and `v`. */
|
|
177
|
-
angle(v: Vec3Like): number {
|
|
178
|
-
const bx = cx(v), by = cy(v), bz = cz(v)
|
|
179
|
-
const mag = Math.sqrt(this.lengthSq() * (bx * bx + by * by + bz * bz))
|
|
180
|
-
return mag === 0 ? 0 : Math.acos(clampN(this.dot(v) / mag, -1, 1))
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// --- rotation ---
|
|
184
|
-
/** Rotate this point around the X axis through `origin` (default 0) by `rad`. */
|
|
185
|
-
rotateX(rad: number, origin: Vec3Like = ZERO3): Vec3 {
|
|
186
|
-
const ox = cx(origin), oy = cy(origin), oz = cz(origin)
|
|
187
|
-
const py = this.y - oy, pz = this.z - oz, c = Math.cos(rad), s = Math.sin(rad)
|
|
188
|
-
return new Vec3(this.x, oy + py * c - pz * s, oz + py * s + pz * c)
|
|
189
|
-
}
|
|
190
|
-
rotateY(rad: number, origin: Vec3Like = ZERO3): Vec3 {
|
|
191
|
-
const ox = cx(origin), oy = cy(origin), oz = cz(origin)
|
|
192
|
-
const px = this.x - ox, pz = this.z - oz, c = Math.cos(rad), s = Math.sin(rad)
|
|
193
|
-
return new Vec3(ox + pz * s + px * c, this.y, oz + pz * c - px * s)
|
|
194
|
-
}
|
|
195
|
-
rotateZ(rad: number, origin: Vec3Like = ZERO3): Vec3 {
|
|
196
|
-
const ox = cx(origin), oy = cy(origin), oz = cz(origin)
|
|
197
|
-
const px = this.x - ox, py = this.y - oy, c = Math.cos(rad), s = Math.sin(rad)
|
|
198
|
-
return new Vec3(ox + px * c - py * s, oy + px * s + py * c, this.z)
|
|
199
|
-
}
|
|
200
|
-
/** Rotate this vector by a quaternion (x, y, z, w). Accepts a `Quat` or a raw `[x,y,z,w]`. */
|
|
201
|
-
rotate(q: ArrayLike<number> | { x: number, y: number, z: number, w: number }): Vec3 {
|
|
202
|
-
const qx = cx(q as Vec3Like), qy = cy(q as Vec3Like), qz = cz(q as Vec3Like)
|
|
203
|
-
const qw = (q as { w?: number }).w ?? (q as ArrayLike<number>)[3]
|
|
204
|
-
const vx = this.x, vy = this.y, vz = this.z
|
|
205
|
-
// t = 2 * (q.xyz × v)
|
|
206
|
-
let tx = qy * vz - qz * vy, ty = qz * vx - qx * vz, tz = qx * vy - qy * vx
|
|
207
|
-
tx += tx; ty += ty; tz += tz
|
|
208
|
-
// v + w·t + q.xyz × t
|
|
209
|
-
return new Vec3(
|
|
210
|
-
vx + qw * tx + qy * tz - qz * ty,
|
|
211
|
-
vy + qw * ty + qz * tx - qx * tz,
|
|
212
|
-
vz + qw * tz + qx * ty - qy * tx,
|
|
213
|
-
)
|
|
214
|
-
}
|
|
215
|
-
/** Transform this point by a column-major mat4 (a `Mat4`, its `.m`, or a raw length-16 array),
|
|
216
|
-
* applying translation and the perspective divide. */
|
|
217
|
-
transform(m: ArrayLike<number> | { m: ArrayLike<number> }): Vec3 {
|
|
218
|
-
const a = (m as { m?: ArrayLike<number> }).m ?? (m as ArrayLike<number>)
|
|
219
|
-
const x = this.x, y = this.y, z = this.z
|
|
220
|
-
let w = a[3] * x + a[7] * y + a[11] * z + a[15]
|
|
221
|
-
w = w || 1
|
|
222
|
-
return new Vec3(
|
|
223
|
-
(a[0] * x + a[4] * y + a[8] * z + a[12]) / w,
|
|
224
|
-
(a[1] * x + a[5] * y + a[9] * z + a[13]) / w,
|
|
225
|
-
(a[2] * x + a[6] * y + a[10] * z + a[14]) / w,
|
|
226
|
-
)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
withX(x: number): Vec3 { return new Vec3(x, this.y, this.z) }
|
|
230
|
-
withY(y: number): Vec3 { return new Vec3(this.x, y, this.z) }
|
|
231
|
-
withZ(z: number): Vec3 { return new Vec3(this.x, this.y, z) }
|
|
232
|
-
|
|
233
|
-
// --- interop ---
|
|
234
|
-
equals(v: Vec3Like, eps = 1e-6): boolean {
|
|
235
|
-
return approx(this.x, cx(v), eps) && approx(this.y, cy(v), eps) && approx(this.z, cz(v), eps)
|
|
236
|
-
}
|
|
237
|
-
clone(): Vec3 { return new Vec3(this.x, this.y, this.z) }
|
|
238
|
-
toArray(): [number, number, number] { return [ this.x, this.y, this.z ] }
|
|
239
|
-
*[Symbol.iterator](): Iterator<number> { yield this.x; yield this.y; yield this.z }
|
|
240
|
-
|
|
241
|
-
static from(v: Vec3Like): Vec3 { return new Vec3(cx(v), cy(v), cz(v)) }
|
|
242
|
-
static get zero(): Vec3 { return new Vec3(0, 0, 0) }
|
|
243
|
-
static get one(): Vec3 { return new Vec3(1, 1, 1) }
|
|
244
|
-
static get up(): Vec3 { return new Vec3(0, 1, 0) }
|
|
245
|
-
static get down(): Vec3 { return new Vec3(0, -1, 0) }
|
|
246
|
-
static get left(): Vec3 { return new Vec3(-1, 0, 0) }
|
|
247
|
-
static get right(): Vec3 { return new Vec3(1, 0, 0) }
|
|
248
|
-
/** Forward is −Z (the engine's cameras look down −Z; `node.forward` is −Z at identity). */
|
|
249
|
-
static get forward(): Vec3 { return new Vec3(0, 0, -1) }
|
|
250
|
-
static get back(): Vec3 { return new Vec3(0, 0, 1) }
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const clampN = (v: number, lo: number, hi: number): number => (v < lo ? lo : v > hi ? hi : v)
|
|
254
|
-
const ZERO2 = new Vec2(0, 0)
|
|
255
|
-
const ZERO3 = new Vec3(0, 0, 0)
|
package/runtime/sdk/net/codec.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// Defaults → wire layout (docs/multiplayer-plan.md §4 "input: defaults object = layout"). The hot
|
|
2
|
-
// stream — inputs and snapshot states — is binary and its shape comes from a plain defaults object:
|
|
3
|
-
// number → f32
|
|
4
|
-
// boolean → 1 bit (packed after the floats)
|
|
5
|
-
// number[] → f32 × length, fixed
|
|
6
|
-
// Nothing else: no strings, no nesting, no optionals — snapshot fields are flat and fixed-size by
|
|
7
|
-
// design. Both sides run the same bundle, so the layout is derived, never transmitted; the `sig`
|
|
8
|
-
// feeds the schema hash the handshake compares.
|
|
9
|
-
|
|
10
|
-
export type FieldKind = 0 | 1 | 2 // f32 · bool · f32[]
|
|
11
|
-
export type Field = { key: string; kind: FieldKind; len: number }
|
|
12
|
-
export type Layout = { fields: Field[]; floats: number; bools: number; bytes: number; sig: string }
|
|
13
|
-
|
|
14
|
-
const describe = (v: unknown): string => Array.isArray(v) ? `array of ${v.length}` : v === null ? "null" : typeof v
|
|
15
|
-
|
|
16
|
-
export const makeLayout = (defaults: Record<string, unknown>, what: string): Layout => {
|
|
17
|
-
const fields: Field[] = []
|
|
18
|
-
let floats = 0
|
|
19
|
-
let bools = 0
|
|
20
|
-
for (const key of Object.keys(defaults)) {
|
|
21
|
-
const v = defaults[key]
|
|
22
|
-
if (typeof v === "number") { fields.push({ key, kind: 0, len: 1 }); floats++ }
|
|
23
|
-
else if (typeof v === "boolean") { fields.push({ key, kind: 1, len: 1 }); bools++ }
|
|
24
|
-
else if (Array.isArray(v) && v.length > 0 && v.length <= 64 && v.every((n) => typeof n === "number")) {
|
|
25
|
-
fields.push({ key, kind: 2, len: v.length }); floats += v.length
|
|
26
|
-
} else {
|
|
27
|
-
throw new Error(`${what}: field '${key}' must be a number, a boolean or a fixed-length number[] (got ${describe(v)})`)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
const sig = fields.map((f) => `${f.key}:${f.kind}${f.len > 1 ? f.len : ""}`).join(",")
|
|
31
|
-
return { fields, floats, bools, bytes: floats * 4 + Math.ceil(bools / 8), sig }
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** A fresh state object shaped like the defaults (arrays copied, never shared). */
|
|
35
|
-
export const cloneDefaults = <T extends object>(defaults: T): T => {
|
|
36
|
-
const out: Record<string, unknown> = {}
|
|
37
|
-
for (const key of Object.keys(defaults)) {
|
|
38
|
-
const v = (defaults as Record<string, unknown>)[key]
|
|
39
|
-
out[key] = Array.isArray(v) ? v.slice() : v
|
|
40
|
-
}
|
|
41
|
-
return out as T
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Copy the layout's fields from `from` into `into` (array elements copied in place; unknown keys
|
|
45
|
-
* ignored — a JSON spawn state or a partial `setInput`). */
|
|
46
|
-
export const assignState = (layout: Layout, into: any, from: any): void => {
|
|
47
|
-
if (!from) return
|
|
48
|
-
for (const f of layout.fields) {
|
|
49
|
-
const v = from[f.key]
|
|
50
|
-
if (v === undefined) continue
|
|
51
|
-
if (f.kind === 2) {
|
|
52
|
-
const a = into[f.key] as number[]
|
|
53
|
-
for (let i = 0; i < f.len; i++) a[i] = +(v[i] ?? 0)
|
|
54
|
-
} else if (f.kind === 1) into[f.key] = !!v
|
|
55
|
-
else into[f.key] = +v
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export const packInto = (view: DataView, offset: number, layout: Layout, s: any): number => {
|
|
60
|
-
let o = offset
|
|
61
|
-
for (const f of layout.fields) {
|
|
62
|
-
if (f.kind === 0) { view.setFloat32(o, s[f.key], true); o += 4 }
|
|
63
|
-
else if (f.kind === 2) {
|
|
64
|
-
const a = s[f.key] as number[]
|
|
65
|
-
for (let i = 0; i < f.len; i++) { view.setFloat32(o, a[i] ?? 0, true); o += 4 }
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
let bits = 0
|
|
69
|
-
let n = 0
|
|
70
|
-
for (const f of layout.fields) {
|
|
71
|
-
if (f.kind !== 1) continue
|
|
72
|
-
if (s[f.key]) bits |= 1 << (n & 7)
|
|
73
|
-
n++
|
|
74
|
-
if ((n & 7) === 0) { view.setUint8(o++, bits); bits = 0 }
|
|
75
|
-
}
|
|
76
|
-
if (n & 7) view.setUint8(o++, bits)
|
|
77
|
-
return o
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const unpackFrom = (view: DataView, offset: number, layout: Layout, into: any): number => {
|
|
81
|
-
let o = offset
|
|
82
|
-
for (const f of layout.fields) {
|
|
83
|
-
if (f.kind === 0) { into[f.key] = view.getFloat32(o, true); o += 4 }
|
|
84
|
-
else if (f.kind === 2) {
|
|
85
|
-
const a = into[f.key] as number[]
|
|
86
|
-
for (let i = 0; i < f.len; i++) { a[i] = view.getFloat32(o, true); o += 4 }
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
let bits = 0
|
|
90
|
-
let n = 0
|
|
91
|
-
for (const f of layout.fields) {
|
|
92
|
-
if (f.kind !== 1) continue
|
|
93
|
-
if ((n & 7) === 0) bits = view.getUint8(o++)
|
|
94
|
-
into[f.key] = (bits & (1 << (n & 7))) !== 0
|
|
95
|
-
n++
|
|
96
|
-
}
|
|
97
|
-
return o
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** `out = a + (b − a) · t` for the numeric fields; booleans take `b`'s. */
|
|
101
|
-
export const lerpState = (layout: Layout, a: any, b: any, t: number, out: any): void => {
|
|
102
|
-
for (const f of layout.fields) {
|
|
103
|
-
if (f.kind === 0) out[f.key] = a[f.key] + (b[f.key] - a[f.key]) * t
|
|
104
|
-
else if (f.kind === 2) {
|
|
105
|
-
const pa = a[f.key] as number[], pb = b[f.key] as number[], po = out[f.key] as number[]
|
|
106
|
-
for (let i = 0; i < f.len; i++) po[i] = pa[i] + (pb[i] - pa[i]) * t
|
|
107
|
-
} else out[f.key] = b[f.key]
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** FNV-1a over a string → 8 hex chars. Enough to tell two bundles' schemas apart. */
|
|
112
|
-
export const hashString = (s: string): string => {
|
|
113
|
-
let h = 0x811c9dc5
|
|
114
|
-
for (let i = 0; i < s.length; i++) {
|
|
115
|
-
h ^= s.charCodeAt(i)
|
|
116
|
-
h = Math.imul(h, 0x01000193)
|
|
117
|
-
}
|
|
118
|
-
return (h >>> 0).toString(16).padStart(8, "0")
|
|
119
|
-
}
|