@promptbook/components 0.112.0-117 → 0.112.0-118
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/esm/index.es.js +225 -45
- package/esm/index.es.js.map +1 -1
- package/esm/src/_packages/node.index.d.ts +10 -0
- package/esm/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
- package/esm/src/book-3.0/CliAgent.d.ts +7 -2
- package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +225 -45
- package/umd/index.umd.js.map +1 -1
- package/umd/src/_packages/node.index.d.ts +10 -0
- package/umd/src/book-3.0/BookNodeAgentSource.d.ts +1 -1
- package/umd/src/book-3.0/CliAgent.d.ts +7 -2
- package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +2 -18
- package/umd/src/version.d.ts +1 -1
package/esm/index.es.js
CHANGED
|
@@ -40,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
40
40
|
* @generated
|
|
41
41
|
* @see https://github.com/webgptorg/promptbook
|
|
42
42
|
*/
|
|
43
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
43
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-118';
|
|
44
44
|
/**
|
|
45
45
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
46
46
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2776,21 +2776,22 @@ function getPointBounds(points) {
|
|
|
2776
2776
|
* @private helper of `fractalAvatarVisual`
|
|
2777
2777
|
*/
|
|
2778
2778
|
function drawDragonCurveLayer(context, points, options) {
|
|
2779
|
-
const {
|
|
2779
|
+
const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
|
|
2780
2780
|
const firstPoint = points[0];
|
|
2781
2781
|
const lastPoint = points[points.length - 1];
|
|
2782
2782
|
const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
|
|
2783
2783
|
ribbonGradient.addColorStop(0, `${primaryColor}f2`);
|
|
2784
2784
|
ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
|
|
2785
2785
|
ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
|
|
2786
|
+
// Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
|
|
2787
|
+
// context.filter blur, which triggers a costly software rasterization pass every frame.
|
|
2786
2788
|
context.save();
|
|
2787
2789
|
context.beginPath();
|
|
2788
2790
|
tracePolyline(context, points);
|
|
2789
|
-
context.strokeStyle = `${shadowColor}
|
|
2790
|
-
context.lineWidth = strokeWidth *
|
|
2791
|
+
context.strokeStyle = `${shadowColor}48`;
|
|
2792
|
+
context.lineWidth = strokeWidth * 4.5;
|
|
2791
2793
|
context.lineJoin = 'round';
|
|
2792
2794
|
context.lineCap = 'round';
|
|
2793
|
-
context.filter = `blur(${size * 0.022}px)`;
|
|
2794
2795
|
context.stroke();
|
|
2795
2796
|
context.restore();
|
|
2796
2797
|
context.beginPath();
|
|
@@ -3374,11 +3375,23 @@ function drawMinecraftBackdrop(context, size, palette, sceneCenterX, spotlightY,
|
|
|
3374
3375
|
* @private helper of `minecraft2AvatarVisual`
|
|
3375
3376
|
*/
|
|
3376
3377
|
function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
|
|
3378
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.03;
|
|
3379
|
+
const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
|
|
3380
|
+
const rx = size * (0.16 + interaction.intensity * 0.015);
|
|
3381
|
+
const ry = size * 0.055;
|
|
3382
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
3377
3383
|
context.save();
|
|
3378
|
-
context.
|
|
3379
|
-
context.
|
|
3384
|
+
context.translate(cx, cy);
|
|
3385
|
+
context.scale(1, ry / rx);
|
|
3386
|
+
const blurRadius = rx * 1.4;
|
|
3387
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
3388
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
3389
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
3390
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
3391
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
3392
|
+
context.fillStyle = shadowGradient;
|
|
3380
3393
|
context.beginPath();
|
|
3381
|
-
context.
|
|
3394
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
3382
3395
|
context.fill();
|
|
3383
3396
|
context.restore();
|
|
3384
3397
|
}
|
|
@@ -3602,13 +3615,27 @@ const minecraftAvatarVisual = {
|
|
|
3602
3615
|
spotlight.addColorStop(1, `${palette.highlight}00`);
|
|
3603
3616
|
context.fillStyle = spotlight;
|
|
3604
3617
|
context.fillRect(0, 0, size, size);
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3618
|
+
{
|
|
3619
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
3620
|
+
const cx = size * 0.5;
|
|
3621
|
+
const cy = size * 0.86;
|
|
3622
|
+
const rx = size * 0.2;
|
|
3623
|
+
const ry = size * 0.06;
|
|
3624
|
+
const blurRadius = rx * 1.4;
|
|
3625
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
3626
|
+
shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
|
|
3627
|
+
shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
|
|
3628
|
+
shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
|
|
3629
|
+
shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
|
|
3630
|
+
context.save();
|
|
3631
|
+
context.translate(cx, cy);
|
|
3632
|
+
context.scale(1, ry / rx);
|
|
3633
|
+
context.fillStyle = shadowGradient;
|
|
3634
|
+
context.beginPath();
|
|
3635
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
3636
|
+
context.fill();
|
|
3637
|
+
context.restore();
|
|
3638
|
+
}
|
|
3612
3639
|
drawVoxelCuboid(context, {
|
|
3613
3640
|
x: bodyX,
|
|
3614
3641
|
y: bodyY,
|
|
@@ -4660,6 +4687,35 @@ const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
|
4660
4687
|
y: -0.62,
|
|
4661
4688
|
z: 0.94,
|
|
4662
4689
|
});
|
|
4690
|
+
/**
|
|
4691
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
4692
|
+
*
|
|
4693
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
4694
|
+
*/
|
|
4695
|
+
const octopus3dStableStateCache = new WeakMap();
|
|
4696
|
+
/**
|
|
4697
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
4698
|
+
*
|
|
4699
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
4700
|
+
*/
|
|
4701
|
+
function getOctopus3dStableState(createRandom) {
|
|
4702
|
+
const cached = octopus3dStableStateCache.get(createRandom);
|
|
4703
|
+
if (cached !== undefined) {
|
|
4704
|
+
return cached;
|
|
4705
|
+
}
|
|
4706
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
4707
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
4708
|
+
const leftEyePhaseOffset = eyeRandom() * 0.6;
|
|
4709
|
+
const rightEyePhaseOffset = eyeRandom() * 0.6;
|
|
4710
|
+
const state = {
|
|
4711
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
4712
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
4713
|
+
leftEyePhaseOffset,
|
|
4714
|
+
rightEyePhaseOffset,
|
|
4715
|
+
};
|
|
4716
|
+
octopus3dStableStateCache.set(createRandom, state);
|
|
4717
|
+
return state;
|
|
4718
|
+
}
|
|
4663
4719
|
/**
|
|
4664
4720
|
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
4665
4721
|
*
|
|
@@ -4672,10 +4728,7 @@ const octopus3dAvatarVisual = {
|
|
|
4672
4728
|
isAnimated: true,
|
|
4673
4729
|
supportsPointerTracking: true,
|
|
4674
4730
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
4675
|
-
const morphologyProfile =
|
|
4676
|
-
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
4677
|
-
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
4678
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
4731
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
|
|
4679
4732
|
const sceneCenterX = size * 0.5;
|
|
4680
4733
|
const sceneCenterY = size * 0.56;
|
|
4681
4734
|
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
@@ -4772,12 +4825,12 @@ const octopus3dAvatarVisual = {
|
|
|
4772
4825
|
x: -faceEyeSpacing,
|
|
4773
4826
|
y: faceEyeYOffset,
|
|
4774
4827
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
4775
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
4828
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
4776
4829
|
drawProjectedOrganicEye(context, {
|
|
4777
4830
|
x: faceEyeSpacing,
|
|
4778
4831
|
y: faceEyeYOffset,
|
|
4779
4832
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
4780
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 +
|
|
4833
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
4781
4834
|
drawProjectedOrganicMouth(context, [
|
|
4782
4835
|
{
|
|
4783
4836
|
x: -mouthHalfWidth,
|
|
@@ -4821,14 +4874,28 @@ function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCent
|
|
|
4821
4874
|
/**
|
|
4822
4875
|
* Draws the soft ground shadow below the octopus.
|
|
4823
4876
|
*
|
|
4877
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
4878
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
4879
|
+
*
|
|
4824
4880
|
* @private helper of `octopus3dAvatarVisual`
|
|
4825
4881
|
*/
|
|
4826
4882
|
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
4883
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.04;
|
|
4884
|
+
const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
|
|
4885
|
+
const rx = size * (0.18 + interaction.intensity * 0.02);
|
|
4886
|
+
const ry = size * 0.06;
|
|
4827
4887
|
context.save();
|
|
4828
|
-
context.
|
|
4829
|
-
context.
|
|
4888
|
+
context.translate(cx, cy);
|
|
4889
|
+
context.scale(1, ry / rx);
|
|
4890
|
+
const blurRadius = rx * 1.4;
|
|
4891
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
4892
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
4893
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
4894
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
4895
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
4896
|
+
context.fillStyle = shadowGradient;
|
|
4830
4897
|
context.beginPath();
|
|
4831
|
-
context.
|
|
4898
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
4832
4899
|
context.fill();
|
|
4833
4900
|
context.restore();
|
|
4834
4901
|
}
|
|
@@ -5059,6 +5126,35 @@ const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
|
5059
5126
|
y: -0.6,
|
|
5060
5127
|
z: 0.98,
|
|
5061
5128
|
});
|
|
5129
|
+
/**
|
|
5130
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
5131
|
+
*
|
|
5132
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
5133
|
+
*/
|
|
5134
|
+
const octopus3d2StableStateCache = new WeakMap();
|
|
5135
|
+
/**
|
|
5136
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
5137
|
+
*
|
|
5138
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
5139
|
+
*/
|
|
5140
|
+
function getOctopus3d2StableState(createRandom) {
|
|
5141
|
+
const cached = octopus3d2StableStateCache.get(createRandom);
|
|
5142
|
+
if (cached !== undefined) {
|
|
5143
|
+
return cached;
|
|
5144
|
+
}
|
|
5145
|
+
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
5146
|
+
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
5147
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
5148
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
5149
|
+
const state = {
|
|
5150
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
5151
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
5152
|
+
leftEyePhaseOffset,
|
|
5153
|
+
rightEyePhaseOffset,
|
|
5154
|
+
};
|
|
5155
|
+
octopus3d2StableStateCache.set(createRandom, state);
|
|
5156
|
+
return state;
|
|
5157
|
+
}
|
|
5062
5158
|
/**
|
|
5063
5159
|
* Octopus 3D 2 avatar visual.
|
|
5064
5160
|
*
|
|
@@ -5071,10 +5167,7 @@ const octopus3d2AvatarVisual = {
|
|
|
5071
5167
|
isAnimated: true,
|
|
5072
5168
|
supportsPointerTracking: true,
|
|
5073
5169
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
5074
|
-
const morphologyProfile =
|
|
5075
|
-
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
5076
|
-
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
5077
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
5170
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
|
|
5078
5171
|
const sceneCenterX = size * 0.5;
|
|
5079
5172
|
const sceneCenterY = size * 0.575;
|
|
5080
5173
|
const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
|
|
@@ -5127,8 +5220,8 @@ const octopus3d2AvatarVisual = {
|
|
|
5127
5220
|
const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
|
|
5128
5221
|
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
5129
5222
|
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
5130
|
-
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
5131
|
-
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 +
|
|
5223
|
+
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5224
|
+
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5132
5225
|
drawProjectedOrganicMouth(context, [
|
|
5133
5226
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
5134
5227
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -5157,14 +5250,28 @@ function drawBlobbyOctopusAtmosphere(context, size, palette, sceneCenterX, scene
|
|
|
5157
5250
|
/**
|
|
5158
5251
|
* Draws the soft floor shadow that anchors the single mesh in the frame.
|
|
5159
5252
|
*
|
|
5253
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
5254
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
5255
|
+
*
|
|
5160
5256
|
* @private helper of `octopus3d2AvatarVisual`
|
|
5161
5257
|
*/
|
|
5162
5258
|
function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
5259
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
5260
|
+
const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
|
|
5261
|
+
const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
|
|
5262
|
+
const ry = size * 0.062;
|
|
5163
5263
|
context.save();
|
|
5164
|
-
context.
|
|
5165
|
-
context.
|
|
5264
|
+
context.translate(cx, cy);
|
|
5265
|
+
context.scale(1, ry / rx);
|
|
5266
|
+
const blurRadius = rx * 1.4;
|
|
5267
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
5268
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
5269
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
5270
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
5271
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
5272
|
+
context.fillStyle = shadowGradient;
|
|
5166
5273
|
context.beginPath();
|
|
5167
|
-
context.
|
|
5274
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
5168
5275
|
context.fill();
|
|
5169
5276
|
context.restore();
|
|
5170
5277
|
}
|
|
@@ -5320,6 +5427,40 @@ const LIGHT_DIRECTION = normalizeVector3({
|
|
|
5320
5427
|
* @private helper of `octopus3d3AvatarVisual`
|
|
5321
5428
|
*/
|
|
5322
5429
|
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
5430
|
+
/**
|
|
5431
|
+
* Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
|
|
5432
|
+
* mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
|
|
5433
|
+
* React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
|
|
5434
|
+
*
|
|
5435
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5436
|
+
*/
|
|
5437
|
+
const stableStateCache = new WeakMap();
|
|
5438
|
+
/**
|
|
5439
|
+
* Returns the stable per-avatar state, computing it on first access and returning the cached
|
|
5440
|
+
* result on every subsequent call within the same `<Avatar/>` mount.
|
|
5441
|
+
*
|
|
5442
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
5443
|
+
*/
|
|
5444
|
+
function getOctopus3d3StableState(createRandom) {
|
|
5445
|
+
const cached = stableStateCache.get(createRandom);
|
|
5446
|
+
if (cached !== undefined) {
|
|
5447
|
+
return cached;
|
|
5448
|
+
}
|
|
5449
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
5450
|
+
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
5451
|
+
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
5452
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
5453
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
5454
|
+
const state = {
|
|
5455
|
+
morphologyProfile,
|
|
5456
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
5457
|
+
leftEyePhaseOffset,
|
|
5458
|
+
rightEyePhaseOffset,
|
|
5459
|
+
tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
5460
|
+
};
|
|
5461
|
+
stableStateCache.set(createRandom, state);
|
|
5462
|
+
return state;
|
|
5463
|
+
}
|
|
5323
5464
|
/**
|
|
5324
5465
|
* Octopus 3D 3 avatar visual.
|
|
5325
5466
|
*
|
|
@@ -5332,11 +5473,7 @@ const octopus3d3AvatarVisual = {
|
|
|
5332
5473
|
isAnimated: true,
|
|
5333
5474
|
supportsPointerTracking: true,
|
|
5334
5475
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
5335
|
-
const morphologyProfile =
|
|
5336
|
-
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
5337
|
-
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
5338
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
5339
|
-
const tentacleProfiles = createContinuousTentacleProfiles(createRandom, morphologyProfile);
|
|
5476
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
|
|
5340
5477
|
const sceneCenterX = size * 0.5;
|
|
5341
5478
|
const sceneCenterY = size * 0.535;
|
|
5342
5479
|
const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
|
|
@@ -5413,8 +5550,8 @@ const octopus3d3AvatarVisual = {
|
|
|
5413
5550
|
size,
|
|
5414
5551
|
palette,
|
|
5415
5552
|
});
|
|
5416
|
-
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
5417
|
-
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 +
|
|
5553
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5554
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
5418
5555
|
drawProjectedOrganicMouth(context, [
|
|
5419
5556
|
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
5420
5557
|
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -5465,14 +5602,30 @@ function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, s
|
|
|
5465
5602
|
/**
|
|
5466
5603
|
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
5467
5604
|
*
|
|
5605
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
5606
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
5607
|
+
*
|
|
5468
5608
|
* @private helper of `octopus3d3AvatarVisual`
|
|
5469
5609
|
*/
|
|
5470
5610
|
function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
5611
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
5612
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
5613
|
+
const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
|
|
5614
|
+
const ry = size * 0.06;
|
|
5615
|
+
// Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
|
|
5616
|
+
// then fill with a radial gradient that approximates the blurry edge without context.filter.
|
|
5471
5617
|
context.save();
|
|
5472
|
-
context.
|
|
5473
|
-
context.
|
|
5618
|
+
context.translate(cx, cy);
|
|
5619
|
+
context.scale(1, ry / rx);
|
|
5620
|
+
const blurRadius = rx * 1.4;
|
|
5621
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
5622
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
5623
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
5624
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
5625
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
5626
|
+
context.fillStyle = shadowGradient;
|
|
5474
5627
|
context.beginPath();
|
|
5475
|
-
context.
|
|
5628
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
5476
5629
|
context.fill();
|
|
5477
5630
|
context.restore();
|
|
5478
5631
|
}
|
|
@@ -30739,6 +30892,22 @@ function ChatInputArea(props) {
|
|
|
30739
30892
|
}
|
|
30740
30893
|
|
|
30741
30894
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
30895
|
+
/**
|
|
30896
|
+
* Target frames per second for the shared avatar animation loop.
|
|
30897
|
+
*
|
|
30898
|
+
* Animated octopus visuals change slowly enough that 24 fps is indistinguishable
|
|
30899
|
+
* from 60 fps in practice, while cutting rendering work by ~60% when multiple
|
|
30900
|
+
* avatars are on screen simultaneously.
|
|
30901
|
+
*
|
|
30902
|
+
* @private utility of the avatar rendering system
|
|
30903
|
+
*/
|
|
30904
|
+
const AVATAR_TARGET_FPS = 24;
|
|
30905
|
+
/**
|
|
30906
|
+
* Minimum elapsed time in milliseconds required between avatar render passes.
|
|
30907
|
+
*
|
|
30908
|
+
* @private utility of the avatar rendering system
|
|
30909
|
+
*/
|
|
30910
|
+
const AVATAR_TARGET_FRAME_INTERVAL_MS = 1000 / AVATAR_TARGET_FPS;
|
|
30742
30911
|
/**
|
|
30743
30912
|
* Next registration id used by the shared avatar animation scheduler.
|
|
30744
30913
|
*
|
|
@@ -30757,6 +30926,14 @@ const avatarAnimationListeners = new Map();
|
|
|
30757
30926
|
* @private utility of the avatar rendering system
|
|
30758
30927
|
*/
|
|
30759
30928
|
let avatarAnimationFrameId = null;
|
|
30929
|
+
/**
|
|
30930
|
+
* Timestamp of the most recently rendered avatar frame.
|
|
30931
|
+
*
|
|
30932
|
+
* Used to throttle callbacks to `AVATAR_TARGET_FRAME_INTERVAL_MS`.
|
|
30933
|
+
*
|
|
30934
|
+
* @private utility of the avatar rendering system
|
|
30935
|
+
*/
|
|
30936
|
+
let lastAvatarFrameTime = 0;
|
|
30760
30937
|
/**
|
|
30761
30938
|
* Registers one avatar animation callback in the shared animation loop.
|
|
30762
30939
|
*
|
|
@@ -30791,8 +30968,11 @@ function ensureAvatarAnimationLoop() {
|
|
|
30791
30968
|
}
|
|
30792
30969
|
const runFrame = (now) => {
|
|
30793
30970
|
avatarAnimationFrameId = null;
|
|
30794
|
-
|
|
30795
|
-
|
|
30971
|
+
if (now - lastAvatarFrameTime >= AVATAR_TARGET_FRAME_INTERVAL_MS) {
|
|
30972
|
+
lastAvatarFrameTime = now;
|
|
30973
|
+
for (const avatarAnimationListener of [...avatarAnimationListeners.values()]) {
|
|
30974
|
+
avatarAnimationListener(now);
|
|
30975
|
+
}
|
|
30796
30976
|
}
|
|
30797
30977
|
ensureAvatarAnimationLoop();
|
|
30798
30978
|
};
|