@nossen/morphing 1.0.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/LICENSE +21 -0
- package/README.md +34 -0
- package/dist/morph.d.ts +98 -0
- package/dist/morph.js +268 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 funeste38
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @nossen/morphing
|
|
2
|
+
|
|
3
|
+
Compact 4-byte value shapes and RGBA/cube helpers for low-friction transport.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
npm install @nossen/morphing
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { enigma } from "@nossen/morphing";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Package Scope
|
|
18
|
+
|
|
19
|
+
This is the NOSSEN distribution of `@funeste38/morphing`. New projects should use `@nossen/morphing`. Existing projects can migrate by replacing imports from `@funeste38/morphing` with `@nossen/morphing`.
|
|
20
|
+
|
|
21
|
+
## Quality Gates
|
|
22
|
+
|
|
23
|
+
| Task | Command |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Build | `npm run build` |
|
|
26
|
+
| Test | `npm test` |
|
|
27
|
+
|
|
28
|
+
## Publishing
|
|
29
|
+
|
|
30
|
+
This package is published as `@nossen/morphing@1.0.0` and mirrored to the Funesterie JFrog npm registry. Keep runtime files limited through the `files` field before publishing.
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
See the package license and repository files for terms.
|
package/dist/morph.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export declare enum MorphKind {
|
|
2
|
+
Int24 = 0,
|
|
3
|
+
FloatQ = 1,
|
|
4
|
+
Flag = 2,
|
|
5
|
+
Ref = 3
|
|
6
|
+
}
|
|
7
|
+
export type morph = {
|
|
8
|
+
/** TT = 2 bits type + 6 bits subtype */
|
|
9
|
+
tt: number;
|
|
10
|
+
d1: number;
|
|
11
|
+
d2: number;
|
|
12
|
+
d3: number;
|
|
13
|
+
};
|
|
14
|
+
export type RgbaTuple = [number, number, number, number];
|
|
15
|
+
export type MorphCubeAxis = string | number;
|
|
16
|
+
export type MorphCube<T = MorphCubeAxis> = {
|
|
17
|
+
r: T;
|
|
18
|
+
g: T;
|
|
19
|
+
b: T;
|
|
20
|
+
a: T;
|
|
21
|
+
};
|
|
22
|
+
export declare function makeMorph(kind: MorphKind, subtype: number, data24: number): morph;
|
|
23
|
+
export declare function kindOf(m: morph): MorphKind;
|
|
24
|
+
export declare function subtypeOf(m: morph): number;
|
|
25
|
+
export declare function data24Of(m: morph): number;
|
|
26
|
+
/** Int24 helper */
|
|
27
|
+
export declare function fromInt24(value: number, subtype?: number): morph;
|
|
28
|
+
export declare function toInt24(m: morph): number;
|
|
29
|
+
/** Signed Int24 helper */
|
|
30
|
+
export declare function fromSignedInt24(value: number, subtype?: number): morph;
|
|
31
|
+
export declare function toSignedInt24(m: morph): number;
|
|
32
|
+
/** Bool / flag helper */
|
|
33
|
+
export declare function fromFlag(flag: boolean, subtype?: number): morph;
|
|
34
|
+
export declare function toFlag(m: morph): boolean;
|
|
35
|
+
/** RGBA <-> morph mapping (pour OC8, PNG, etc.) */
|
|
36
|
+
export declare function morphToRgba(m: morph): RgbaTuple;
|
|
37
|
+
export declare function rgbaToMorph(rgba: RgbaTuple): morph;
|
|
38
|
+
/** Pack a morph array into a flat RGBA byte buffer */
|
|
39
|
+
export declare function morphsToRgbaBytes(values: morph[]): Uint8Array;
|
|
40
|
+
export declare function rgbaBytesToMorphs(bytes: ArrayLike<number>): morph[];
|
|
41
|
+
/** Encode en Uint32 (petit utilitaire pratique) */
|
|
42
|
+
export declare function morphToUint32(m: morph): number;
|
|
43
|
+
export declare function uint32ToMorph(v: number): morph;
|
|
44
|
+
export declare const enum MorphSpace {
|
|
45
|
+
Global = 0,
|
|
46
|
+
QFlush = 1,
|
|
47
|
+
Spyder = 2,
|
|
48
|
+
A11 = 3,
|
|
49
|
+
Nossen = 4,
|
|
50
|
+
Five = 5,
|
|
51
|
+
Six = 6,
|
|
52
|
+
Enigma = 7
|
|
53
|
+
}
|
|
54
|
+
export declare const MorphRefSpaces: any[][];
|
|
55
|
+
export declare function subtypeToSpace(subtype: number): MorphSpace;
|
|
56
|
+
export declare function subtypeToLocalTag(subtype: number): number;
|
|
57
|
+
/** Crée un morph REF dans un espace donné */
|
|
58
|
+
export declare function toRef(value: any, space?: MorphSpace, localTag?: number): morph;
|
|
59
|
+
/** Récupère la valeur référencée par un morph REF */
|
|
60
|
+
export declare function deref(m: morph): any;
|
|
61
|
+
/** Clone bit à bit un morph (utile pour alias) */
|
|
62
|
+
export declare function cloneMorph(m: morph): morph;
|
|
63
|
+
/** Alias : m2 devient une copie du "shape" de m1 */
|
|
64
|
+
export declare function aliasMorph(target: morph, source: morph): morph;
|
|
65
|
+
/** Lightweight visual projection for Freeland Bros / RGBA diagnostics */
|
|
66
|
+
export declare function cubeToRgba(cube: MorphCube): RgbaTuple;
|
|
67
|
+
export declare function rgbaToCube(rgba: RgbaTuple): MorphCube<number>;
|
|
68
|
+
/** Enigma JSON : stocke la STRING JSON dans une ref et retourne un morph REF */
|
|
69
|
+
export declare function fromJson(value: any, space?: MorphSpace): morph;
|
|
70
|
+
/** Essaie de décoder un morph REF en JSON */
|
|
71
|
+
export declare function toJson(m: morph): any;
|
|
72
|
+
/** Enigma : stocke une valeur "secrète" dans l'espace Enigma */
|
|
73
|
+
export declare function enigma(value: any, localTag?: number): morph;
|
|
74
|
+
/** Enigma : récupère une valeur dans l'espace Enigma */
|
|
75
|
+
export declare function unveil(m: morph): any;
|
|
76
|
+
export type PorygonShard = {
|
|
77
|
+
id: number;
|
|
78
|
+
morph: morph;
|
|
79
|
+
};
|
|
80
|
+
export type Porygon = {
|
|
81
|
+
layoutId: number;
|
|
82
|
+
shards: PorygonShard[];
|
|
83
|
+
};
|
|
84
|
+
export type PorygonLayout = {
|
|
85
|
+
id: number;
|
|
86
|
+
description: string;
|
|
87
|
+
};
|
|
88
|
+
/** Crée un Porygon à partir d'un objet JSON (1 shard, layoutId=0) */
|
|
89
|
+
export declare function buildPorygonFromJson(value: any, layoutId?: number): Porygon;
|
|
90
|
+
/** Résout un Porygon (simple : 1 shard JSON Enigma) */
|
|
91
|
+
export declare function resolvePorygon(p: Porygon): any;
|
|
92
|
+
/** Cube helpers for higher-level Freeland / RGBA tooling */
|
|
93
|
+
export declare function cubeToRef(cube: MorphCube, space?: MorphSpace, localTag?: number): morph;
|
|
94
|
+
export declare function refToCube(m: morph): MorphCube | undefined;
|
|
95
|
+
export declare const MORPHING_VERSION = "0.1.5";
|
|
96
|
+
export declare function isMorph(x: any): x is morph;
|
|
97
|
+
export declare function clearSpace(space: MorphSpace): void;
|
|
98
|
+
export declare function clearAllSpaces(): void;
|
package/dist/morph.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
export var MorphKind;
|
|
2
|
+
(function (MorphKind) {
|
|
3
|
+
MorphKind[MorphKind["Int24"] = 0] = "Int24";
|
|
4
|
+
MorphKind[MorphKind["FloatQ"] = 1] = "FloatQ";
|
|
5
|
+
MorphKind[MorphKind["Flag"] = 2] = "Flag";
|
|
6
|
+
MorphKind[MorphKind["Ref"] = 3] = "Ref";
|
|
7
|
+
})(MorphKind || (MorphKind = {}));
|
|
8
|
+
export function makeMorph(kind, subtype, data24) {
|
|
9
|
+
if (subtype < 0 || subtype > 63) {
|
|
10
|
+
throw new RangeError("subtype must be between 0 and 63");
|
|
11
|
+
}
|
|
12
|
+
const tt = ((kind & 0b11) << 6) | (subtype & 63);
|
|
13
|
+
const v = data24 & 0xFFFFFF;
|
|
14
|
+
return {
|
|
15
|
+
tt,
|
|
16
|
+
d1: (v >> 16) & 0xFF,
|
|
17
|
+
d2: (v >> 8) & 0xFF,
|
|
18
|
+
d3: v & 0xFF,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function kindOf(m) {
|
|
22
|
+
return (m.tt >> 6);
|
|
23
|
+
}
|
|
24
|
+
export function subtypeOf(m) {
|
|
25
|
+
return m.tt & 63;
|
|
26
|
+
}
|
|
27
|
+
export function data24Of(m) {
|
|
28
|
+
return (m.d1 << 16) | (m.d2 << 8) | m.d3;
|
|
29
|
+
}
|
|
30
|
+
/** Int24 helper */
|
|
31
|
+
export function fromInt24(value, subtype = 0) {
|
|
32
|
+
return makeMorph(MorphKind.Int24, subtype, value);
|
|
33
|
+
}
|
|
34
|
+
export function toInt24(m) {
|
|
35
|
+
return data24Of(m);
|
|
36
|
+
}
|
|
37
|
+
/** Signed Int24 helper */
|
|
38
|
+
export function fromSignedInt24(value, subtype = 0) {
|
|
39
|
+
if (!Number.isInteger(value) || value < -0x800000 || value > 0x7FFFFF) {
|
|
40
|
+
throw new RangeError("signed Int24 value must be between -8388608 and 8388607");
|
|
41
|
+
}
|
|
42
|
+
const encoded = value < 0 ? 0x1000000 + value : value;
|
|
43
|
+
return makeMorph(MorphKind.Int24, subtype, encoded);
|
|
44
|
+
}
|
|
45
|
+
export function toSignedInt24(m) {
|
|
46
|
+
const raw = data24Of(m) & 0xFFFFFF;
|
|
47
|
+
return (raw & 0x800000) !== 0 ? raw - 0x1000000 : raw;
|
|
48
|
+
}
|
|
49
|
+
/** Bool / flag helper */
|
|
50
|
+
export function fromFlag(flag, subtype = 0) {
|
|
51
|
+
const data = flag ? 1 : 0;
|
|
52
|
+
return makeMorph(MorphKind.Flag, subtype, data);
|
|
53
|
+
}
|
|
54
|
+
export function toFlag(m) {
|
|
55
|
+
return data24Of(m) !== 0;
|
|
56
|
+
}
|
|
57
|
+
/** RGBA <-> morph mapping (pour OC8, PNG, etc.) */
|
|
58
|
+
export function morphToRgba(m) {
|
|
59
|
+
return [m.tt & 0xFF, m.d1, m.d2, m.d3];
|
|
60
|
+
}
|
|
61
|
+
export function rgbaToMorph(rgba) {
|
|
62
|
+
const [r, g, b, a] = rgba;
|
|
63
|
+
return { tt: r & 0xFF, d1: g & 0xFF, d2: b & 0xFF, d3: a & 0xFF };
|
|
64
|
+
}
|
|
65
|
+
/** Pack a morph array into a flat RGBA byte buffer */
|
|
66
|
+
export function morphsToRgbaBytes(values) {
|
|
67
|
+
const out = new Uint8Array(values.length * 4);
|
|
68
|
+
for (let i = 0; i < values.length; i++) {
|
|
69
|
+
const offset = i * 4;
|
|
70
|
+
const m = values[i];
|
|
71
|
+
out[offset] = m.tt & 0xFF;
|
|
72
|
+
out[offset + 1] = m.d1 & 0xFF;
|
|
73
|
+
out[offset + 2] = m.d2 & 0xFF;
|
|
74
|
+
out[offset + 3] = m.d3 & 0xFF;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
export function rgbaBytesToMorphs(bytes) {
|
|
79
|
+
if ((bytes.length % 4) !== 0) {
|
|
80
|
+
throw new RangeError("RGBA byte buffer length must be a multiple of 4");
|
|
81
|
+
}
|
|
82
|
+
const out = [];
|
|
83
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
84
|
+
out.push({
|
|
85
|
+
tt: Number(bytes[i]) & 0xFF,
|
|
86
|
+
d1: Number(bytes[i + 1]) & 0xFF,
|
|
87
|
+
d2: Number(bytes[i + 2]) & 0xFF,
|
|
88
|
+
d3: Number(bytes[i + 3]) & 0xFF
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
/** Encode en Uint32 (petit utilitaire pratique) */
|
|
94
|
+
export function morphToUint32(m) {
|
|
95
|
+
return ((m.tt & 0xFF) << 24) |
|
|
96
|
+
((m.d1 & 0xFF) << 16) |
|
|
97
|
+
((m.d2 & 0xFF) << 8) |
|
|
98
|
+
(m.d3 & 0xFF);
|
|
99
|
+
}
|
|
100
|
+
export function uint32ToMorph(v) {
|
|
101
|
+
return {
|
|
102
|
+
tt: (v >> 24) & 0xFF,
|
|
103
|
+
d1: (v >> 16) & 0xFF,
|
|
104
|
+
d2: (v >> 8) & 0xFF,
|
|
105
|
+
d3: v & 0xFF,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// 8 espaces max (0–7)
|
|
109
|
+
export const MorphRefSpaces = [
|
|
110
|
+
[], // Global
|
|
111
|
+
[], // QFlush
|
|
112
|
+
[], // Spyder
|
|
113
|
+
[], // A11
|
|
114
|
+
[], // Nossen
|
|
115
|
+
[], // 5
|
|
116
|
+
[], // 6
|
|
117
|
+
[], // Enigma
|
|
118
|
+
];
|
|
119
|
+
function makeSubtype(space, localTag) {
|
|
120
|
+
if (localTag < 0 || localTag > 0b000111) {
|
|
121
|
+
throw new RangeError("localTag must be between 0 and 7");
|
|
122
|
+
}
|
|
123
|
+
return ((space & 0b00000111) << 3) | (localTag & 0b00000111);
|
|
124
|
+
}
|
|
125
|
+
export function subtypeToSpace(subtype) {
|
|
126
|
+
return ((subtype >> 3) & 0b00000111);
|
|
127
|
+
}
|
|
128
|
+
export function subtypeToLocalTag(subtype) {
|
|
129
|
+
return subtype & 0b00000111;
|
|
130
|
+
}
|
|
131
|
+
// --- Références puissantes ---
|
|
132
|
+
/** Crée un morph REF dans un espace donné */
|
|
133
|
+
export function toRef(value, space = 0 /* MorphSpace.Global */, localTag = 0) {
|
|
134
|
+
const spaceTable = getSpaceTable(space);
|
|
135
|
+
const index = spaceTable.push(value) - 1;
|
|
136
|
+
const subtype = makeSubtype(space, localTag);
|
|
137
|
+
return makeMorph(MorphKind.Ref, subtype, index);
|
|
138
|
+
}
|
|
139
|
+
/** Récupère la valeur référencée par un morph REF */
|
|
140
|
+
export function deref(m) {
|
|
141
|
+
if (kindOf(m) !== MorphKind.Ref)
|
|
142
|
+
return undefined;
|
|
143
|
+
const space = subtypeToSpace(subtypeOf(m));
|
|
144
|
+
const index = data24Of(m);
|
|
145
|
+
const table = getSpaceTable(space);
|
|
146
|
+
return table[index];
|
|
147
|
+
}
|
|
148
|
+
/** Clone bit à bit un morph (utile pour alias) */
|
|
149
|
+
export function cloneMorph(m) {
|
|
150
|
+
return { tt: m.tt, d1: m.d1, d2: m.d2, d3: m.d3 };
|
|
151
|
+
}
|
|
152
|
+
/** Alias : m2 devient une copie du "shape" de m1 */
|
|
153
|
+
export function aliasMorph(target, source) {
|
|
154
|
+
target.tt = source.tt;
|
|
155
|
+
target.d1 = source.d1;
|
|
156
|
+
target.d2 = source.d2;
|
|
157
|
+
target.d3 = source.d3;
|
|
158
|
+
return target;
|
|
159
|
+
}
|
|
160
|
+
function axisToByte(axis) {
|
|
161
|
+
if (typeof axis === "number" && Number.isFinite(axis)) {
|
|
162
|
+
return Math.max(0, Math.min(255, Math.trunc(axis)));
|
|
163
|
+
}
|
|
164
|
+
const text = String(axis ?? "");
|
|
165
|
+
let hash = 2166136261;
|
|
166
|
+
for (let i = 0; i < text.length; i++) {
|
|
167
|
+
hash ^= text.charCodeAt(i);
|
|
168
|
+
hash = Math.imul(hash, 16777619);
|
|
169
|
+
}
|
|
170
|
+
return (hash >>> 0) & 0xFF;
|
|
171
|
+
}
|
|
172
|
+
/** Lightweight visual projection for Freeland Bros / RGBA diagnostics */
|
|
173
|
+
export function cubeToRgba(cube) {
|
|
174
|
+
return [
|
|
175
|
+
axisToByte(cube.r),
|
|
176
|
+
axisToByte(cube.g),
|
|
177
|
+
axisToByte(cube.b),
|
|
178
|
+
axisToByte(cube.a)
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
export function rgbaToCube(rgba) {
|
|
182
|
+
return { r: rgba[0], g: rgba[1], b: rgba[2], a: rgba[3] };
|
|
183
|
+
}
|
|
184
|
+
// --- Compatibilité JSON ---
|
|
185
|
+
/** Enigma JSON : stocke la STRING JSON dans une ref et retourne un morph REF */
|
|
186
|
+
export function fromJson(value, space = 0 /* MorphSpace.Global */) {
|
|
187
|
+
const json = JSON.stringify(value);
|
|
188
|
+
return toRef(json, space, /* localTag = */ 1);
|
|
189
|
+
}
|
|
190
|
+
/** Essaie de décoder un morph REF en JSON */
|
|
191
|
+
export function toJson(m) {
|
|
192
|
+
const v = deref(m);
|
|
193
|
+
if (typeof v !== "string")
|
|
194
|
+
return undefined;
|
|
195
|
+
try {
|
|
196
|
+
return JSON.parse(v);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// --- Mode Enigma ---
|
|
203
|
+
/** Enigma : stocke une valeur "secrète" dans l'espace Enigma */
|
|
204
|
+
export function enigma(value, localTag = 0) {
|
|
205
|
+
return toRef(value, 7 /* MorphSpace.Enigma */, localTag);
|
|
206
|
+
}
|
|
207
|
+
/** Enigma : récupère une valeur dans l'espace Enigma */
|
|
208
|
+
export function unveil(m) {
|
|
209
|
+
if (subtypeToSpace(subtypeOf(m)) !== 7 /* MorphSpace.Enigma */)
|
|
210
|
+
return undefined;
|
|
211
|
+
return deref(m);
|
|
212
|
+
}
|
|
213
|
+
/** Crée un Porygon à partir d'un objet JSON (1 shard, layoutId=0) */
|
|
214
|
+
export function buildPorygonFromJson(value, layoutId = 0) {
|
|
215
|
+
const m = fromJson(value, 7 /* MorphSpace.Enigma */);
|
|
216
|
+
return {
|
|
217
|
+
layoutId,
|
|
218
|
+
shards: [{ id: 0, morph: m }],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/** Résout un Porygon (simple : 1 shard JSON Enigma) */
|
|
222
|
+
export function resolvePorygon(p) {
|
|
223
|
+
if (!p.shards.length)
|
|
224
|
+
return undefined;
|
|
225
|
+
const shard = p.shards[0];
|
|
226
|
+
return toJson(shard.morph);
|
|
227
|
+
}
|
|
228
|
+
/** Cube helpers for higher-level Freeland / RGBA tooling */
|
|
229
|
+
export function cubeToRef(cube, space = 0 /* MorphSpace.Global */, localTag = 2) {
|
|
230
|
+
return toRef(cube, space, localTag);
|
|
231
|
+
}
|
|
232
|
+
export function refToCube(m) {
|
|
233
|
+
const value = deref(m);
|
|
234
|
+
if (value &&
|
|
235
|
+
typeof value === "object" &&
|
|
236
|
+
"r" in value &&
|
|
237
|
+
"g" in value &&
|
|
238
|
+
"b" in value &&
|
|
239
|
+
"a" in value) {
|
|
240
|
+
return value;
|
|
241
|
+
}
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
// --- Version ---
|
|
245
|
+
export const MORPHING_VERSION = "0.1.5";
|
|
246
|
+
// --- Type guard ---
|
|
247
|
+
export function isMorph(x) {
|
|
248
|
+
return (x != null &&
|
|
249
|
+
typeof x === "object" &&
|
|
250
|
+
typeof x.tt === "number" &&
|
|
251
|
+
typeof x.d1 === "number" &&
|
|
252
|
+
typeof x.d2 === "number" &&
|
|
253
|
+
typeof x.d3 === "number");
|
|
254
|
+
}
|
|
255
|
+
// --- Ref space safety ---
|
|
256
|
+
function getSpaceTable(space) {
|
|
257
|
+
const idx = space & 0b00000111;
|
|
258
|
+
return MorphRefSpaces[idx] ?? MorphRefSpaces[0];
|
|
259
|
+
}
|
|
260
|
+
export function clearSpace(space) {
|
|
261
|
+
const table = getSpaceTable(space);
|
|
262
|
+
table.length = 0;
|
|
263
|
+
}
|
|
264
|
+
export function clearAllSpaces() {
|
|
265
|
+
for (let i = 0; i < MorphRefSpaces.length; i++) {
|
|
266
|
+
MorphRefSpaces[i].length = 0;
|
|
267
|
+
}
|
|
268
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nossen/morphing",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Compact 4-byte value shapes and RGBA/cube helpers for low-friction transport.",
|
|
5
|
+
"author": "NOSSEN <contact@funesterie.me>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/morph.js",
|
|
9
|
+
"types": "dist/morph.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/morph.js",
|
|
13
|
+
"types": "./dist/morph.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"dev": "tsc -w",
|
|
20
|
+
"prepare": "npm run build",
|
|
21
|
+
"test": "node test/smoke.mjs"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/Funesterie/funesterie.git",
|
|
31
|
+
"directory": "runtime/modules/morphing"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/Funesterie/funesterie/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/Funesterie/funesterie/tree/master/runtime/modules/morphing#readme",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"morphing",
|
|
39
|
+
"nossen",
|
|
40
|
+
"funesterie",
|
|
41
|
+
"a11",
|
|
42
|
+
"morph",
|
|
43
|
+
"qflush",
|
|
44
|
+
"spyder",
|
|
45
|
+
"oc8",
|
|
46
|
+
"rgba",
|
|
47
|
+
"binary",
|
|
48
|
+
"encoding"
|
|
49
|
+
],
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^24.10.1",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|