@promptbook/remote-server 0.112.0-69 → 0.112.0-71
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 +772 -240
- package/esm/index.es.js.map +1 -1
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/avatar3dProjectionShared.d.ts +141 -0
- package/esm/src/avatars/visuals/octopus3dAvatarVisual.d.ts +7 -0
- package/esm/src/cli/cli-commands/agent/agentProjectPaths.d.ts +2 -2
- package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +22 -0
- package/esm/src/cli/cli-commands/agent/runMultiple.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent.d.ts +3 -2
- package/esm/src/cli/other/install.test.d.ts +1 -0
- package/esm/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +1 -1
- package/esm/src/commitments/WALLET/WALLET.d.ts +1 -1
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +772 -240
- package/umd/index.umd.js.map +1 -1
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/avatar3dProjectionShared.d.ts +141 -0
- package/umd/src/avatars/visuals/octopus3dAvatarVisual.d.ts +7 -0
- package/umd/src/cli/cli-commands/agent/agentProjectPaths.d.ts +2 -2
- package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +22 -0
- package/umd/src/cli/cli-commands/agent/runMultiple.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent.d.ts +3 -2
- package/umd/src/cli/other/install.test.d.ts +1 -0
- package/umd/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +1 -1
- package/umd/src/commitments/WALLET/WALLET.d.ts +1 -1
- 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-71';
|
|
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
|
|
@@ -3529,11 +3529,11 @@ function capitalize(word) {
|
|
|
3529
3529
|
*/
|
|
3530
3530
|
function pipelineJsonToString(pipelineJson) {
|
|
3531
3531
|
const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
|
|
3532
|
-
let pipelineString =
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3532
|
+
let pipelineString = spaceTrim$1((block) => `
|
|
3533
|
+
# ${title}
|
|
3534
|
+
|
|
3535
|
+
${block(description || '')}
|
|
3536
|
+
`);
|
|
3537
3537
|
const commands = [];
|
|
3538
3538
|
if (pipelineUrl) {
|
|
3539
3539
|
commands.push(`PIPELINE URL ${pipelineUrl}`);
|
|
@@ -3549,20 +3549,17 @@ function pipelineJsonToString(pipelineJson) {
|
|
|
3549
3549
|
for (const parameter of parameters.filter(({ isOutput }) => isOutput)) {
|
|
3550
3550
|
commands.push(`OUTPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
|
|
3551
3551
|
}
|
|
3552
|
-
pipelineString
|
|
3553
|
-
|
|
3552
|
+
pipelineString = spaceTrim$1((block) => `
|
|
3553
|
+
${block(pipelineString)}
|
|
3554
|
+
|
|
3555
|
+
${block(commands.map((command) => `- ${command}`).join('\n'))}
|
|
3556
|
+
`);
|
|
3554
3557
|
for (const task of tasks) {
|
|
3555
3558
|
const {
|
|
3556
3559
|
/* Note: Not using:> name, */
|
|
3557
3560
|
title, description,
|
|
3558
3561
|
/* Note: dependentParameterNames, */
|
|
3559
3562
|
jokerParameterNames: jokers, taskType, content, postprocessingFunctionNames: postprocessing, expectations, format, resultingParameterName, } = task;
|
|
3560
|
-
pipelineString += '\n\n';
|
|
3561
|
-
pipelineString += `## ${title}`;
|
|
3562
|
-
if (description) {
|
|
3563
|
-
pipelineString += '\n\n';
|
|
3564
|
-
pipelineString += description;
|
|
3565
|
-
}
|
|
3566
3563
|
const commands = [];
|
|
3567
3564
|
let contentLanguage = 'text';
|
|
3568
3565
|
if (taskType === 'PROMPT_TASK') {
|
|
@@ -3625,18 +3622,23 @@ function pipelineJsonToString(pipelineJson) {
|
|
|
3625
3622
|
commands.push(`FORMAT JSON`);
|
|
3626
3623
|
}
|
|
3627
3624
|
} /* not else */
|
|
3628
|
-
pipelineString
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3625
|
+
pipelineString = spaceTrim$1((block) => `
|
|
3626
|
+
${block(pipelineString)}
|
|
3627
|
+
|
|
3628
|
+
## ${title}
|
|
3629
|
+
|
|
3630
|
+
${block(description || '')}
|
|
3631
|
+
|
|
3632
|
+
${block(commands.map((command) => `- ${command}`).join('\n'))}
|
|
3633
|
+
|
|
3634
|
+
\`\`\`${contentLanguage}
|
|
3635
|
+
${block(spaceTrim$1(content))}
|
|
3636
|
+
\`\`\`
|
|
3637
|
+
|
|
3638
|
+
\`-> {${resultingParameterName}}\`
|
|
3639
|
+
`); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
|
|
3640
|
+
// <- TODO: [main] !!3 Escape
|
|
3641
|
+
// <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
|
|
3640
3642
|
}
|
|
3641
3643
|
return validatePipelineString(pipelineString);
|
|
3642
3644
|
}
|
|
@@ -13274,6 +13276,184 @@ function drawFractalCore(context, size, palette, timeMs, corePhase) {
|
|
|
13274
13276
|
context.restore();
|
|
13275
13277
|
}
|
|
13276
13278
|
|
|
13279
|
+
/* eslint-disable no-magic-numbers */
|
|
13280
|
+
/**
|
|
13281
|
+
* Default camera distance ratio shared by the proper-3D avatar visuals.
|
|
13282
|
+
*
|
|
13283
|
+
* @private helper of the 3D avatar visuals
|
|
13284
|
+
*/
|
|
13285
|
+
const DEFAULT_3D_CAMERA_DISTANCE_RATIO = 1.4;
|
|
13286
|
+
/**
|
|
13287
|
+
* Clamps one number into the provided range.
|
|
13288
|
+
*
|
|
13289
|
+
* @param value Input value.
|
|
13290
|
+
* @param minimumValue Inclusive lower bound.
|
|
13291
|
+
* @param maximumValue Inclusive upper bound.
|
|
13292
|
+
* @returns Clamped value.
|
|
13293
|
+
*
|
|
13294
|
+
* @private helper of the 3D avatar visuals
|
|
13295
|
+
*/
|
|
13296
|
+
function clampNumber$1(value, minimumValue, maximumValue) {
|
|
13297
|
+
return Math.min(maximumValue, Math.max(minimumValue, value));
|
|
13298
|
+
}
|
|
13299
|
+
/**
|
|
13300
|
+
* Rotates one point around the local Y axis.
|
|
13301
|
+
*
|
|
13302
|
+
* @param point Source point.
|
|
13303
|
+
* @param angle Rotation angle in radians.
|
|
13304
|
+
* @returns Rotated point.
|
|
13305
|
+
*
|
|
13306
|
+
* @private helper of the 3D avatar visuals
|
|
13307
|
+
*/
|
|
13308
|
+
function rotatePointAroundY(point, angle) {
|
|
13309
|
+
const cosine = Math.cos(angle);
|
|
13310
|
+
const sine = Math.sin(angle);
|
|
13311
|
+
return {
|
|
13312
|
+
x: point.x * cosine + point.z * sine,
|
|
13313
|
+
y: point.y,
|
|
13314
|
+
z: -point.x * sine + point.z * cosine,
|
|
13315
|
+
};
|
|
13316
|
+
}
|
|
13317
|
+
/**
|
|
13318
|
+
* Rotates one point around the local X axis.
|
|
13319
|
+
*
|
|
13320
|
+
* @param point Source point.
|
|
13321
|
+
* @param angle Rotation angle in radians.
|
|
13322
|
+
* @returns Rotated point.
|
|
13323
|
+
*
|
|
13324
|
+
* @private helper of the 3D avatar visuals
|
|
13325
|
+
*/
|
|
13326
|
+
function rotatePointAroundX(point, angle) {
|
|
13327
|
+
const cosine = Math.cos(angle);
|
|
13328
|
+
const sine = Math.sin(angle);
|
|
13329
|
+
return {
|
|
13330
|
+
x: point.x,
|
|
13331
|
+
y: point.y * cosine - point.z * sine,
|
|
13332
|
+
z: point.y * sine + point.z * cosine,
|
|
13333
|
+
};
|
|
13334
|
+
}
|
|
13335
|
+
/**
|
|
13336
|
+
* Applies the local rotations and translation to one scene point.
|
|
13337
|
+
*
|
|
13338
|
+
* @param localPoint Point in local object space.
|
|
13339
|
+
* @param center Object center in scene space.
|
|
13340
|
+
* @param rotationX Object pitch in radians.
|
|
13341
|
+
* @param rotationY Object yaw in radians.
|
|
13342
|
+
* @returns Transformed scene-space point.
|
|
13343
|
+
*
|
|
13344
|
+
* @private helper of the 3D avatar visuals
|
|
13345
|
+
*/
|
|
13346
|
+
function transformScenePoint(localPoint, center, rotationX, rotationY) {
|
|
13347
|
+
const yawedPoint = rotatePointAroundY(localPoint, rotationY);
|
|
13348
|
+
const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
|
|
13349
|
+
return {
|
|
13350
|
+
x: center.x + pitchedPoint.x,
|
|
13351
|
+
y: center.y + pitchedPoint.y,
|
|
13352
|
+
z: center.z + pitchedPoint.z,
|
|
13353
|
+
};
|
|
13354
|
+
}
|
|
13355
|
+
/**
|
|
13356
|
+
* Projects one scene point into canvas coordinates.
|
|
13357
|
+
*
|
|
13358
|
+
* @param point Scene-space point.
|
|
13359
|
+
* @param size Canvas size in CSS pixels.
|
|
13360
|
+
* @param sceneCenterX Horizontal scene center.
|
|
13361
|
+
* @param sceneCenterY Vertical scene center.
|
|
13362
|
+
* @param cameraDistanceRatio Optional camera distance ratio.
|
|
13363
|
+
* @returns Projected point.
|
|
13364
|
+
*
|
|
13365
|
+
* @private helper of the 3D avatar visuals
|
|
13366
|
+
*/
|
|
13367
|
+
function projectScenePoint(point, size, sceneCenterX, sceneCenterY, cameraDistanceRatio = DEFAULT_3D_CAMERA_DISTANCE_RATIO) {
|
|
13368
|
+
const cameraDistance = size * cameraDistanceRatio;
|
|
13369
|
+
const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
|
|
13370
|
+
return {
|
|
13371
|
+
x: sceneCenterX + point.x * perspectiveScale,
|
|
13372
|
+
y: sceneCenterY + point.y * perspectiveScale,
|
|
13373
|
+
z: point.z,
|
|
13374
|
+
};
|
|
13375
|
+
}
|
|
13376
|
+
/**
|
|
13377
|
+
* Subtracts one 3D point from another.
|
|
13378
|
+
*
|
|
13379
|
+
* @param leftPoint Left point.
|
|
13380
|
+
* @param rightPoint Right point.
|
|
13381
|
+
* @returns Difference vector.
|
|
13382
|
+
*
|
|
13383
|
+
* @private helper of the 3D avatar visuals
|
|
13384
|
+
*/
|
|
13385
|
+
function subtractPoint3D(leftPoint, rightPoint) {
|
|
13386
|
+
return {
|
|
13387
|
+
x: leftPoint.x - rightPoint.x,
|
|
13388
|
+
y: leftPoint.y - rightPoint.y,
|
|
13389
|
+
z: leftPoint.z - rightPoint.z,
|
|
13390
|
+
};
|
|
13391
|
+
}
|
|
13392
|
+
/**
|
|
13393
|
+
* Computes the 3D cross product of two vectors.
|
|
13394
|
+
*
|
|
13395
|
+
* @param leftVector Left vector.
|
|
13396
|
+
* @param rightVector Right vector.
|
|
13397
|
+
* @returns Cross product.
|
|
13398
|
+
*
|
|
13399
|
+
* @private helper of the 3D avatar visuals
|
|
13400
|
+
*/
|
|
13401
|
+
function crossProduct3D(leftVector, rightVector) {
|
|
13402
|
+
return {
|
|
13403
|
+
x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
|
|
13404
|
+
y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
|
|
13405
|
+
z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
|
|
13406
|
+
};
|
|
13407
|
+
}
|
|
13408
|
+
/**
|
|
13409
|
+
* Computes the 3D dot product of two vectors.
|
|
13410
|
+
*
|
|
13411
|
+
* @param leftVector Left vector.
|
|
13412
|
+
* @param rightVector Right vector.
|
|
13413
|
+
* @returns Dot product.
|
|
13414
|
+
*
|
|
13415
|
+
* @private helper of the 3D avatar visuals
|
|
13416
|
+
*/
|
|
13417
|
+
function dotProduct3D(leftVector, rightVector) {
|
|
13418
|
+
return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
|
|
13419
|
+
}
|
|
13420
|
+
/**
|
|
13421
|
+
* Normalizes one 3D vector while keeping zero vectors stable.
|
|
13422
|
+
*
|
|
13423
|
+
* @param vector Source vector.
|
|
13424
|
+
* @returns Normalized vector.
|
|
13425
|
+
*
|
|
13426
|
+
* @private helper of the 3D avatar visuals
|
|
13427
|
+
*/
|
|
13428
|
+
function normalizeVector3(vector) {
|
|
13429
|
+
const length = Math.hypot(vector.x, vector.y, vector.z);
|
|
13430
|
+
if (length === 0) {
|
|
13431
|
+
return vector;
|
|
13432
|
+
}
|
|
13433
|
+
return {
|
|
13434
|
+
x: vector.x / length,
|
|
13435
|
+
y: vector.y / length,
|
|
13436
|
+
z: vector.z / length,
|
|
13437
|
+
};
|
|
13438
|
+
}
|
|
13439
|
+
/**
|
|
13440
|
+
* Measures the perimeter of one projected quad.
|
|
13441
|
+
*
|
|
13442
|
+
* @param corners Quad corners.
|
|
13443
|
+
* @returns Perimeter length.
|
|
13444
|
+
*
|
|
13445
|
+
* @private helper of the 3D avatar visuals
|
|
13446
|
+
*/
|
|
13447
|
+
function getProjectedQuadPerimeter(corners) {
|
|
13448
|
+
let perimeter = 0;
|
|
13449
|
+
for (let cornerIndex = 0; cornerIndex < corners.length; cornerIndex++) {
|
|
13450
|
+
const currentCorner = corners[cornerIndex];
|
|
13451
|
+
const nextCorner = corners[(cornerIndex + 1) % corners.length];
|
|
13452
|
+
perimeter += Math.hypot(nextCorner.x - currentCorner.x, nextCorner.y - currentCorner.y);
|
|
13453
|
+
}
|
|
13454
|
+
return perimeter;
|
|
13455
|
+
}
|
|
13456
|
+
|
|
13277
13457
|
/* eslint-disable no-magic-numbers */
|
|
13278
13458
|
/**
|
|
13279
13459
|
* Builds the seeded six-face texture pack used by the Minecraft-style head cuboid.
|
|
@@ -13446,18 +13626,12 @@ function fillTextureRect(texture, x, y, width, height, color) {
|
|
|
13446
13626
|
}
|
|
13447
13627
|
|
|
13448
13628
|
/* eslint-disable no-magic-numbers */
|
|
13449
|
-
/**
|
|
13450
|
-
* Fixed scene camera distance used for the proper-3D projection.
|
|
13451
|
-
*
|
|
13452
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13453
|
-
*/
|
|
13454
|
-
const CAMERA_DISTANCE_RATIO = 1.4;
|
|
13455
13629
|
/**
|
|
13456
13630
|
* Shared light direction used to shade projected cuboid faces.
|
|
13457
13631
|
*
|
|
13458
13632
|
* @private helper of `minecraft2AvatarVisual`
|
|
13459
13633
|
*/
|
|
13460
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
13634
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
13461
13635
|
x: 0.4,
|
|
13462
13636
|
y: -0.65,
|
|
13463
13637
|
z: 0.92,
|
|
@@ -13669,7 +13843,7 @@ function resolveVisibleCuboidFaces(cuboid, size, sceneCenterX, sceneCenterY) {
|
|
|
13669
13843
|
corners: projectedCorners,
|
|
13670
13844
|
texture: faceDefinition.texture,
|
|
13671
13845
|
averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
|
|
13672
|
-
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION), -1, 1),
|
|
13846
|
+
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$1), -1, 1),
|
|
13673
13847
|
outlineColor: cuboid.outlineColor,
|
|
13674
13848
|
};
|
|
13675
13849
|
});
|
|
@@ -13696,7 +13870,7 @@ function drawTexturedProjectedFace(context, face) {
|
|
|
13696
13870
|
const endX = (columnIndex + 1) / columns;
|
|
13697
13871
|
const startY = rowIndex / rows;
|
|
13698
13872
|
const endY = (rowIndex + 1) / rows;
|
|
13699
|
-
drawProjectedQuad(context, [
|
|
13873
|
+
drawProjectedQuad$1(context, [
|
|
13700
13874
|
interpolateProjectedQuad(face.corners, startX, startY),
|
|
13701
13875
|
interpolateProjectedQuad(face.corners, endX, startY),
|
|
13702
13876
|
interpolateProjectedQuad(face.corners, endX, endY),
|
|
@@ -13705,10 +13879,10 @@ function drawTexturedProjectedFace(context, face) {
|
|
|
13705
13879
|
}
|
|
13706
13880
|
}
|
|
13707
13881
|
if (face.lightIntensity > 0) {
|
|
13708
|
-
drawProjectedQuad(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
|
|
13882
|
+
drawProjectedQuad$1(context, face.corners, `rgba(255, 255, 255, ${0.15 * face.lightIntensity})`);
|
|
13709
13883
|
}
|
|
13710
13884
|
else if (face.lightIntensity < 0) {
|
|
13711
|
-
drawProjectedQuad(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
|
|
13885
|
+
drawProjectedQuad$1(context, face.corners, `rgba(0, 0, 0, ${0.22 * Math.abs(face.lightIntensity)})`);
|
|
13712
13886
|
}
|
|
13713
13887
|
context.save();
|
|
13714
13888
|
context.beginPath();
|
|
@@ -13732,7 +13906,7 @@ function drawTexturedProjectedFace(context, face) {
|
|
|
13732
13906
|
*
|
|
13733
13907
|
* @private helper of `minecraft2AvatarVisual`
|
|
13734
13908
|
*/
|
|
13735
|
-
function drawProjectedQuad(context, corners, fillStyle) {
|
|
13909
|
+
function drawProjectedQuad$1(context, corners, fillStyle) {
|
|
13736
13910
|
context.beginPath();
|
|
13737
13911
|
context.moveTo(corners[0].x, corners[0].y);
|
|
13738
13912
|
context.lineTo(corners[1].x, corners[1].y);
|
|
@@ -13745,203 +13919,34 @@ function drawProjectedQuad(context, corners, fillStyle) {
|
|
|
13745
13919
|
/**
|
|
13746
13920
|
* Interpolates one point inside a projected quad across both quad axes.
|
|
13747
13921
|
*
|
|
13748
|
-
* @param corners Quad corners in clockwise order.
|
|
13749
|
-
* @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
|
|
13750
|
-
* @param verticalRatio Vertical ratio in the range `[0, 1]`.
|
|
13751
|
-
* @returns Interpolated projected point.
|
|
13752
|
-
*
|
|
13753
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13754
|
-
*/
|
|
13755
|
-
function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
|
|
13756
|
-
const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
|
|
13757
|
-
const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
|
|
13758
|
-
return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
|
|
13759
|
-
}
|
|
13760
|
-
/**
|
|
13761
|
-
* Interpolates between two projected points.
|
|
13762
|
-
*
|
|
13763
|
-
* @param startPoint Start point.
|
|
13764
|
-
* @param endPoint End point.
|
|
13765
|
-
* @param ratio Interpolation ratio in the range `[0, 1]`.
|
|
13766
|
-
* @returns Interpolated projected point.
|
|
13767
|
-
*
|
|
13768
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13769
|
-
*/
|
|
13770
|
-
function interpolateProjectedPoint(startPoint, endPoint, ratio) {
|
|
13771
|
-
return {
|
|
13772
|
-
x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
|
|
13773
|
-
y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
|
|
13774
|
-
z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
|
|
13775
|
-
};
|
|
13776
|
-
}
|
|
13777
|
-
/**
|
|
13778
|
-
* Projects one rotated scene point into canvas coordinates.
|
|
13779
|
-
*
|
|
13780
|
-
* @param point Scene point.
|
|
13781
|
-
* @param size Canvas size in CSS pixels.
|
|
13782
|
-
* @param sceneCenterX Horizontal scene center.
|
|
13783
|
-
* @param sceneCenterY Vertical scene center.
|
|
13784
|
-
* @returns Projected point.
|
|
13785
|
-
*
|
|
13786
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13787
|
-
*/
|
|
13788
|
-
function projectScenePoint(point, size, sceneCenterX, sceneCenterY) {
|
|
13789
|
-
const cameraDistance = size * CAMERA_DISTANCE_RATIO;
|
|
13790
|
-
const perspectiveScale = cameraDistance / Math.max(cameraDistance - point.z, cameraDistance * 0.35);
|
|
13791
|
-
return {
|
|
13792
|
-
x: sceneCenterX + point.x * perspectiveScale,
|
|
13793
|
-
y: sceneCenterY + point.y * perspectiveScale,
|
|
13794
|
-
z: point.z,
|
|
13795
|
-
};
|
|
13796
|
-
}
|
|
13797
|
-
/**
|
|
13798
|
-
* Applies the local cuboid rotations and translation to one scene point.
|
|
13799
|
-
*
|
|
13800
|
-
* @param localPoint Point in cuboid-local space.
|
|
13801
|
-
* @param center Cuboid center in scene space.
|
|
13802
|
-
* @param rotationX Cuboid pitch in radians.
|
|
13803
|
-
* @param rotationY Cuboid yaw in radians.
|
|
13804
|
-
* @returns Transformed scene-space point.
|
|
13805
|
-
*
|
|
13806
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13807
|
-
*/
|
|
13808
|
-
function transformScenePoint(localPoint, center, rotationX, rotationY) {
|
|
13809
|
-
const yawedPoint = rotatePointAroundY(localPoint, rotationY);
|
|
13810
|
-
const pitchedPoint = rotatePointAroundX(yawedPoint, rotationX);
|
|
13811
|
-
return {
|
|
13812
|
-
x: center.x + pitchedPoint.x,
|
|
13813
|
-
y: center.y + pitchedPoint.y,
|
|
13814
|
-
z: center.z + pitchedPoint.z,
|
|
13815
|
-
};
|
|
13816
|
-
}
|
|
13817
|
-
/**
|
|
13818
|
-
* Rotates one point around the local Y axis.
|
|
13819
|
-
*
|
|
13820
|
-
* @param point Source point.
|
|
13821
|
-
* @param angle Rotation angle in radians.
|
|
13822
|
-
* @returns Rotated point.
|
|
13823
|
-
*
|
|
13824
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13825
|
-
*/
|
|
13826
|
-
function rotatePointAroundY(point, angle) {
|
|
13827
|
-
const cosine = Math.cos(angle);
|
|
13828
|
-
const sine = Math.sin(angle);
|
|
13829
|
-
return {
|
|
13830
|
-
x: point.x * cosine + point.z * sine,
|
|
13831
|
-
y: point.y,
|
|
13832
|
-
z: -point.x * sine + point.z * cosine,
|
|
13833
|
-
};
|
|
13834
|
-
}
|
|
13835
|
-
/**
|
|
13836
|
-
* Rotates one point around the local X axis.
|
|
13837
|
-
*
|
|
13838
|
-
* @param point Source point.
|
|
13839
|
-
* @param angle Rotation angle in radians.
|
|
13840
|
-
* @returns Rotated point.
|
|
13841
|
-
*
|
|
13842
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13843
|
-
*/
|
|
13844
|
-
function rotatePointAroundX(point, angle) {
|
|
13845
|
-
const cosine = Math.cos(angle);
|
|
13846
|
-
const sine = Math.sin(angle);
|
|
13847
|
-
return {
|
|
13848
|
-
x: point.x,
|
|
13849
|
-
y: point.y * cosine - point.z * sine,
|
|
13850
|
-
z: point.y * sine + point.z * cosine,
|
|
13851
|
-
};
|
|
13852
|
-
}
|
|
13853
|
-
/**
|
|
13854
|
-
* Subtracts one 3D point from another.
|
|
13855
|
-
*
|
|
13856
|
-
* @param leftPoint Left point.
|
|
13857
|
-
* @param rightPoint Right point.
|
|
13858
|
-
* @returns Difference vector.
|
|
13859
|
-
*
|
|
13860
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13861
|
-
*/
|
|
13862
|
-
function subtractPoint3D(leftPoint, rightPoint) {
|
|
13863
|
-
return {
|
|
13864
|
-
x: leftPoint.x - rightPoint.x,
|
|
13865
|
-
y: leftPoint.y - rightPoint.y,
|
|
13866
|
-
z: leftPoint.z - rightPoint.z,
|
|
13867
|
-
};
|
|
13868
|
-
}
|
|
13869
|
-
/**
|
|
13870
|
-
* Computes the 3D cross product of two vectors.
|
|
13871
|
-
*
|
|
13872
|
-
* @param leftVector Left vector.
|
|
13873
|
-
* @param rightVector Right vector.
|
|
13874
|
-
* @returns Cross product.
|
|
13875
|
-
*
|
|
13876
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13877
|
-
*/
|
|
13878
|
-
function crossProduct3D(leftVector, rightVector) {
|
|
13879
|
-
return {
|
|
13880
|
-
x: leftVector.y * rightVector.z - leftVector.z * rightVector.y,
|
|
13881
|
-
y: leftVector.z * rightVector.x - leftVector.x * rightVector.z,
|
|
13882
|
-
z: leftVector.x * rightVector.y - leftVector.y * rightVector.x,
|
|
13883
|
-
};
|
|
13884
|
-
}
|
|
13885
|
-
/**
|
|
13886
|
-
* Computes the 3D dot product of two vectors.
|
|
13887
|
-
*
|
|
13888
|
-
* @param leftVector Left vector.
|
|
13889
|
-
* @param rightVector Right vector.
|
|
13890
|
-
* @returns Dot product.
|
|
13891
|
-
*
|
|
13892
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13893
|
-
*/
|
|
13894
|
-
function dotProduct3D(leftVector, rightVector) {
|
|
13895
|
-
return leftVector.x * rightVector.x + leftVector.y * rightVector.y + leftVector.z * rightVector.z;
|
|
13896
|
-
}
|
|
13897
|
-
/**
|
|
13898
|
-
* Normalizes one 3D vector while keeping zero vectors stable.
|
|
13899
|
-
*
|
|
13900
|
-
* @param vector Source vector.
|
|
13901
|
-
* @returns Normalized vector.
|
|
13902
|
-
*
|
|
13903
|
-
* @private helper of `minecraft2AvatarVisual`
|
|
13904
|
-
*/
|
|
13905
|
-
function normalizeVector3(vector) {
|
|
13906
|
-
const length = Math.hypot(vector.x, vector.y, vector.z);
|
|
13907
|
-
if (length === 0) {
|
|
13908
|
-
return vector;
|
|
13909
|
-
}
|
|
13910
|
-
return {
|
|
13911
|
-
x: vector.x / length,
|
|
13912
|
-
y: vector.y / length,
|
|
13913
|
-
z: vector.z / length,
|
|
13914
|
-
};
|
|
13915
|
-
}
|
|
13916
|
-
/**
|
|
13917
|
-
* Clamps one number into the provided range.
|
|
13918
|
-
*
|
|
13919
|
-
* @param value Input value.
|
|
13920
|
-
* @param minimumValue Inclusive lower bound.
|
|
13921
|
-
* @param maximumValue Inclusive upper bound.
|
|
13922
|
-
* @returns Clamped value.
|
|
13922
|
+
* @param corners Quad corners in clockwise order.
|
|
13923
|
+
* @param horizontalRatio Horizontal ratio in the range `[0, 1]`.
|
|
13924
|
+
* @param verticalRatio Vertical ratio in the range `[0, 1]`.
|
|
13925
|
+
* @returns Interpolated projected point.
|
|
13923
13926
|
*
|
|
13924
13927
|
* @private helper of `minecraft2AvatarVisual`
|
|
13925
13928
|
*/
|
|
13926
|
-
function
|
|
13927
|
-
|
|
13929
|
+
function interpolateProjectedQuad(corners, horizontalRatio, verticalRatio) {
|
|
13930
|
+
const topPoint = interpolateProjectedPoint(corners[0], corners[1], horizontalRatio);
|
|
13931
|
+
const bottomPoint = interpolateProjectedPoint(corners[3], corners[2], horizontalRatio);
|
|
13932
|
+
return interpolateProjectedPoint(topPoint, bottomPoint, verticalRatio);
|
|
13928
13933
|
}
|
|
13929
13934
|
/**
|
|
13930
|
-
*
|
|
13935
|
+
* Interpolates between two projected points.
|
|
13931
13936
|
*
|
|
13932
|
-
* @param
|
|
13933
|
-
* @
|
|
13937
|
+
* @param startPoint Start point.
|
|
13938
|
+
* @param endPoint End point.
|
|
13939
|
+
* @param ratio Interpolation ratio in the range `[0, 1]`.
|
|
13940
|
+
* @returns Interpolated projected point.
|
|
13934
13941
|
*
|
|
13935
13942
|
* @private helper of `minecraft2AvatarVisual`
|
|
13936
13943
|
*/
|
|
13937
|
-
function
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
}
|
|
13944
|
-
return perimeter;
|
|
13944
|
+
function interpolateProjectedPoint(startPoint, endPoint, ratio) {
|
|
13945
|
+
return {
|
|
13946
|
+
x: startPoint.x + (endPoint.x - startPoint.x) * ratio,
|
|
13947
|
+
y: startPoint.y + (endPoint.y - startPoint.y) * ratio,
|
|
13948
|
+
z: startPoint.z + (endPoint.z - startPoint.z) * ratio,
|
|
13949
|
+
};
|
|
13945
13950
|
}
|
|
13946
13951
|
|
|
13947
13952
|
/* eslint-disable no-magic-numbers */
|
|
@@ -14511,7 +14516,7 @@ function resolveSeededIntegerRange(random, minimumValue, maximumValue) {
|
|
|
14511
14516
|
*
|
|
14512
14517
|
* @private helper of `octopus3AvatarVisual`
|
|
14513
14518
|
*/
|
|
14514
|
-
function formatAlphaHex(opacity) {
|
|
14519
|
+
function formatAlphaHex$1(opacity) {
|
|
14515
14520
|
return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
|
|
14516
14521
|
.toString(16)
|
|
14517
14522
|
.padStart(2, '0');
|
|
@@ -14890,7 +14895,7 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
|
|
|
14890
14895
|
context.beginPath();
|
|
14891
14896
|
context.moveTo(-radiusX * 0.74, radiusY * 0.2);
|
|
14892
14897
|
context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
|
|
14893
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
|
|
14898
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
|
|
14894
14899
|
context.lineWidth = radiusX * 0.08;
|
|
14895
14900
|
context.lineCap = 'round';
|
|
14896
14901
|
context.stroke();
|
|
@@ -14898,6 +14903,518 @@ function drawSeededEye(context, centerX, centerY, radiusX, radiusY, rotation, pa
|
|
|
14898
14903
|
context.restore();
|
|
14899
14904
|
}
|
|
14900
14905
|
|
|
14906
|
+
/* eslint-disable no-magic-numbers */
|
|
14907
|
+
/**
|
|
14908
|
+
* Light direction used by the organic 3D octopus shading.
|
|
14909
|
+
*
|
|
14910
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
14911
|
+
*/
|
|
14912
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
14913
|
+
x: 0.48,
|
|
14914
|
+
y: -0.62,
|
|
14915
|
+
z: 0.94,
|
|
14916
|
+
});
|
|
14917
|
+
/**
|
|
14918
|
+
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
14919
|
+
*
|
|
14920
|
+
* @private built-in avatar visual
|
|
14921
|
+
*/
|
|
14922
|
+
const octopus3dAvatarVisual = {
|
|
14923
|
+
id: 'octopus3d',
|
|
14924
|
+
title: 'Octopus 3D',
|
|
14925
|
+
description: 'Proper 3D octopus portrait with a turning silhouette, expressive eyes, and depth-sorted tentacles.',
|
|
14926
|
+
isAnimated: true,
|
|
14927
|
+
supportsPointerTracking: true,
|
|
14928
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
14929
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
14930
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
14931
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
14932
|
+
const animationPhase = animationRandom() * Math.PI * 2;
|
|
14933
|
+
const sceneCenterX = size * 0.5;
|
|
14934
|
+
const sceneCenterY = size * 0.56;
|
|
14935
|
+
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
14936
|
+
const mantleCenter = {
|
|
14937
|
+
x: interaction.bodyOffsetX * size * 0.042 + size * morphologyProfile.body.centerXJitterRatio * 0.65,
|
|
14938
|
+
y: -size * 0.09 + interaction.bodyOffsetY * size * 0.028 + bob,
|
|
14939
|
+
z: interaction.intensity * size * 0.012,
|
|
14940
|
+
};
|
|
14941
|
+
const underbodyCenter = {
|
|
14942
|
+
x: mantleCenter.x * 0.86,
|
|
14943
|
+
y: mantleCenter.y + size * 0.168,
|
|
14944
|
+
z: mantleCenter.z - size * 0.018,
|
|
14945
|
+
};
|
|
14946
|
+
const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
|
|
14947
|
+
const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
|
|
14948
|
+
const mantleRadiusZ = size * morphologyProfile.body.bodyRadiusRatio * (0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
|
|
14949
|
+
const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
|
|
14950
|
+
const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
|
|
14951
|
+
const underbodyRadiusZ = mantleRadiusZ * 0.78;
|
|
14952
|
+
const bodyYaw = -0.18 +
|
|
14953
|
+
Math.sin(timeMs / 2400 + animationPhase) * 0.05 +
|
|
14954
|
+
interaction.bodyOffsetX * 0.18 +
|
|
14955
|
+
interaction.gazeX * 0.22;
|
|
14956
|
+
const bodyPitch = -0.08 +
|
|
14957
|
+
Math.cos(timeMs / 2700 + animationPhase * 0.6) * 0.025 -
|
|
14958
|
+
interaction.bodyOffsetY * 0.08 -
|
|
14959
|
+
interaction.gazeY * 0.08;
|
|
14960
|
+
const headYaw = bodyYaw - 0.04 + interaction.gazeX * 0.56;
|
|
14961
|
+
const headPitch = bodyPitch - 0.02 - interaction.gazeY * 0.32;
|
|
14962
|
+
const mantlePatches = resolveVisibleEllipsoidPatches({
|
|
14963
|
+
center: mantleCenter,
|
|
14964
|
+
radiusX: mantleRadiusX,
|
|
14965
|
+
radiusY: mantleRadiusY,
|
|
14966
|
+
radiusZ: mantleRadiusZ,
|
|
14967
|
+
rotationX: headPitch,
|
|
14968
|
+
rotationY: headYaw,
|
|
14969
|
+
sceneCenterX,
|
|
14970
|
+
sceneCenterY,
|
|
14971
|
+
size,
|
|
14972
|
+
palette,
|
|
14973
|
+
verticalColorBias: 0,
|
|
14974
|
+
outlineColor: `${palette.highlight}7a`,
|
|
14975
|
+
});
|
|
14976
|
+
const underbodyPatches = resolveVisibleEllipsoidPatches({
|
|
14977
|
+
center: underbodyCenter,
|
|
14978
|
+
radiusX: underbodyRadiusX,
|
|
14979
|
+
radiusY: underbodyRadiusY,
|
|
14980
|
+
radiusZ: underbodyRadiusZ,
|
|
14981
|
+
rotationX: bodyPitch,
|
|
14982
|
+
rotationY: bodyYaw,
|
|
14983
|
+
sceneCenterX,
|
|
14984
|
+
sceneCenterY,
|
|
14985
|
+
size,
|
|
14986
|
+
palette,
|
|
14987
|
+
verticalColorBias: 0.18,
|
|
14988
|
+
outlineColor: `${palette.shadow}8f`,
|
|
14989
|
+
});
|
|
14990
|
+
const tentacleStrokes = createOctopusTentacleStrokes({
|
|
14991
|
+
createRandom,
|
|
14992
|
+
morphologyProfile,
|
|
14993
|
+
timeMs,
|
|
14994
|
+
size,
|
|
14995
|
+
center: underbodyCenter,
|
|
14996
|
+
radiusX: underbodyRadiusX,
|
|
14997
|
+
radiusY: underbodyRadiusY,
|
|
14998
|
+
radiusZ: underbodyRadiusZ,
|
|
14999
|
+
rotationX: bodyPitch,
|
|
15000
|
+
rotationY: bodyYaw,
|
|
15001
|
+
sceneCenterX,
|
|
15002
|
+
sceneCenterY,
|
|
15003
|
+
animationPhase,
|
|
15004
|
+
});
|
|
15005
|
+
const faceEyeSpacing = size * morphologyProfile.face.eyeSpacingRatio * 0.92;
|
|
15006
|
+
const faceEyeYOffset = size * morphologyProfile.face.eyeCenterYOffsetRatio - mantleRadiusY * 0.02;
|
|
15007
|
+
const faceEyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.82;
|
|
15008
|
+
const faceEyeRadiusY = faceEyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.96;
|
|
15009
|
+
const mouthHalfWidth = size * morphologyProfile.face.mouthWidthRatio * 0.92;
|
|
15010
|
+
const mouthY = size * morphologyProfile.face.mouthYOffsetRatio + mantleRadiusY * 0.08;
|
|
15011
|
+
drawAvatarFrame(context, size, palette);
|
|
15012
|
+
drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
15013
|
+
drawOctopus3dShadow(context, size, palette, interaction, timeMs);
|
|
15014
|
+
for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => !candidateTentacleStroke.isFrontFacing)) {
|
|
15015
|
+
drawTentacleStroke(context, tentacleStroke, palette);
|
|
15016
|
+
}
|
|
15017
|
+
for (const surfacePatch of [...mantlePatches, ...underbodyPatches].sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
15018
|
+
drawSurfacePatch(context, surfacePatch);
|
|
15019
|
+
}
|
|
15020
|
+
for (const tentacleStroke of tentacleStrokes.filter((candidateTentacleStroke) => candidateTentacleStroke.isFrontFacing)) {
|
|
15021
|
+
drawTentacleStroke(context, tentacleStroke, palette);
|
|
15022
|
+
}
|
|
15023
|
+
drawProjectedEye(context, {
|
|
15024
|
+
x: -faceEyeSpacing,
|
|
15025
|
+
y: faceEyeYOffset,
|
|
15026
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
15027
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
|
|
15028
|
+
drawProjectedEye(context, {
|
|
15029
|
+
x: faceEyeSpacing,
|
|
15030
|
+
y: faceEyeYOffset,
|
|
15031
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
15032
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
|
|
15033
|
+
drawProjectedMouth(context, [
|
|
15034
|
+
{ x: -mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY) },
|
|
15035
|
+
{
|
|
15036
|
+
x: size * morphologyProfile.face.mouthCenterOffsetRatio,
|
|
15037
|
+
y: mouthY +
|
|
15038
|
+
size * morphologyProfile.face.mouthCurveDepthRatio * 0.38 +
|
|
15039
|
+
Math.sin(timeMs / 760 + animationPhase) * size * 0.01 +
|
|
15040
|
+
interaction.gazeY * size * 0.01,
|
|
15041
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
|
|
15042
|
+
},
|
|
15043
|
+
{ x: mouthHalfWidth, y: mouthY, z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY) },
|
|
15044
|
+
], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
|
|
15045
|
+
},
|
|
15046
|
+
};
|
|
15047
|
+
/**
|
|
15048
|
+
* Draws the atmospheric underwater glow behind the octopus mesh.
|
|
15049
|
+
*
|
|
15050
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15051
|
+
*/
|
|
15052
|
+
function drawOctopus3dAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
15053
|
+
const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.1, sceneCenterY - size * 0.2 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY - size * 0.04, size * 0.62);
|
|
15054
|
+
glowGradient.addColorStop(0, `${palette.highlight}5c`);
|
|
15055
|
+
glowGradient.addColorStop(0.36, `${palette.accent}24`);
|
|
15056
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
15057
|
+
context.fillStyle = glowGradient;
|
|
15058
|
+
context.fillRect(0, 0, size, size);
|
|
15059
|
+
const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1700) * size * 0.05, sceneCenterY + size * 0.23, size * 0.08, sceneCenterX, sceneCenterY + size * 0.28, size * 0.5);
|
|
15060
|
+
lowerGradient.addColorStop(0, `${palette.secondary}1d`);
|
|
15061
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
15062
|
+
context.fillStyle = lowerGradient;
|
|
15063
|
+
context.fillRect(0, 0, size, size);
|
|
15064
|
+
}
|
|
15065
|
+
/**
|
|
15066
|
+
* Draws the soft ground shadow below the octopus.
|
|
15067
|
+
*
|
|
15068
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15069
|
+
*/
|
|
15070
|
+
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
15071
|
+
context.save();
|
|
15072
|
+
context.fillStyle = `${palette.shadow}66`;
|
|
15073
|
+
context.filter = `blur(${size * 0.022}px)`;
|
|
15074
|
+
context.beginPath();
|
|
15075
|
+
context.ellipse(size * 0.5 + interaction.gazeX * size * 0.04, size * 0.87 + Math.sin(timeMs / 920) * size * 0.008, size * (0.18 + interaction.intensity * 0.02), size * 0.06, 0, 0, Math.PI * 2);
|
|
15076
|
+
context.fill();
|
|
15077
|
+
context.restore();
|
|
15078
|
+
}
|
|
15079
|
+
/**
|
|
15080
|
+
* Resolves visible projected patches for one rotated ellipsoid mesh.
|
|
15081
|
+
*
|
|
15082
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15083
|
+
*/
|
|
15084
|
+
function resolveVisibleEllipsoidPatches(options) {
|
|
15085
|
+
const { center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, verticalColorBias, outlineColor, } = options;
|
|
15086
|
+
const latitudePatchCount = 10;
|
|
15087
|
+
const longitudePatchCount = 18;
|
|
15088
|
+
const surfacePatches = [];
|
|
15089
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
15090
|
+
const startLatitude = -Math.PI / 2 + (latitudeIndex / latitudePatchCount) * Math.PI;
|
|
15091
|
+
const endLatitude = -Math.PI / 2 + ((latitudeIndex + 1) / latitudePatchCount) * Math.PI;
|
|
15092
|
+
const verticalProgress = (latitudeIndex + 0.5) / latitudePatchCount;
|
|
15093
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
15094
|
+
const startLongitude = -Math.PI + (longitudeIndex / longitudePatchCount) * Math.PI * 2;
|
|
15095
|
+
const endLongitude = -Math.PI + ((longitudeIndex + 1) / longitudePatchCount) * Math.PI * 2;
|
|
15096
|
+
const localCorners = [
|
|
15097
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, startLongitude),
|
|
15098
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, startLatitude, endLongitude),
|
|
15099
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, endLongitude),
|
|
15100
|
+
sampleEllipsoidPoint(radiusX, radiusY, radiusZ, endLatitude, startLongitude),
|
|
15101
|
+
];
|
|
15102
|
+
const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
|
|
15103
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
15104
|
+
if (surfaceNormal.z <= 0.01) {
|
|
15105
|
+
continue;
|
|
15106
|
+
}
|
|
15107
|
+
const projectedCorners = transformedCorners.map((transformedCorner) => projectScenePoint(transformedCorner, size, sceneCenterX, sceneCenterY));
|
|
15108
|
+
surfacePatches.push({
|
|
15109
|
+
corners: projectedCorners,
|
|
15110
|
+
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
15111
|
+
transformedCorners.length,
|
|
15112
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1),
|
|
15113
|
+
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
15114
|
+
outlineColor,
|
|
15115
|
+
});
|
|
15116
|
+
}
|
|
15117
|
+
}
|
|
15118
|
+
return surfacePatches;
|
|
15119
|
+
}
|
|
15120
|
+
/**
|
|
15121
|
+
* Samples one point on an ellipsoid aligned to the local axes.
|
|
15122
|
+
*
|
|
15123
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15124
|
+
*/
|
|
15125
|
+
function sampleEllipsoidPoint(radiusX, radiusY, radiusZ, latitude, longitude) {
|
|
15126
|
+
const cosineLatitude = Math.cos(latitude);
|
|
15127
|
+
return {
|
|
15128
|
+
x: Math.sin(longitude) * cosineLatitude * radiusX,
|
|
15129
|
+
y: Math.sin(latitude) * radiusY,
|
|
15130
|
+
z: Math.cos(longitude) * cosineLatitude * radiusZ,
|
|
15131
|
+
};
|
|
15132
|
+
}
|
|
15133
|
+
/**
|
|
15134
|
+
* Resolves one base fill tone for a surface patch across the octopus body.
|
|
15135
|
+
*
|
|
15136
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15137
|
+
*/
|
|
15138
|
+
function resolveSurfacePatchFillStyle(palette, verticalProgress) {
|
|
15139
|
+
const clampedVerticalProgress = clampNumber$1(verticalProgress, 0, 1);
|
|
15140
|
+
if (clampedVerticalProgress < 0.2) {
|
|
15141
|
+
return palette.highlight;
|
|
15142
|
+
}
|
|
15143
|
+
if (clampedVerticalProgress < 0.45) {
|
|
15144
|
+
return palette.secondary;
|
|
15145
|
+
}
|
|
15146
|
+
if (clampedVerticalProgress < 0.8) {
|
|
15147
|
+
return palette.primary;
|
|
15148
|
+
}
|
|
15149
|
+
return `${palette.shadow}f2`;
|
|
15150
|
+
}
|
|
15151
|
+
/**
|
|
15152
|
+
* Draws one projected mesh patch with organic shading.
|
|
15153
|
+
*
|
|
15154
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15155
|
+
*/
|
|
15156
|
+
function drawSurfacePatch(context, surfacePatch) {
|
|
15157
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
15158
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
15159
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.15 * surfacePatch.lightIntensity})`);
|
|
15160
|
+
}
|
|
15161
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
15162
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.24 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
15163
|
+
}
|
|
15164
|
+
context.save();
|
|
15165
|
+
context.beginPath();
|
|
15166
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
15167
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
15168
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
15169
|
+
}
|
|
15170
|
+
context.closePath();
|
|
15171
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
15172
|
+
context.lineWidth = Math.max(1, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0044);
|
|
15173
|
+
context.lineJoin = 'round';
|
|
15174
|
+
context.stroke();
|
|
15175
|
+
context.restore();
|
|
15176
|
+
}
|
|
15177
|
+
/**
|
|
15178
|
+
* Creates the projected 3D tentacle strokes orbiting around the lower octopus body.
|
|
15179
|
+
*
|
|
15180
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15181
|
+
*/
|
|
15182
|
+
function createOctopusTentacleStrokes(options) {
|
|
15183
|
+
const { createRandom, morphologyProfile, timeMs, size, center, radiusX, radiusY, radiusZ, rotationX, rotationY, sceneCenterX, sceneCenterY, animationPhase, } = options;
|
|
15184
|
+
return Array.from({ length: morphologyProfile.tentacles.count }, (_, tentacleIndex) => {
|
|
15185
|
+
const tentacleRandom = createRandom(`octopus3d-tentacle-${tentacleIndex}`);
|
|
15186
|
+
const spreadProgress = morphologyProfile.tentacles.count === 1 ? 0.5 : tentacleIndex / (morphologyProfile.tentacles.count - 1);
|
|
15187
|
+
const orbitAngle = -Math.PI * 0.92 + spreadProgress * Math.PI * 1.84 + (tentacleRandom() - 0.5) * 0.16;
|
|
15188
|
+
const flowLength = size * (0.19 + morphologyProfile.tentacles.flowLengthScale * 0.075 + tentacleRandom() * 0.018);
|
|
15189
|
+
const lateralReach = size *
|
|
15190
|
+
(0.08 + morphologyProfile.tentacles.lateralReachScale * 0.05 + Math.abs(Math.sin(orbitAngle)) * 0.018);
|
|
15191
|
+
const depthReach = size * (0.028 + morphologyProfile.tentacles.tipReachScale * 0.032);
|
|
15192
|
+
const sway = Math.sin(timeMs / (760 + tentacleIndex * 36) + animationPhase + tentacleRandom() * Math.PI * 2);
|
|
15193
|
+
const anchorPoint = {
|
|
15194
|
+
x: Math.sin(orbitAngle) * radiusX * (0.84 + tentacleRandom() * 0.08),
|
|
15195
|
+
y: radiusY * (0.22 + tentacleRandom() * 0.18),
|
|
15196
|
+
z: Math.cos(orbitAngle) * radiusZ * (0.72 + tentacleRandom() * 0.12),
|
|
15197
|
+
};
|
|
15198
|
+
const controlPointOne = {
|
|
15199
|
+
x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * 0.44,
|
|
15200
|
+
y: anchorPoint.y + flowLength * 0.26,
|
|
15201
|
+
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
|
|
15202
|
+
};
|
|
15203
|
+
const controlPointTwo = {
|
|
15204
|
+
x: anchorPoint.x + Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
|
|
15205
|
+
y: anchorPoint.y + flowLength * 0.66,
|
|
15206
|
+
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
|
|
15207
|
+
};
|
|
15208
|
+
const endPoint = {
|
|
15209
|
+
x: anchorPoint.x +
|
|
15210
|
+
Math.sin(orbitAngle) * lateralReach * (1.02 + morphologyProfile.tentacles.tipWidthScale * 0.12) +
|
|
15211
|
+
sway * size * 0.028,
|
|
15212
|
+
y: anchorPoint.y + flowLength,
|
|
15213
|
+
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach + sway * size * 0.016,
|
|
15214
|
+
};
|
|
15215
|
+
const scenePoints = Array.from({ length: 12 }, (_, sampleIndex) => transformScenePoint(sampleCubicBezierPoint3D(anchorPoint, controlPointOne, controlPointTwo, endPoint, sampleIndex / 11), center, rotationX, rotationY));
|
|
15216
|
+
const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
15217
|
+
const averageDepth = scenePoints.reduce((depthSum, scenePoint) => depthSum + scenePoint.z, 0) / scenePoints.length;
|
|
15218
|
+
return {
|
|
15219
|
+
projectedPoints,
|
|
15220
|
+
averageDepth,
|
|
15221
|
+
isFrontFacing: averageDepth >= center.z - size * 0.006,
|
|
15222
|
+
baseWidth: size *
|
|
15223
|
+
(0.019 +
|
|
15224
|
+
morphologyProfile.tentacles.baseWidthScale * 0.007 +
|
|
15225
|
+
tentacleRandom() * 0.003 +
|
|
15226
|
+
Math.abs(Math.sin(orbitAngle)) * 0.002),
|
|
15227
|
+
tipWidth: size * (0.0046 + morphologyProfile.tentacles.tipWidthScale * 0.0018),
|
|
15228
|
+
colorBias: tentacleRandom(),
|
|
15229
|
+
};
|
|
15230
|
+
});
|
|
15231
|
+
}
|
|
15232
|
+
/**
|
|
15233
|
+
* Samples one point on a cubic Bezier curve in 3D.
|
|
15234
|
+
*
|
|
15235
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15236
|
+
*/
|
|
15237
|
+
function sampleCubicBezierPoint3D(startPoint, controlPointOne, controlPointTwo, endPoint, progress) {
|
|
15238
|
+
const inverseProgress = 1 - progress;
|
|
15239
|
+
return {
|
|
15240
|
+
x: inverseProgress * inverseProgress * inverseProgress * startPoint.x +
|
|
15241
|
+
3 * inverseProgress * inverseProgress * progress * controlPointOne.x +
|
|
15242
|
+
3 * inverseProgress * progress * progress * controlPointTwo.x +
|
|
15243
|
+
progress * progress * progress * endPoint.x,
|
|
15244
|
+
y: inverseProgress * inverseProgress * inverseProgress * startPoint.y +
|
|
15245
|
+
3 * inverseProgress * inverseProgress * progress * controlPointOne.y +
|
|
15246
|
+
3 * inverseProgress * progress * progress * controlPointTwo.y +
|
|
15247
|
+
progress * progress * progress * endPoint.y,
|
|
15248
|
+
z: inverseProgress * inverseProgress * inverseProgress * startPoint.z +
|
|
15249
|
+
3 * inverseProgress * inverseProgress * progress * controlPointOne.z +
|
|
15250
|
+
3 * inverseProgress * progress * progress * controlPointTwo.z +
|
|
15251
|
+
progress * progress * progress * endPoint.z,
|
|
15252
|
+
};
|
|
15253
|
+
}
|
|
15254
|
+
/**
|
|
15255
|
+
* Draws one projected tentacle stroke with a slim highlight ridge.
|
|
15256
|
+
*
|
|
15257
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15258
|
+
*/
|
|
15259
|
+
function drawTentacleStroke(context, tentacleStroke, palette) {
|
|
15260
|
+
const projectedSegments = tentacleStroke.projectedPoints.length - 1;
|
|
15261
|
+
for (let segmentIndex = 0; segmentIndex < projectedSegments; segmentIndex++) {
|
|
15262
|
+
const startPoint = tentacleStroke.projectedPoints[segmentIndex];
|
|
15263
|
+
const endPoint = tentacleStroke.projectedPoints[segmentIndex + 1];
|
|
15264
|
+
const progress = segmentIndex / projectedSegments;
|
|
15265
|
+
const width = tentacleStroke.baseWidth + (tentacleStroke.tipWidth - tentacleStroke.baseWidth) * progress;
|
|
15266
|
+
context.beginPath();
|
|
15267
|
+
context.moveTo(startPoint.x, startPoint.y);
|
|
15268
|
+
context.lineTo(endPoint.x, endPoint.y);
|
|
15269
|
+
context.strokeStyle =
|
|
15270
|
+
tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
|
|
15271
|
+
context.lineWidth = width;
|
|
15272
|
+
context.lineCap = 'round';
|
|
15273
|
+
context.stroke();
|
|
15274
|
+
context.beginPath();
|
|
15275
|
+
context.moveTo(startPoint.x, startPoint.y);
|
|
15276
|
+
context.lineTo(endPoint.x, endPoint.y);
|
|
15277
|
+
context.strokeStyle = tentacleStroke.isFrontFacing ? `${palette.highlight}80` : `${palette.highlight}40`;
|
|
15278
|
+
context.lineWidth = Math.max(1, width * 0.34);
|
|
15279
|
+
context.lineCap = 'round';
|
|
15280
|
+
context.stroke();
|
|
15281
|
+
}
|
|
15282
|
+
}
|
|
15283
|
+
/**
|
|
15284
|
+
* Resolves the front surface depth on an ellipsoid for one local face point.
|
|
15285
|
+
*
|
|
15286
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15287
|
+
*/
|
|
15288
|
+
function resolveEllipsoidSurfaceDepth(radiusX, radiusY, radiusZ, x, y) {
|
|
15289
|
+
const normalizedX = x / radiusX;
|
|
15290
|
+
const normalizedY = y / radiusY;
|
|
15291
|
+
const remainingDepthRatio = Math.max(0, 1 - normalizedX * normalizedX - normalizedY * normalizedY);
|
|
15292
|
+
return Math.sqrt(remainingDepthRatio) * radiusZ;
|
|
15293
|
+
}
|
|
15294
|
+
/**
|
|
15295
|
+
* Draws one projected eye on the turned octopus mantle.
|
|
15296
|
+
*
|
|
15297
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15298
|
+
*/
|
|
15299
|
+
function drawProjectedEye(context, localCenter, radiusX, radiusY, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, phase, interaction, eyeStyle) {
|
|
15300
|
+
const centerScenePoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
15301
|
+
if (centerScenePoint.z <= center.z) {
|
|
15302
|
+
return;
|
|
15303
|
+
}
|
|
15304
|
+
const horizontalScenePoint = transformScenePoint({ x: localCenter.x + radiusX, y: localCenter.y, z: localCenter.z }, center, rotationX, rotationY);
|
|
15305
|
+
const verticalScenePoint = transformScenePoint({ x: localCenter.x, y: localCenter.y + radiusY, z: localCenter.z }, center, rotationX, rotationY);
|
|
15306
|
+
const projectedCenterPoint = projectScenePoint(centerScenePoint, size, sceneCenterX, sceneCenterY);
|
|
15307
|
+
const projectedHorizontalPoint = projectScenePoint(horizontalScenePoint, size, sceneCenterX, sceneCenterY);
|
|
15308
|
+
const projectedVerticalPoint = projectScenePoint(verticalScenePoint, size, sceneCenterX, sceneCenterY);
|
|
15309
|
+
const projectedRadiusX = Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y);
|
|
15310
|
+
const projectedRadiusY = Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y);
|
|
15311
|
+
if (projectedRadiusX < size * 0.008 || projectedRadiusY < size * 0.008) {
|
|
15312
|
+
return;
|
|
15313
|
+
}
|
|
15314
|
+
const { pupilOffsetX, pupilOffsetY } = resolveOrganicEyeMotion({
|
|
15315
|
+
radiusX: projectedRadiusX,
|
|
15316
|
+
radiusY: projectedRadiusY,
|
|
15317
|
+
timeMs,
|
|
15318
|
+
phase,
|
|
15319
|
+
interaction,
|
|
15320
|
+
});
|
|
15321
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
15322
|
+
context.save();
|
|
15323
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
15324
|
+
context.rotate(rotation);
|
|
15325
|
+
context.beginPath();
|
|
15326
|
+
context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
|
|
15327
|
+
context.fillStyle = '#f8fbff';
|
|
15328
|
+
context.fill();
|
|
15329
|
+
context.clip();
|
|
15330
|
+
const irisGradient = context.createRadialGradient(-projectedRadiusX * 0.2, -projectedRadiusY * 0.26, projectedRadiusX * 0.05, 0, 0, projectedRadiusX * 0.92);
|
|
15331
|
+
irisGradient.addColorStop(0, palette.highlight);
|
|
15332
|
+
irisGradient.addColorStop(0.56, palette.secondary);
|
|
15333
|
+
irisGradient.addColorStop(1, palette.shadow);
|
|
15334
|
+
context.beginPath();
|
|
15335
|
+
context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.62 * eyeStyle.irisScale, projectedRadiusY * 0.72 * eyeStyle.irisScale, 0, 0, Math.PI * 2);
|
|
15336
|
+
context.fillStyle = irisGradient;
|
|
15337
|
+
context.fill();
|
|
15338
|
+
context.beginPath();
|
|
15339
|
+
context.ellipse(pupilOffsetX, pupilOffsetY, projectedRadiusX * 0.15 * eyeStyle.pupilWidthScale, projectedRadiusY * 0.48 * eyeStyle.pupilHeightScale, 0, 0, Math.PI * 2);
|
|
15340
|
+
context.fillStyle = palette.ink;
|
|
15341
|
+
context.fill();
|
|
15342
|
+
context.beginPath();
|
|
15343
|
+
context.ellipse(pupilOffsetX - projectedRadiusX * 0.22, pupilOffsetY - projectedRadiusY * 0.24, projectedRadiusX * 0.12, projectedRadiusY * 0.14, 0, 0, Math.PI * 2);
|
|
15344
|
+
context.fillStyle = '#ffffff';
|
|
15345
|
+
context.fill();
|
|
15346
|
+
context.restore();
|
|
15347
|
+
context.save();
|
|
15348
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
15349
|
+
context.rotate(rotation);
|
|
15350
|
+
context.beginPath();
|
|
15351
|
+
context.ellipse(0, 0, projectedRadiusX, projectedRadiusY, 0, 0, Math.PI * 2);
|
|
15352
|
+
context.strokeStyle = `${palette.shadow}cc`;
|
|
15353
|
+
context.lineWidth = projectedRadiusX * 0.16;
|
|
15354
|
+
context.stroke();
|
|
15355
|
+
context.beginPath();
|
|
15356
|
+
context.moveTo(-projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
|
|
15357
|
+
context.quadraticCurveTo(0, -projectedRadiusY * (eyeStyle.upperLidArchRatio - interaction.gazeY * 0.16 + interaction.intensity * 0.08), projectedRadiusX * 0.88, -projectedRadiusY * eyeStyle.upperLidInsetRatio);
|
|
15358
|
+
context.strokeStyle = `${palette.shadow}73`;
|
|
15359
|
+
context.lineWidth = projectedRadiusX * 0.14;
|
|
15360
|
+
context.lineCap = 'round';
|
|
15361
|
+
context.stroke();
|
|
15362
|
+
if (eyeStyle.lowerLidOpacity > 0) {
|
|
15363
|
+
context.beginPath();
|
|
15364
|
+
context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
15365
|
+
context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
15366
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
|
|
15367
|
+
context.lineWidth = projectedRadiusX * 0.08;
|
|
15368
|
+
context.lineCap = 'round';
|
|
15369
|
+
context.stroke();
|
|
15370
|
+
}
|
|
15371
|
+
context.restore();
|
|
15372
|
+
}
|
|
15373
|
+
/**
|
|
15374
|
+
* Draws a subtle projected mouth arc across the front of the mantle.
|
|
15375
|
+
*
|
|
15376
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15377
|
+
*/
|
|
15378
|
+
function drawProjectedMouth(context, localPoints, center, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size) {
|
|
15379
|
+
const scenePoints = localPoints.map((localPoint) => transformScenePoint(localPoint, center, rotationX, rotationY));
|
|
15380
|
+
if (scenePoints.some((scenePoint) => scenePoint.z <= center.z)) {
|
|
15381
|
+
return;
|
|
15382
|
+
}
|
|
15383
|
+
const projectedPoints = scenePoints.map((scenePoint) => projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
15384
|
+
context.beginPath();
|
|
15385
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
15386
|
+
context.quadraticCurveTo(projectedPoints[1].x, projectedPoints[1].y, projectedPoints[2].x, projectedPoints[2].y);
|
|
15387
|
+
context.strokeStyle = `${palette.ink}b8`;
|
|
15388
|
+
context.lineWidth = Math.max(1.1, size * 0.009);
|
|
15389
|
+
context.lineCap = 'round';
|
|
15390
|
+
context.stroke();
|
|
15391
|
+
}
|
|
15392
|
+
/**
|
|
15393
|
+
* Draws one filled projected quad.
|
|
15394
|
+
*
|
|
15395
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15396
|
+
*/
|
|
15397
|
+
function drawProjectedQuad(context, corners, fillStyle) {
|
|
15398
|
+
context.beginPath();
|
|
15399
|
+
context.moveTo(corners[0].x, corners[0].y);
|
|
15400
|
+
context.lineTo(corners[1].x, corners[1].y);
|
|
15401
|
+
context.lineTo(corners[2].x, corners[2].y);
|
|
15402
|
+
context.lineTo(corners[3].x, corners[3].y);
|
|
15403
|
+
context.closePath();
|
|
15404
|
+
context.fillStyle = fillStyle;
|
|
15405
|
+
context.fill();
|
|
15406
|
+
}
|
|
15407
|
+
/**
|
|
15408
|
+
* Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
|
|
15409
|
+
*
|
|
15410
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
15411
|
+
*/
|
|
15412
|
+
function formatAlphaHex(opacity) {
|
|
15413
|
+
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
15414
|
+
.toString(16)
|
|
15415
|
+
.padStart(2, '0');
|
|
15416
|
+
}
|
|
15417
|
+
|
|
14901
15418
|
/* eslint-disable no-magic-numbers */
|
|
14902
15419
|
/**
|
|
14903
15420
|
* Octopus avatar visual.
|
|
@@ -15666,6 +16183,7 @@ const AVATAR_VISUALS = [
|
|
|
15666
16183
|
octopusAvatarVisual,
|
|
15667
16184
|
octopus2AvatarVisual,
|
|
15668
16185
|
octopus3AvatarVisual,
|
|
16186
|
+
octopus3dAvatarVisual,
|
|
15669
16187
|
asciiOctopusAvatarVisual,
|
|
15670
16188
|
minecraftAvatarVisual,
|
|
15671
16189
|
minecraft2AvatarVisual,
|
|
@@ -17859,16 +18377,22 @@ function resolveTeamTeammateLabels(teamContent, teammates) {
|
|
|
17859
18377
|
* Each teammate is listed with its tool name, TEAM instructions, and optional profile hints.
|
|
17860
18378
|
*/
|
|
17861
18379
|
function buildTeamSystemMessageBody(teamEntries) {
|
|
17862
|
-
const
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
|
|
17868
|
-
|
|
17869
|
-
|
|
17870
|
-
|
|
17871
|
-
|
|
18380
|
+
const teammateSections = teamEntries.map((entry, index) => {
|
|
18381
|
+
const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
|
|
18382
|
+
const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
|
|
18383
|
+
if (detailLines.length === 0) {
|
|
18384
|
+
return toolLine;
|
|
18385
|
+
}
|
|
18386
|
+
return spaceTrim$1((block) => `
|
|
18387
|
+
${toolLine}
|
|
18388
|
+
${block(detailLines.join('\n'))}
|
|
18389
|
+
`);
|
|
18390
|
+
});
|
|
18391
|
+
return spaceTrim$1((block) => `
|
|
18392
|
+
${block(TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES.join('\n'))}
|
|
18393
|
+
|
|
18394
|
+
${block(teammateSections.join('\n\n'))}
|
|
18395
|
+
`);
|
|
17872
18396
|
}
|
|
17873
18397
|
/**
|
|
17874
18398
|
* Builds the model-visible description for one teammate tool.
|
|
@@ -30996,7 +31520,11 @@ function createExampleInteractionsContent(parseResult, samples) {
|
|
|
30996
31520
|
if (examples.length === 0) {
|
|
30997
31521
|
return null;
|
|
30998
31522
|
}
|
|
30999
|
-
return
|
|
31523
|
+
return spaceTrim$1((block) => `
|
|
31524
|
+
## Sample of communication with the agent:
|
|
31525
|
+
|
|
31526
|
+
${block(examples.join('\n\n'))}
|
|
31527
|
+
`);
|
|
31000
31528
|
}
|
|
31001
31529
|
/**
|
|
31002
31530
|
* Collects the individual lines used in the example interaction section.
|
|
@@ -31033,7 +31561,11 @@ function collectExampleInteractionLines(parseResult, samples) {
|
|
|
31033
31561
|
function appendSystemMessageSection(requirements, section) {
|
|
31034
31562
|
return {
|
|
31035
31563
|
...requirements,
|
|
31036
|
-
systemMessage:
|
|
31564
|
+
systemMessage: spaceTrim$1((block) => `
|
|
31565
|
+
${block(requirements.systemMessage)}
|
|
31566
|
+
|
|
31567
|
+
${block(section)}
|
|
31568
|
+
`),
|
|
31037
31569
|
};
|
|
31038
31570
|
}
|
|
31039
31571
|
/**
|