@pieai/swimmer-avatar-kit 0.2.0 → 0.4.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 +24 -1
- package/dist/avatar.js +3 -2
- package/dist/avatar.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/kindergrimm-quality.d.ts +2 -0
- package/dist/kindergrimm-quality.js +28 -0
- package/dist/kindergrimm-quality.js.map +1 -0
- package/dist/portrait.d.ts +14 -0
- package/dist/portrait.js +167 -0
- package/dist/portrait.js.map +1 -0
- package/dist/react-three-fiber.d.ts +3 -1
- package/dist/react-three-fiber.js +3 -2
- package/dist/react-three-fiber.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/package.json +6 -6
- package/UPSTREAM.md +0 -47
- package/upstream-lock.json +0 -38
package/README.md
CHANGED
|
@@ -20,6 +20,29 @@ React Three Fiber consumers also install compatible React and R3F versions:
|
|
|
20
20
|
pnpm add react @react-three/fiber
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Compact portraits
|
|
24
|
+
|
|
25
|
+
Use the same avatar identity in DOM UI without opening one WebGL context per
|
|
26
|
+
chip. The pure portrait helpers complete the recipe and preserve its palette,
|
|
27
|
+
species, body silhouette, eyes, mouth, facial mark, and dressed state.
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import {
|
|
31
|
+
createAvatarPortraitDataUrl,
|
|
32
|
+
randomRecipe,
|
|
33
|
+
} from "@pieai/swimmer-avatar-kit";
|
|
34
|
+
|
|
35
|
+
const recipe = randomRecipe("user-42");
|
|
36
|
+
const src = createAvatarPortraitDataUrl(recipe, {
|
|
37
|
+
size: 96,
|
|
38
|
+
title: "Ada's avatar",
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use the returned URL in `<img src>`, CSS, canvas, or a native bridge. Use
|
|
43
|
+
`createAvatarPortraitSvg` when the caller needs the raw SVG. Both functions are
|
|
44
|
+
framework-neutral and deterministic; the host still owns storage and alt text.
|
|
45
|
+
|
|
23
46
|
## React Three Fiber
|
|
24
47
|
|
|
25
48
|
```tsx
|
|
@@ -104,7 +127,7 @@ identity policy.
|
|
|
104
127
|
|
|
105
128
|
| Entry point | Purpose | Runtime |
|
|
106
129
|
| --- | --- | --- |
|
|
107
|
-
| `@pieai/swimmer-avatar-kit` | Recipes,
|
|
130
|
+
| `@pieai/swimmer-avatar-kit` | Recipes, compact portraits, build/update/dispose | Pure SVG + Three.js |
|
|
108
131
|
| `@pieai/swimmer-avatar-kit/materials` | Gloss materials and studio environment | Browser/WebGL |
|
|
109
132
|
| `@pieai/swimmer-avatar-kit/react-three-fiber` | `<Avatar />` adapter | React + R3F |
|
|
110
133
|
|
package/dist/avatar.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createGlossFace } from "../vendor/kindergrimm/src/gloss/gface.js";
|
|
2
2
|
import { buildGloss } from "../vendor/kindergrimm/src/gloss/grig.js";
|
|
3
3
|
import { completeRecipe, EXPRESSIONS } from "./recipe.js";
|
|
4
|
+
import { withAvatarQuality } from "./kindergrimm-quality.js";
|
|
4
5
|
const expressionIds = new Set(EXPRESSIONS);
|
|
5
6
|
function disposeGeometries(root) {
|
|
6
7
|
const geometries = new Set();
|
|
@@ -17,9 +18,9 @@ export function buildAvatar(recipe, options) {
|
|
|
17
18
|
throw new TypeError("buildAvatar requires a materialFor function.");
|
|
18
19
|
}
|
|
19
20
|
const completedRecipe = completeRecipe(recipe);
|
|
20
|
-
const built = buildGloss(completedRecipe, {
|
|
21
|
+
const built = withAvatarQuality(options.quality, () => buildGloss(completedRecipe, {
|
|
21
22
|
materialFor: options.materialFor,
|
|
22
|
-
});
|
|
23
|
+
}));
|
|
23
24
|
const life = createGlossFace(built, { gaze: options.gaze ?? true });
|
|
24
25
|
const restY = Number(built.head.userData.restY ?? built.L.cy);
|
|
25
26
|
let elapsedSeconds = 0;
|
package/dist/avatar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.js","sourceRoot":"","sources":["../src/avatar.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAgB,MAAM,yCAAyC,CAAC;AAEnF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"avatar.js","sourceRoot":"","sources":["../src/avatar.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { buildAvatar } from "./avatar.js";
|
|
2
|
+
export { createAvatarPortraitDataUrl, createAvatarPortraitSvg } from "./portrait.js";
|
|
3
|
+
export type { AvatarPortraitOptions } from "./portrait.js";
|
|
2
4
|
export { assertAvatarRecipe, AVATAR_RECIPE_SCHEMA, AVATAR_RECIPE_VERSION, cloneAvatarRecipe, completeRecipe, createAvatarRecipe, createAvatarRecipeDocument, deserializeAvatarRecipe, EXPRESSIONS, fillRecipe, PALETTES, parseAvatarRecipeDocument, PARTS, randomRecipe, rerollPart, serializeAvatarRecipe, SPECIES, } from "./recipe.js";
|
|
3
|
-
export type { AvatarBounds, AvatarBuildOptions, AvatarBuildStats, AvatarExpression, AvatarHandle, AvatarMaterialFor, AvatarOption, AvatarPaletteOption, AvatarPartRecipe, AvatarRecipe, AvatarRecipeDocument, AvatarRecipeDocumentV1, AvatarSeed, } from "./types.js";
|
|
5
|
+
export type { AvatarBounds, AvatarBuildOptions, AvatarBuildStats, AvatarExpression, AvatarHandle, AvatarMaterialFor, AvatarOption, AvatarPaletteOption, AvatarPartRecipe, AvatarRecipe, AvatarRecipeDocument, AvatarRecipeDocumentV1, AvatarQuality, AvatarSeed, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { buildAvatar } from "./avatar.js";
|
|
2
|
+
export { createAvatarPortraitDataUrl, createAvatarPortraitSvg } from "./portrait.js";
|
|
2
3
|
export { assertAvatarRecipe, AVATAR_RECIPE_SCHEMA, AVATAR_RECIPE_VERSION, cloneAvatarRecipe, completeRecipe, createAvatarRecipe, createAvatarRecipeDocument, deserializeAvatarRecipe, EXPRESSIONS, fillRecipe, PALETTES, parseAvatarRecipeDocument, PARTS, randomRecipe, rerollPart, serializeAvatarRecipe, SPECIES, } from "./recipe.js";
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,yBAAyB,EACzB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,OAAO,GACR,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAErF,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,yBAAyB,EACzB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,OAAO,GACR,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Kindergrimm's runtime is intentionally vendored as JavaScript. Keep its
|
|
2
|
+
// untyped quality hook behind this small, typed adapter rather than leaking a
|
|
3
|
+
// vendor module into the public API or changing the vendor for compatibility.
|
|
4
|
+
// @ts-expect-error The imported donor module has no declaration file by design.
|
|
5
|
+
import * as vendorShape from "../vendor/kindergrimm/src/gloss/gshape.js";
|
|
6
|
+
const shapeQuality = vendorShape;
|
|
7
|
+
const DETAIL_BY_QUALITY = {
|
|
8
|
+
full: 1,
|
|
9
|
+
compact: 0.2,
|
|
10
|
+
};
|
|
11
|
+
function detailFor(quality) {
|
|
12
|
+
if (quality === undefined)
|
|
13
|
+
return DETAIL_BY_QUALITY.full;
|
|
14
|
+
if (Object.hasOwn(DETAIL_BY_QUALITY, quality))
|
|
15
|
+
return DETAIL_BY_QUALITY[quality];
|
|
16
|
+
throw new RangeError(`Unknown avatar quality: ${String(quality)}`);
|
|
17
|
+
}
|
|
18
|
+
export function withAvatarQuality(quality, build) {
|
|
19
|
+
const previousDetail = shapeQuality.glossDetail();
|
|
20
|
+
shapeQuality.setGlossDetail(detailFor(quality));
|
|
21
|
+
try {
|
|
22
|
+
return build();
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
shapeQuality.setGlossDetail(previousDetail);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=kindergrimm-quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kindergrimm-quality.js","sourceRoot":"","sources":["../src/kindergrimm-quality.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,8EAA8E;AAC9E,8EAA8E;AAC9E,gFAAgF;AAChF,OAAO,KAAK,WAAW,MAAM,2CAA2C,CAAC;AAOzE,MAAM,YAAY,GAAG,WAA+C,CAAC;AACrE,MAAM,iBAAiB,GAAkC;IACvD,IAAI,EAAE,CAAC;IACP,OAAO,EAAE,GAAG;CACb,CAAC;AAEF,SAAS,SAAS,CAAC,OAAkC;IACnD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC;IACzD,IAAI,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC;QAAE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACjF,MAAM,IAAI,UAAU,CAAC,2BAA2B,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAI,OAAkC,EAAE,KAAc;IACrF,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IAClD,YAAY,CAAC,cAAc,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,OAAO,KAAK,EAAE,CAAC;IACjB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AvatarRecipe } from "./types.js";
|
|
2
|
+
export interface AvatarPortraitOptions {
|
|
3
|
+
/** Raster display size. The SVG keeps a 100 × 100 viewBox. */
|
|
4
|
+
size?: number;
|
|
5
|
+
/** Optional accessible name embedded as an SVG title. */
|
|
6
|
+
title?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Render a deterministic compact portrait from the same completed recipe as
|
|
10
|
+
* the 3D avatar. The result is pure SVG: no DOM, React, canvas, or WebGL.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createAvatarPortraitSvg(input: AvatarRecipe, options?: AvatarPortraitOptions): string;
|
|
13
|
+
/** A browser-ready data URL for `<img src>`, CSS, canvas, or native bridges. */
|
|
14
|
+
export declare function createAvatarPortraitDataUrl(recipe: AvatarRecipe, options?: AvatarPortraitOptions): string;
|
package/dist/portrait.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { completeRecipe, PALETTES } from "./recipe.js";
|
|
2
|
+
const DEFAULT_SIZE = 96;
|
|
3
|
+
const MIN_SIZE = 16;
|
|
4
|
+
const MAX_SIZE = 1024;
|
|
5
|
+
const INK = "#20312d";
|
|
6
|
+
const PAPER = "#fff8e8";
|
|
7
|
+
function escapeXml(value) {
|
|
8
|
+
return value.replace(/[&<>"']/g, (character) => {
|
|
9
|
+
if (character === "&")
|
|
10
|
+
return "&";
|
|
11
|
+
if (character === "<")
|
|
12
|
+
return "<";
|
|
13
|
+
if (character === ">")
|
|
14
|
+
return ">";
|
|
15
|
+
if (character === '"')
|
|
16
|
+
return """;
|
|
17
|
+
return "'";
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function paramsFor(recipe, partId) {
|
|
21
|
+
const value = recipe.parts[partId]?.params;
|
|
22
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
23
|
+
? value
|
|
24
|
+
: {};
|
|
25
|
+
}
|
|
26
|
+
function stringParam(params, key, fallback) {
|
|
27
|
+
return typeof params[key] === "string" ? params[key] : fallback;
|
|
28
|
+
}
|
|
29
|
+
function booleanParam(params, key) {
|
|
30
|
+
return params[key] === true;
|
|
31
|
+
}
|
|
32
|
+
function numberParam(params, key, fallback) {
|
|
33
|
+
const value = params[key];
|
|
34
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
35
|
+
}
|
|
36
|
+
function portraitSize(size) {
|
|
37
|
+
if (size === undefined)
|
|
38
|
+
return DEFAULT_SIZE;
|
|
39
|
+
if (!Number.isFinite(size))
|
|
40
|
+
throw new TypeError("Avatar portrait size must be finite.");
|
|
41
|
+
return Math.round(Math.min(MAX_SIZE, Math.max(MIN_SIZE, size)));
|
|
42
|
+
}
|
|
43
|
+
function paletteColors(recipe) {
|
|
44
|
+
const palette = PALETTES.find((candidate) => candidate.id === recipe.palette) ?? PALETTES[0];
|
|
45
|
+
if (!palette)
|
|
46
|
+
return ["#cfc6d9", "#7b7bb0", "#f5c48a"];
|
|
47
|
+
const length = Math.max(1, palette.colors.length);
|
|
48
|
+
const index = Math.min(recipe.colorIx ?? 0, length - 1);
|
|
49
|
+
const primary = palette.colors[index] ?? palette.colors[0] ?? "#cfc6d9";
|
|
50
|
+
return [
|
|
51
|
+
primary,
|
|
52
|
+
palette.colors[(index + 1) % length] ?? primary,
|
|
53
|
+
palette.colors[(index + 2) % length] ?? primary,
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
function crestMarkup(species, accent, secondary) {
|
|
57
|
+
if (species === "cat") {
|
|
58
|
+
return `<path d="M22 32 28 6 44 29ZM56 29 72 6 78 32Z" fill="${accent}" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/><path d="m28 15 4 13h7zm44 0-4 13h-7z" fill="${secondary}"/>`;
|
|
59
|
+
}
|
|
60
|
+
if (species === "bunny") {
|
|
61
|
+
return `<rect x="25" y="2" width="17" height="39" rx="9" fill="${accent}" stroke="${INK}" stroke-width="4" transform="rotate(-7 33.5 21.5)"/><rect x="58" y="2" width="17" height="39" rx="9" fill="${accent}" stroke="${INK}" stroke-width="4" transform="rotate(7 66.5 21.5)"/><path d="M33 10v23M67 10v23" stroke="${secondary}" stroke-width="5" stroke-linecap="round"/>`;
|
|
62
|
+
}
|
|
63
|
+
if (species === "bear") {
|
|
64
|
+
return `<circle cx="25" cy="25" r="14" fill="${accent}" stroke="${INK}" stroke-width="4"/><circle cx="75" cy="25" r="14" fill="${accent}" stroke="${INK}" stroke-width="4"/><circle cx="25" cy="25" r="6" fill="${secondary}"/><circle cx="75" cy="25" r="6" fill="${secondary}"/>`;
|
|
65
|
+
}
|
|
66
|
+
if (species === "robot") {
|
|
67
|
+
return `<path d="M50 23V10" stroke="${INK}" stroke-width="5" stroke-linecap="round"/><circle cx="50" cy="8" r="6" fill="${secondary}" stroke="${INK}" stroke-width="3"/><rect x="22" y="17" width="13" height="15" rx="5" fill="${accent}" stroke="${INK}" stroke-width="4"/><rect x="65" y="17" width="13" height="15" rx="5" fill="${accent}" stroke="${INK}" stroke-width="4"/>`;
|
|
68
|
+
}
|
|
69
|
+
if (species === "monster") {
|
|
70
|
+
return `<path d="M25 31 17 8l23 19m35 4 8-23-23 19" fill="${secondary}" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/>`;
|
|
71
|
+
}
|
|
72
|
+
if (species === "slime") {
|
|
73
|
+
return `<path d="M24 32Q28 8 39 27 50 3 61 27 72 8 76 32Z" fill="${accent}" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/>`;
|
|
74
|
+
}
|
|
75
|
+
if (species === "rock") {
|
|
76
|
+
return `<path d="m30 31 8-21 12 13 11-16 10 24Z" fill="${accent}" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/>`;
|
|
77
|
+
}
|
|
78
|
+
if (species === "humanoid") {
|
|
79
|
+
return `<path d="M21 35Q25 7 50 8t29 27Q67 23 50 28 35 20 21 35Z" fill="${accent}" stroke="${INK}" stroke-width="4"/>`;
|
|
80
|
+
}
|
|
81
|
+
return `<path d="m50 5 7 15 17 2-12 12 3 17-15-8-15 8 3-17-12-12 17-2Z" fill="${secondary}" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/>`;
|
|
82
|
+
}
|
|
83
|
+
function headMarkup(body) {
|
|
84
|
+
if (body === "sphere") {
|
|
85
|
+
return `<ellipse cx="50" cy="52" rx="36" ry="33" fill="url(#avatar-head)" stroke="${INK}" stroke-width="4"/>`;
|
|
86
|
+
}
|
|
87
|
+
if (body === "rock") {
|
|
88
|
+
return `<path d="M18 35 31 19l26-5 24 17 5 27-15 25-34 3-23-20Z" fill="url(#avatar-head)" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/>`;
|
|
89
|
+
}
|
|
90
|
+
if (body === "drop") {
|
|
91
|
+
return `<path d="M50 13C42 27 16 46 16 63c0 18 14 27 34 27s34-9 34-27C84 46 58 27 50 13Z" fill="url(#avatar-head)" stroke="${INK}" stroke-width="4"/>`;
|
|
92
|
+
}
|
|
93
|
+
return `<rect x="14" y="20" width="72" height="67" rx="18" fill="url(#avatar-head)" stroke="${INK}" stroke-width="4"/>`;
|
|
94
|
+
}
|
|
95
|
+
function eyeMarkup(style, wink, secondary) {
|
|
96
|
+
const left = wink
|
|
97
|
+
? `<path d="M29 50q7 7 14 0" fill="none" stroke="${INK}" stroke-width="5" stroke-linecap="round"/>`
|
|
98
|
+
: eyeShape(style, 36, secondary);
|
|
99
|
+
return `${left}${eyeShape(style, 64, secondary)}`;
|
|
100
|
+
}
|
|
101
|
+
function eyeShape(style, x, secondary) {
|
|
102
|
+
if (style === "happy") {
|
|
103
|
+
return `<path d="M${x - 7} 52q7-8 14 0" fill="none" stroke="${INK}" stroke-width="5" stroke-linecap="round"/>`;
|
|
104
|
+
}
|
|
105
|
+
if (style === "sleepy") {
|
|
106
|
+
return `<path d="M${x - 8} 51q8 5 16 0" fill="none" stroke="${INK}" stroke-width="5" stroke-linecap="round"/>`;
|
|
107
|
+
}
|
|
108
|
+
if (style === "box" || style === "slab") {
|
|
109
|
+
return `<rect x="${x - 8}" y="43" width="16" height="14" rx="4" fill="${INK}"/><rect x="${x - 3}" y="46" width="6" height="5" rx="2" fill="${secondary}"/>`;
|
|
110
|
+
}
|
|
111
|
+
return `<ellipse cx="${x}" cy="50" rx="8" ry="10" fill="${INK}"/><circle cx="${x + 2}" cy="47" r="3" fill="${PAPER}"/>`;
|
|
112
|
+
}
|
|
113
|
+
function mouthMarkup(style, secondary) {
|
|
114
|
+
if (style === "flat") {
|
|
115
|
+
return `<path d="M42 69h16" stroke="${INK}" stroke-width="5" stroke-linecap="round"/>`;
|
|
116
|
+
}
|
|
117
|
+
if (style === "zig") {
|
|
118
|
+
return `<path d="m38 68 7-5 6 8 6-8 6 5" fill="none" stroke="${INK}" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`;
|
|
119
|
+
}
|
|
120
|
+
if (style === "cat") {
|
|
121
|
+
return `<path d="M50 64v4m0 0q-8 8-13 0m13 0q8 8 13 0" fill="none" stroke="${INK}" stroke-width="4" stroke-linecap="round"/>`;
|
|
122
|
+
}
|
|
123
|
+
if (style === "grin") {
|
|
124
|
+
return `<path d="M36 64q14 18 28 0Z" fill="${PAPER}" stroke="${INK}" stroke-width="4" stroke-linejoin="round"/><path d="M43 67v6m7-7v10m7-9v6" stroke="${secondary}" stroke-width="2"/>`;
|
|
125
|
+
}
|
|
126
|
+
return `<path d="M38 65q12 13 24 0" fill="none" stroke="${INK}" stroke-width="5" stroke-linecap="round"/>`;
|
|
127
|
+
}
|
|
128
|
+
function markMarkup(style, side, secondary) {
|
|
129
|
+
const x = side < 0 ? 28 : 72;
|
|
130
|
+
if (style === "spot") {
|
|
131
|
+
return `<circle cx="${x}" cy="35" r="9" fill="${secondary}" opacity=".72"/>`;
|
|
132
|
+
}
|
|
133
|
+
if (style === "scar") {
|
|
134
|
+
return `<path d="m${x - 5} 35 10 16m-9-10 7-4m-4 10 7-4" stroke="${secondary}" stroke-width="3" stroke-linecap="round"/>`;
|
|
135
|
+
}
|
|
136
|
+
if (style === "sutures") {
|
|
137
|
+
return `<path d="m${x - 7} 35 14 17m-11-12 7-4m-3 9 7-4m-3 9 6-3" stroke="${secondary}" stroke-width="3" stroke-linecap="round"/>`;
|
|
138
|
+
}
|
|
139
|
+
return "";
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Render a deterministic compact portrait from the same completed recipe as
|
|
143
|
+
* the 3D avatar. The result is pure SVG: no DOM, React, canvas, or WebGL.
|
|
144
|
+
*/
|
|
145
|
+
export function createAvatarPortraitSvg(input, options = {}) {
|
|
146
|
+
const recipe = completeRecipe(input);
|
|
147
|
+
const size = portraitSize(options.size);
|
|
148
|
+
const species = recipe.species ?? "wildcard";
|
|
149
|
+
const body = recipe.body ?? "cube";
|
|
150
|
+
const [primary, accent, secondary] = paletteColors(recipe);
|
|
151
|
+
const eyeParams = paramsFor(recipe, "eyes");
|
|
152
|
+
const mouthParams = paramsFor(recipe, "mouth");
|
|
153
|
+
const markParams = paramsFor(recipe, "mark");
|
|
154
|
+
const frameParams = paramsFor(recipe, "frame");
|
|
155
|
+
const eyeStyle = stringParam(eyeParams, "style", "bead");
|
|
156
|
+
const mouthStyle = stringParam(mouthParams, "style", "smile");
|
|
157
|
+
const markStyle = stringParam(markParams, "style", "none");
|
|
158
|
+
const markSide = numberParam(markParams, "side", 1);
|
|
159
|
+
const dressed = booleanParam(frameParams, "dressed");
|
|
160
|
+
const title = options.title ? `<title>${escapeXml(options.title)}</title>` : "";
|
|
161
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 100 100" role="img" data-avatar-seed="${recipe.seed}" data-avatar-species="${escapeXml(species)}" data-avatar-palette="${escapeXml(recipe.palette ?? "unknown")}">${title}<defs><linearGradient id="avatar-bg" x1="0" y1="0" x2="1" y2="1"><stop stop-color="${secondary}"/><stop offset="1" stop-color="${accent}"/></linearGradient><linearGradient id="avatar-head" x1="0" y1="0" x2=".75" y2="1"><stop stop-color="${PAPER}" stop-opacity=".58"/><stop offset=".18" stop-color="${primary}"/><stop offset="1" stop-color="${accent}"/></linearGradient></defs><rect width="100" height="100" rx="50" fill="url(#avatar-bg)"/><circle cx="50" cy="49" r="44" fill="${PAPER}" opacity=".46"/><ellipse cx="50" cy="88" rx="30" ry="6" fill="${INK}" opacity=".2"/>${crestMarkup(species, accent, secondary)}${dressed ? `<path d="M25 86q25-18 50 0v10H25Z" fill="${secondary}" stroke="${INK}" stroke-width="4"/>` : ""}${headMarkup(body)}${markMarkup(markStyle, markSide, secondary)}${eyeMarkup(eyeStyle, booleanParam(eyeParams, "wink"), secondary)}${mouthMarkup(mouthStyle, secondary)}<path d="M28 31q18-13 39-2" fill="none" stroke="${PAPER}" stroke-width="5" stroke-linecap="round" opacity=".48"/></svg>`;
|
|
162
|
+
}
|
|
163
|
+
/** A browser-ready data URL for `<img src>`, CSS, canvas, or native bridges. */
|
|
164
|
+
export function createAvatarPortraitDataUrl(recipe, options = {}) {
|
|
165
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(createAvatarPortraitSvg(recipe, options))}`;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=portrait.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portrait.js","sourceRoot":"","sources":["../src/portrait.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { type ThreeElements } from "@react-three/fiber";
|
|
2
2
|
import type { Group } from "three";
|
|
3
3
|
import { type AvatarMaterialLibrary } from "./materials.js";
|
|
4
|
-
import type { AvatarHandle, AvatarRecipe } from "./types.js";
|
|
4
|
+
import type { AvatarHandle, AvatarQuality, AvatarRecipe } from "./types.js";
|
|
5
5
|
export type AvatarProps = Omit<ThreeElements["group"], "children" | "ref"> & {
|
|
6
6
|
recipe: AvatarRecipe;
|
|
7
7
|
gaze?: boolean;
|
|
8
|
+
quality?: AvatarQuality;
|
|
8
9
|
materialLibrary?: AvatarMaterialLibrary;
|
|
9
10
|
onBuilt?: (avatar: AvatarHandle) => void;
|
|
10
11
|
};
|
|
11
12
|
export declare const Avatar: import("react").ForwardRefExoticComponent<Omit<import("@react-three/fiber/dist/declarations/src/core/utils.js").Mutable<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Partial<import("@react-three/fiber/dist/declarations/src/core/utils.js").Overwrite<Group<import("three").Object3DEventMap>, import("@react-three/fiber").MathProps<Group<import("three").Object3DEventMap>> & import("@react-three/fiber").ReactProps<Group<import("three").Object3DEventMap>> & Partial<import("@react-three/fiber").EventHandlers>>>, Omit<import("@react-three/fiber").InstanceProps<Group<import("three").Object3DEventMap>, typeof Group>, "object">>>, "children" | "ref"> & {
|
|
12
13
|
recipe: AvatarRecipe;
|
|
13
14
|
gaze?: boolean;
|
|
15
|
+
quality?: AvatarQuality;
|
|
14
16
|
materialLibrary?: AvatarMaterialLibrary;
|
|
15
17
|
onBuilt?: (avatar: AvatarHandle) => void;
|
|
16
18
|
} & import("react").RefAttributes<AvatarHandle>>;
|
|
@@ -3,7 +3,7 @@ import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, us
|
|
|
3
3
|
import { useFrame, useThree } from "@react-three/fiber";
|
|
4
4
|
import { buildAvatar } from "./avatar.js";
|
|
5
5
|
import { acquireStudioMaterialLibrary } from "./materials.js";
|
|
6
|
-
export const Avatar = forwardRef(function Avatar({ recipe, gaze = true, materialLibrary, onBuilt, ...groupProps }, forwardedRef) {
|
|
6
|
+
export const Avatar = forwardRef(function Avatar({ recipe, gaze = true, quality, materialLibrary, onBuilt, ...groupProps }, forwardedRef) {
|
|
7
7
|
const { gl } = useThree();
|
|
8
8
|
const [ownedLibrary, setOwnedLibrary] = useState(null);
|
|
9
9
|
const [avatar, setAvatar] = useState(null);
|
|
@@ -29,6 +29,7 @@ export const Avatar = forwardRef(function Avatar({ recipe, gaze = true, material
|
|
|
29
29
|
return;
|
|
30
30
|
const next = buildAvatar(recipe, {
|
|
31
31
|
gaze,
|
|
32
|
+
...(quality === undefined ? {} : { quality }),
|
|
32
33
|
materialFor: activeLibrary.materialFor,
|
|
33
34
|
});
|
|
34
35
|
avatarRef.current = next;
|
|
@@ -42,7 +43,7 @@ export const Avatar = forwardRef(function Avatar({ recipe, gaze = true, material
|
|
|
42
43
|
setObject(null);
|
|
43
44
|
next.dispose();
|
|
44
45
|
};
|
|
45
|
-
}, [activeLibrary, gaze, recipe]);
|
|
46
|
+
}, [activeLibrary, gaze, quality, recipe]);
|
|
46
47
|
useFrame((_, deltaSeconds) => {
|
|
47
48
|
avatarRef.current?.update(deltaSeconds);
|
|
48
49
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-three-fiber.js","sourceRoot":"","sources":["../src/react-three-fiber.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,eAAe,EACf,MAAM,EACN,QAAQ,GAET,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAsB,MAAM,oBAAoB,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,4BAA4B,EAA8B,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"react-three-fiber.js","sourceRoot":"","sources":["../src/react-three-fiber.tsx"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface AvatarRecipe {
|
|
|
14
14
|
parts: Record<string, AvatarPartRecipe>;
|
|
15
15
|
}
|
|
16
16
|
export type AvatarSeed = number | string;
|
|
17
|
+
/** Geometry density for a rendered avatar; the recipe and silhouette stay the same. */
|
|
18
|
+
export type AvatarQuality = "full" | "compact";
|
|
17
19
|
export type AvatarExpression = "idle" | "happy" | "angry" | "sad" | "surprised";
|
|
18
20
|
export interface AvatarOption {
|
|
19
21
|
id: string;
|
|
@@ -41,6 +43,8 @@ export interface AvatarBounds {
|
|
|
41
43
|
export interface AvatarBuildOptions {
|
|
42
44
|
materialFor: AvatarMaterialFor;
|
|
43
45
|
gaze?: boolean;
|
|
46
|
+
/** Defaults to `full` for backwards-compatible high-detail output. */
|
|
47
|
+
quality?: AvatarQuality;
|
|
44
48
|
}
|
|
45
49
|
export interface AvatarHandle {
|
|
46
50
|
readonly object: Group;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pieai/swimmer-avatar-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Shared procedural 3D avatar recipes, Three.js runtime, and React Three Fiber adapter for PieAI products.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"avatar",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"vendor",
|
|
25
25
|
"README.md",
|
|
26
26
|
"NOTICE.md",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
27
|
+
"donors-individual.md",
|
|
28
|
+
"donors-individual-lock.json",
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"type": "module",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"registry": "https://registry.npmjs.org/"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@pieai/doc-gov": "0.9.
|
|
56
|
-
"@pieai/pro-gov": "0.9.
|
|
55
|
+
"@pieai/doc-gov": "0.9.9",
|
|
56
|
+
"@pieai/pro-gov": "0.9.9",
|
|
57
57
|
"@react-three/fiber": "9.6.1",
|
|
58
58
|
"@types/node": "24.13.2",
|
|
59
59
|
"@types/react": "19.2.17",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"test:package-consumer": "node scripts/test-package-consumer.mjs",
|
|
93
93
|
"host-links:check": "node scripts/verify-host-links.mjs",
|
|
94
94
|
"vendor:verify": "node scripts/verify-vendor.mjs",
|
|
95
|
-
"
|
|
95
|
+
"donors-individual:check": "node scripts/check-donors-individual.mjs",
|
|
96
96
|
"verify": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm vendor:verify && pnpm test && pnpm build",
|
|
97
97
|
"verify:release": "pnpm verify && pnpm docs:check && pnpm test:package-consumer",
|
|
98
98
|
"doc-gov": "doc-gov",
|
package/UPSTREAM.md
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Kindergrimm Upstream Maintenance
|
|
2
|
-
|
|
3
|
-
SwimmerAvatarKit owns the stable product contract; Kindergrimm remains the art
|
|
4
|
-
generator upstream. An upstream change is never merged automatically.
|
|
5
|
-
|
|
6
|
-
## Check for updates
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
pnpm upstream:check
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
The command compares the pinned commit in `upstream-lock.json` with the current
|
|
13
|
-
tip of Kindergrimm's `main` branch. It exits successfully when they match and
|
|
14
|
-
fails with both commit IDs when review is needed. The scheduled GitHub workflow
|
|
15
|
-
runs the same read-only check weekly.
|
|
16
|
-
|
|
17
|
-
## Review and import an update
|
|
18
|
-
|
|
19
|
-
1. Clone Kindergrimm into a temporary directory and check out the candidate
|
|
20
|
-
commit. Do not add it as a mutable Git submodule.
|
|
21
|
-
2. Review the full range from the pinned commit to the candidate, especially
|
|
22
|
-
`src/gloss/**`, `src/rng.js`, recipe shape, material callback arguments,
|
|
23
|
-
returned rig fields, and browser-only API use.
|
|
24
|
-
3. Replace only the imported dependency closure under
|
|
25
|
-
`vendor/kindergrimm/`. Keep `src/gloss/gloss.js` and
|
|
26
|
-
`src/gloss/gcrowd.js` excluded unless the package contract deliberately
|
|
27
|
-
changes.
|
|
28
|
-
4. Do not repair compatibility inside vendored JavaScript. Adapt the typed
|
|
29
|
-
boundary under `src/**` and add regression tests.
|
|
30
|
-
5. Update the commit metadata and Git blob hashes in `upstream-lock.json`, then
|
|
31
|
-
run `pnpm vendor:verify` before any other test.
|
|
32
|
-
6. Run `pnpm verify`, `pnpm docs:check`, and inspect `pnpm pack --dry-run`.
|
|
33
|
-
7. Choose semver from the public package impact, not from the upstream commit
|
|
34
|
-
label. A compatible art/bug refresh is normally a patch; changed recipes or
|
|
35
|
-
APIs may require a minor or major release.
|
|
36
|
-
8. Merge the reviewed package change, then publish only through the trusted
|
|
37
|
-
GitHub Actions workflow. Products upgrade to that released version in their
|
|
38
|
-
own PRs.
|
|
39
|
-
|
|
40
|
-
This creates a deliberate two-step buffer:
|
|
41
|
-
|
|
42
|
-
```text
|
|
43
|
-
Kindergrimm update -> reviewed SwimmerAvatarKit release -> product dependency update
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
An upstream bug fix can therefore be adopted quickly without giving an
|
|
47
|
-
unreviewed external commit authority over every PieAI product.
|
package/upstream-lock.json
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
|
-
"repository": "https://github.com/albertobeiz/kindergrimm.git",
|
|
4
|
-
"defaultBranch": "main",
|
|
5
|
-
"commit": "811214c6dd5de18cc20335cd3d4ab0a06e45ffd4",
|
|
6
|
-
"commitDate": "2026-08-21T12:51:30+02:00",
|
|
7
|
-
"importedAt": "2026-08-22",
|
|
8
|
-
"license": "Unlicense",
|
|
9
|
-
"excludedEntrypoints": ["src/gloss/gcrowd.js", "src/gloss/gloss.js"],
|
|
10
|
-
"files": {
|
|
11
|
-
"LICENSE": "c290d5a44ba844c2aa271e3be836c25176fe1f8b",
|
|
12
|
-
"src/gloss/catmullClark.js": "3fa76213faa27595836365a451218fb650529fc5",
|
|
13
|
-
"src/gloss/gface.js": "f0e90be3039ddd93adb06254943985ee1a37e0d4",
|
|
14
|
-
"src/gloss/gform.js": "c07865a29b7741fa454238a724e330f05f0f0747",
|
|
15
|
-
"src/gloss/ghair.js": "bfb35aa765415f9d2fe4f14b6dd5cf947ecece4b",
|
|
16
|
-
"src/gloss/glayout.js": "21667e34b7f486b89608698abe2527a9dc565c26",
|
|
17
|
-
"src/gloss/gmedia.js": "aef88b617f3f694b1051237055fd5edcd8be546e",
|
|
18
|
-
"src/gloss/gpalette.js": "31d1fd17decc8a8f779aa06288bf2fe057aec9dd",
|
|
19
|
-
"src/gloss/gparts/blush.js": "daed9e66e45b918569490be71392858fa5e502ec",
|
|
20
|
-
"src/gloss/gparts/body.js": "5f15d76362ec47dc24846a3eacb5f5ba6a435484",
|
|
21
|
-
"src/gloss/gparts/brows.js": "d646a477b73de7e7d0fa4443848aff9d5c328f45",
|
|
22
|
-
"src/gloss/gparts/crest.js": "fb5470509ff75ad2d03addc4d9ba02109f7f5521",
|
|
23
|
-
"src/gloss/gparts/eyes.js": "9188e13c0df544620d951ecf5b0dba05b59ab55b",
|
|
24
|
-
"src/gloss/gparts/frame.js": "957727c53a6f9f9c4bd95399378ae3cdaa5a1423",
|
|
25
|
-
"src/gloss/gparts/hair.js": "b962f6fe0e30bbed537054b4fa0d6193052e5b87",
|
|
26
|
-
"src/gloss/gparts/hat.js": "27fbc817ab9797dd706bde93913dccbdfebb2c48",
|
|
27
|
-
"src/gloss/gparts/index.js": "82fc74c3c2cec69e11b28bfebbdee2df60dc3052",
|
|
28
|
-
"src/gloss/gparts/mark.js": "046143fdedab6bc03c0e7495338b342cef5c4fc9",
|
|
29
|
-
"src/gloss/gparts/mouth.js": "1d6cdcb271b002b37469c985517764aed1647091",
|
|
30
|
-
"src/gloss/gparts/nose.js": "82683b6bda2bb2fcae9b833686aaf9a10c039e52",
|
|
31
|
-
"src/gloss/gparts/specs.js": "780de9e2584f68eb34c199756c1fc045187f0159",
|
|
32
|
-
"src/gloss/grig.js": "2bc489a2bf05d9299a56bca808522a9e2f5fdf49",
|
|
33
|
-
"src/gloss/gshape.js": "c44e0b43ddfebef130d7ec8d6be26f4e9b4e7448",
|
|
34
|
-
"src/gloss/gspecies.js": "1805c18339956068354a6c85c1ca93d39e1758f1",
|
|
35
|
-
"src/gloss/gtexture.js": "00a4c3ddc46496357f7232ea6f68ff537a432697",
|
|
36
|
-
"src/rng.js": "5a6e8fe001552cf8ae860d70318e1c85da58baec"
|
|
37
|
-
}
|
|
38
|
-
}
|