@promptbook/browser 0.112.0-99 → 0.112.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 +37 -21
- package/esm/index.es.js +1683 -94
- package/esm/index.es.js.map +1 -1
- package/esm/src/_packages/components.index.d.ts +4 -0
- package/esm/src/_packages/core.index.d.ts +0 -2
- package/esm/src/_packages/node.index.d.ts +40 -0
- package/esm/src/_packages/types.index.d.ts +18 -0
- package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/esm/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
- package/esm/src/book-3.0/CliAgent.d.ts +66 -0
- package/esm/src/book-3.0/CliAgent.test.d.ts +1 -0
- package/esm/src/book-3.0/LiteAgent.d.ts +68 -0
- package/esm/src/book-3.0/LiteAgent.test.d.ts +1 -0
- package/esm/src/book-3.0/agentFolderPaths.d.ts +30 -0
- package/esm/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/esm/src/book-components/BookEditor/BookEditor.d.ts +6 -5
- package/esm/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
- package/esm/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
- package/esm/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
- package/esm/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
- package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
- package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
- package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
- package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
- package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
- package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
- package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
- package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
- package/esm/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
- package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
- package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
- package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
- package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
- package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
- package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
- package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
- package/esm/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
- package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
- package/esm/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
- package/esm/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
- package/esm/src/book-components/_common/Modal/Modal.d.ts +1 -1
- package/esm/src/book-components/icons/AboutIcon.d.ts +1 -1
- package/esm/src/book-components/icons/DownloadIcon.d.ts +1 -1
- package/esm/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
- package/esm/src/book-components/icons/FullscreenIcon.d.ts +1 -1
- package/esm/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
- package/esm/src/cli/cli-commands/agent/chat.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent/exec.d.ts +10 -0
- package/esm/src/cli/cli-commands/agent/run.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
- package/esm/src/cli/cli-commands/agent.d.ts +14 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +17 -1
- package/esm/src/cli/cli-commands/agents-server/run.d.ts +6 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
- package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
- package/esm/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
- package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
- package/esm/src/cli/cli-commands/coder/server.d.ts +13 -0
- package/esm/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
- package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/esm/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
- package/esm/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
- package/esm/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
- package/esm/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
- package/esm/src/utils/isTimingSafeEqualString.d.ts +25 -0
- package/esm/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +1686 -95
- package/umd/index.umd.js.map +1 -1
- package/umd/src/_packages/components.index.d.ts +4 -0
- package/umd/src/_packages/core.index.d.ts +0 -2
- package/umd/src/_packages/node.index.d.ts +40 -0
- package/umd/src/_packages/types.index.d.ts +18 -0
- package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopus3d4AvatarVisual.d.ts +7 -0
- package/umd/src/book-3.0/BookNodeAgentSource.d.ts +38 -0
- package/umd/src/book-3.0/CliAgent.d.ts +66 -0
- package/umd/src/book-3.0/CliAgent.test.d.ts +1 -0
- package/umd/src/book-3.0/LiteAgent.d.ts +68 -0
- package/umd/src/book-3.0/LiteAgent.test.d.ts +1 -0
- package/umd/src/book-3.0/agentFolderPaths.d.ts +30 -0
- package/umd/src/book-3.0/cliAgentEnv.d.ts +33 -0
- package/umd/src/book-components/BookEditor/BookEditor.d.ts +6 -5
- package/umd/src/book-components/BookEditor/BookEditorAboutPromptbookInformation.d.ts +12 -0
- package/umd/src/book-components/BookEditor/BookEditorBrowserConfig.d.ts +2 -0
- package/umd/src/book-components/BookEditor/BookEditorForClient.d.ts +7 -0
- package/umd/src/book-components/BookEditor/BookEditorMonacoTokenization.d.ts +1 -0
- package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
- package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +9 -0
- package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
- package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
- package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
- package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
- package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +20 -0
- package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
- package/umd/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
- package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
- package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
- package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
- package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
- package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
- package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
- package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
- package/umd/src/book-components/Chat/utils/isVisibleChatToolCall.d.ts +10 -0
- package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
- package/umd/src/book-components/_common/Dropdown/Dropdown.d.ts +1 -1
- package/umd/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +1 -1
- package/umd/src/book-components/_common/Modal/Modal.d.ts +1 -1
- package/umd/src/book-components/icons/AboutIcon.d.ts +1 -1
- package/umd/src/book-components/icons/DownloadIcon.d.ts +1 -1
- package/umd/src/book-components/icons/ExitFullscreenIcon.d.ts +1 -1
- package/umd/src/book-components/icons/FullscreenIcon.d.ts +1 -1
- package/umd/src/cli/cli-commands/agent/agentCliOptions.d.ts +38 -0
- package/umd/src/cli/cli-commands/agent/chat.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent/exec.d.ts +10 -0
- package/umd/src/cli/cli-commands/agent/run.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +2 -24
- package/umd/src/cli/cli-commands/agent.d.ts +14 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +17 -1
- package/umd/src/cli/cli-commands/agents-server/run.d.ts +6 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +9 -2
- package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/ensureCoderDeveloperAgentFile.d.ts +25 -0
- package/umd/src/cli/cli-commands/coder/find-unwritten.d.ts +10 -0
- package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +2 -0
- package/umd/src/cli/cli-commands/coder/server.d.ts +13 -0
- package/umd/src/cli/cli-commands/coder/waitOptions.d.ts +14 -0
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +9 -25
- package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/umd/src/commitments/_common/teamInternalAgentAccess.d.ts +9 -1
- package/umd/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
- package/umd/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
- package/umd/src/scrapers/website/utils/createShowdownConverter.d.ts +2 -2
- package/umd/src/utils/isTimingSafeEqualString.d.ts +25 -0
- package/umd/src/utils/validators/url/isValidAgentUrl.d.ts +5 -0
- package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('spacetrim'), require('destroyable'), require('rxjs'), require('crypto
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'spacetrim', 'destroyable', 'rxjs', 'crypto
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-browser"] = {}, global.spacetrim, global.destroyable, global.rxjs, global.
|
|
5
|
-
})(this, (function (exports, spacetrim, destroyable, rxjs,
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('spacetrim'), require('destroyable'), require('rxjs'), require('crypto-js'), require('crypto-js/enc-hex'), require('path'), require('crypto'), require('moment'), require('mime-types'), require('waitasecond'), require('crypto-js/sha256'), require('papaparse'), require('@openai/agents'), require('colors'), require('bottleneck'), require('openai')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'spacetrim', 'destroyable', 'rxjs', 'crypto-js', 'crypto-js/enc-hex', 'path', 'crypto', 'moment', 'mime-types', 'waitasecond', 'crypto-js/sha256', 'papaparse', '@openai/agents', 'colors', 'bottleneck', 'openai'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-browser"] = {}, global.spacetrim, global.destroyable, global.rxjs, global.CryptoJS, global.hexEncoder, global.path, null, global.moment, global.mimeTypes, global.waitasecond, global.sha256, global.papaparse, global.agents, global.colors, global.Bottleneck, global.OpenAI));
|
|
5
|
+
})(this, (function (exports, spacetrim, destroyable, rxjs, CryptoJS, hexEncoder, path, crypto, moment, mimeTypes, waitasecond, sha256, papaparse, agents, colors, Bottleneck, OpenAI) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
8
|
|
|
9
|
+
var CryptoJS__default = /*#__PURE__*/_interopDefaultLegacy(CryptoJS);
|
|
9
10
|
var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
|
|
10
11
|
var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
|
|
11
12
|
var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
|
|
13
|
+
var papaparse__default = /*#__PURE__*/_interopDefaultLegacy(papaparse);
|
|
12
14
|
var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
|
|
13
15
|
var Bottleneck__default = /*#__PURE__*/_interopDefaultLegacy(Bottleneck);
|
|
14
16
|
var OpenAI__default = /*#__PURE__*/_interopDefaultLegacy(OpenAI);
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
* @generated
|
|
28
30
|
* @see https://github.com/webgptorg/promptbook
|
|
29
31
|
*/
|
|
30
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0
|
|
32
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0';
|
|
31
33
|
/**
|
|
32
34
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
33
35
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2330,7 +2332,7 @@
|
|
|
2330
2332
|
* @public exported from `@promptbook/utils`
|
|
2331
2333
|
*/
|
|
2332
2334
|
function computeHash(value) {
|
|
2333
|
-
return
|
|
2335
|
+
return CryptoJS__default["default"].SHA256(hexEncoder__default["default"].parse(spacetrim.spaceTrim(valueToString(value)))).toString( /* hex */);
|
|
2334
2336
|
}
|
|
2335
2337
|
// TODO: [🥬][🥬] Use this ACRY
|
|
2336
2338
|
|
|
@@ -2603,6 +2605,11 @@
|
|
|
2603
2605
|
* - `isValidAgentUrl` *(this one)* which tests just agent URL
|
|
2604
2606
|
* - `isValidPipelineUrl` which tests just pipeline URL
|
|
2605
2607
|
*
|
|
2608
|
+
* Note: This is a pure structural validator and does not block private/internal network
|
|
2609
|
+
* addresses. Callers that fetch the URL server-side from an untrusted network context must
|
|
2610
|
+
* additionally apply the `assertSafeUrl` SSRF guard from the Agents Server before any
|
|
2611
|
+
* outbound request.
|
|
2612
|
+
*
|
|
2606
2613
|
* @public exported from `@promptbook/utils`
|
|
2607
2614
|
*/
|
|
2608
2615
|
function isValidAgentUrl(url) {
|
|
@@ -2616,12 +2623,6 @@
|
|
|
2616
2623
|
// TODO: [🐠]
|
|
2617
2624
|
return false;
|
|
2618
2625
|
}
|
|
2619
|
-
/*
|
|
2620
|
-
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
2621
|
-
if (isUrlOnPrivateNetwork(url)) {
|
|
2622
|
-
return false;
|
|
2623
|
-
}
|
|
2624
|
-
*/
|
|
2625
2626
|
return true;
|
|
2626
2627
|
}
|
|
2627
2628
|
// TODO: [🐠] Maybe more info why the URL is invalid
|
|
@@ -2797,8 +2798,11 @@
|
|
|
2797
2798
|
if (!trimmedContent) {
|
|
2798
2799
|
return requirements;
|
|
2799
2800
|
}
|
|
2800
|
-
|
|
2801
|
-
|
|
2801
|
+
const goalSection = spacetrim.spaceTrim((block) => `
|
|
2802
|
+
## Goal
|
|
2803
|
+
|
|
2804
|
+
${block(trimmedContent)}
|
|
2805
|
+
`);
|
|
2802
2806
|
const requirementsWithGoal = this.appendToSystemMessage(requirements, goalSection, '\n\n');
|
|
2803
2807
|
return this.appendToPromptSuffix(requirementsWithGoal, trimmedContent);
|
|
2804
2808
|
}
|
|
@@ -3692,8 +3696,11 @@
|
|
|
3692
3696
|
if (!trimmedContent) {
|
|
3693
3697
|
return requirements;
|
|
3694
3698
|
}
|
|
3695
|
-
|
|
3696
|
-
|
|
3699
|
+
const languageSection = spacetrim.spaceTrim((block) => `
|
|
3700
|
+
## Language
|
|
3701
|
+
|
|
3702
|
+
- Your language is ${block(trimmedContent)}
|
|
3703
|
+
`);
|
|
3697
3704
|
return this.appendToSystemMessage(requirements, languageSection, '\n\n');
|
|
3698
3705
|
}
|
|
3699
3706
|
}
|
|
@@ -6159,21 +6166,22 @@
|
|
|
6159
6166
|
* @private helper of `fractalAvatarVisual`
|
|
6160
6167
|
*/
|
|
6161
6168
|
function drawDragonCurveLayer(context, points, options) {
|
|
6162
|
-
const {
|
|
6169
|
+
const { primaryColor, secondaryColor, tertiaryColor, shadowColor, strokeWidth, timeMs, layerIndex } = options;
|
|
6163
6170
|
const firstPoint = points[0];
|
|
6164
6171
|
const lastPoint = points[points.length - 1];
|
|
6165
6172
|
const ribbonGradient = context.createLinearGradient(firstPoint.x, firstPoint.y, lastPoint.x, lastPoint.y);
|
|
6166
6173
|
ribbonGradient.addColorStop(0, `${primaryColor}f2`);
|
|
6167
6174
|
ribbonGradient.addColorStop(0.5, `${secondaryColor}e6`);
|
|
6168
6175
|
ribbonGradient.addColorStop(1, `${tertiaryColor}f2`);
|
|
6176
|
+
// Approximate the blurred shadow stroke with a wider semi-transparent stroke instead of
|
|
6177
|
+
// context.filter blur, which triggers a costly software rasterization pass every frame.
|
|
6169
6178
|
context.save();
|
|
6170
6179
|
context.beginPath();
|
|
6171
6180
|
tracePolyline(context, points);
|
|
6172
|
-
context.strokeStyle = `${shadowColor}
|
|
6173
|
-
context.lineWidth = strokeWidth *
|
|
6181
|
+
context.strokeStyle = `${shadowColor}48`;
|
|
6182
|
+
context.lineWidth = strokeWidth * 4.5;
|
|
6174
6183
|
context.lineJoin = 'round';
|
|
6175
6184
|
context.lineCap = 'round';
|
|
6176
|
-
context.filter = `blur(${size * 0.022}px)`;
|
|
6177
6185
|
context.stroke();
|
|
6178
6186
|
context.restore();
|
|
6179
6187
|
context.beginPath();
|
|
@@ -6635,7 +6643,7 @@
|
|
|
6635
6643
|
*
|
|
6636
6644
|
* @private helper of `minecraft2AvatarVisual`
|
|
6637
6645
|
*/
|
|
6638
|
-
const LIGHT_DIRECTION$
|
|
6646
|
+
const LIGHT_DIRECTION$4 = normalizeVector3({
|
|
6639
6647
|
x: 0.4,
|
|
6640
6648
|
y: -0.65,
|
|
6641
6649
|
z: 0.92,
|
|
@@ -6757,11 +6765,23 @@
|
|
|
6757
6765
|
* @private helper of `minecraft2AvatarVisual`
|
|
6758
6766
|
*/
|
|
6759
6767
|
function drawMinecraftShadow(context, size, palette, interaction, timeMs) {
|
|
6768
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.03;
|
|
6769
|
+
const cy = size * 0.85 + Math.sin(timeMs / 880) * size * 0.01;
|
|
6770
|
+
const rx = size * (0.16 + interaction.intensity * 0.015);
|
|
6771
|
+
const ry = size * 0.055;
|
|
6772
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
6760
6773
|
context.save();
|
|
6761
|
-
context.
|
|
6762
|
-
context.
|
|
6774
|
+
context.translate(cx, cy);
|
|
6775
|
+
context.scale(1, ry / rx);
|
|
6776
|
+
const blurRadius = rx * 1.4;
|
|
6777
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
6778
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
6779
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
6780
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
6781
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
6782
|
+
context.fillStyle = shadowGradient;
|
|
6763
6783
|
context.beginPath();
|
|
6764
|
-
context.
|
|
6784
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
6765
6785
|
context.fill();
|
|
6766
6786
|
context.restore();
|
|
6767
6787
|
}
|
|
@@ -6847,7 +6867,7 @@
|
|
|
6847
6867
|
corners: projectedCorners,
|
|
6848
6868
|
texture: faceDefinition.texture,
|
|
6849
6869
|
averageDepth: transformedCorners.reduce((depthSum, corner) => depthSum + corner.z, 0) / transformedCorners.length,
|
|
6850
|
-
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$
|
|
6870
|
+
lightIntensity: clampNumber$1(dotProduct3D(faceNormal, LIGHT_DIRECTION$4), -1, 1),
|
|
6851
6871
|
outlineColor: cuboid.outlineColor,
|
|
6852
6872
|
};
|
|
6853
6873
|
});
|
|
@@ -6985,13 +7005,27 @@
|
|
|
6985
7005
|
spotlight.addColorStop(1, `${palette.highlight}00`);
|
|
6986
7006
|
context.fillStyle = spotlight;
|
|
6987
7007
|
context.fillRect(0, 0, size, size);
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
7008
|
+
{
|
|
7009
|
+
// Radial gradient approximates the blurry ellipse shadow without context.filter blur.
|
|
7010
|
+
const cx = size * 0.5;
|
|
7011
|
+
const cy = size * 0.86;
|
|
7012
|
+
const rx = size * 0.2;
|
|
7013
|
+
const ry = size * 0.06;
|
|
7014
|
+
const blurRadius = rx * 1.4;
|
|
7015
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
7016
|
+
shadowGradient.addColorStop(0, 'rgba(0,0,0,0.28)');
|
|
7017
|
+
shadowGradient.addColorStop(0.45, 'rgba(0,0,0,0.14)');
|
|
7018
|
+
shadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.05)');
|
|
7019
|
+
shadowGradient.addColorStop(1, 'rgba(0,0,0,0)');
|
|
7020
|
+
context.save();
|
|
7021
|
+
context.translate(cx, cy);
|
|
7022
|
+
context.scale(1, ry / rx);
|
|
7023
|
+
context.fillStyle = shadowGradient;
|
|
7024
|
+
context.beginPath();
|
|
7025
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
7026
|
+
context.fill();
|
|
7027
|
+
context.restore();
|
|
7028
|
+
}
|
|
6995
7029
|
drawVoxelCuboid(context, {
|
|
6996
7030
|
x: bodyX,
|
|
6997
7031
|
y: bodyY,
|
|
@@ -7520,7 +7554,7 @@
|
|
|
7520
7554
|
*
|
|
7521
7555
|
* @private helper of `octopus3AvatarVisual`
|
|
7522
7556
|
*/
|
|
7523
|
-
function formatAlphaHex$
|
|
7557
|
+
function formatAlphaHex$2(opacity) {
|
|
7524
7558
|
return Math.round(Math.min(1, Math.max(0, opacity)) * 255)
|
|
7525
7559
|
.toString(16)
|
|
7526
7560
|
.padStart(2, '0');
|
|
@@ -7899,7 +7933,7 @@
|
|
|
7899
7933
|
context.beginPath();
|
|
7900
7934
|
context.moveTo(-radiusX * 0.74, radiusY * 0.2);
|
|
7901
7935
|
context.quadraticCurveTo(0, radiusY * 0.38, radiusX * 0.74, radiusY * 0.2);
|
|
7902
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex$
|
|
7936
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$2(eyeStyle.lowerLidOpacity)}`;
|
|
7903
7937
|
context.lineWidth = radiusX * 0.08;
|
|
7904
7938
|
context.lineCap = 'round';
|
|
7905
7939
|
context.stroke();
|
|
@@ -7980,7 +8014,7 @@
|
|
|
7980
8014
|
context.beginPath();
|
|
7981
8015
|
context.moveTo(-projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
7982
8016
|
context.quadraticCurveTo(0, projectedRadiusY * 0.38, projectedRadiusX * 0.74, projectedRadiusY * 0.2);
|
|
7983
|
-
context.strokeStyle = `${palette.highlight}${formatAlphaHex(eyeStyle.lowerLidOpacity)}`;
|
|
8017
|
+
context.strokeStyle = `${palette.highlight}${formatAlphaHex$1(eyeStyle.lowerLidOpacity)}`;
|
|
7984
8018
|
context.lineWidth = projectedRadiusX * 0.08;
|
|
7985
8019
|
context.lineCap = 'round';
|
|
7986
8020
|
context.stroke();
|
|
@@ -8026,7 +8060,7 @@
|
|
|
8026
8060
|
*
|
|
8027
8061
|
* @private helper of the 3D octopus avatar visuals
|
|
8028
8062
|
*/
|
|
8029
|
-
function formatAlphaHex(opacity) {
|
|
8063
|
+
function formatAlphaHex$1(opacity) {
|
|
8030
8064
|
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
8031
8065
|
.toString(16)
|
|
8032
8066
|
.padStart(2, '0');
|
|
@@ -8038,11 +8072,40 @@
|
|
|
8038
8072
|
*
|
|
8039
8073
|
* @private helper of `octopus3dAvatarVisual`
|
|
8040
8074
|
*/
|
|
8041
|
-
const LIGHT_DIRECTION$
|
|
8075
|
+
const LIGHT_DIRECTION$3 = normalizeVector3({
|
|
8042
8076
|
x: 0.48,
|
|
8043
8077
|
y: -0.62,
|
|
8044
8078
|
z: 0.94,
|
|
8045
8079
|
});
|
|
8080
|
+
/**
|
|
8081
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
8082
|
+
*
|
|
8083
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
8084
|
+
*/
|
|
8085
|
+
const octopus3dStableStateCache = new WeakMap();
|
|
8086
|
+
/**
|
|
8087
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
8088
|
+
*
|
|
8089
|
+
* @private helper of `octopus3dAvatarVisual`
|
|
8090
|
+
*/
|
|
8091
|
+
function getOctopus3dStableState(createRandom) {
|
|
8092
|
+
const cached = octopus3dStableStateCache.get(createRandom);
|
|
8093
|
+
if (cached !== undefined) {
|
|
8094
|
+
return cached;
|
|
8095
|
+
}
|
|
8096
|
+
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
8097
|
+
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
8098
|
+
const leftEyePhaseOffset = eyeRandom() * 0.6;
|
|
8099
|
+
const rightEyePhaseOffset = eyeRandom() * 0.6;
|
|
8100
|
+
const state = {
|
|
8101
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
8102
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
8103
|
+
leftEyePhaseOffset,
|
|
8104
|
+
rightEyePhaseOffset,
|
|
8105
|
+
};
|
|
8106
|
+
octopus3dStableStateCache.set(createRandom, state);
|
|
8107
|
+
return state;
|
|
8108
|
+
}
|
|
8046
8109
|
/**
|
|
8047
8110
|
* Proper 3D Octopus visual built from projected organic meshes and tentacles.
|
|
8048
8111
|
*
|
|
@@ -8055,10 +8118,7 @@
|
|
|
8055
8118
|
isAnimated: true,
|
|
8056
8119
|
supportsPointerTracking: true,
|
|
8057
8120
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
8058
|
-
const morphologyProfile =
|
|
8059
|
-
const animationRandom = createRandom('octopus3d-animation-profile');
|
|
8060
|
-
const eyeRandom = createRandom('octopus3d-eye-profile');
|
|
8061
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
8121
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3dStableState(createRandom);
|
|
8062
8122
|
const sceneCenterX = size * 0.5;
|
|
8063
8123
|
const sceneCenterY = size * 0.56;
|
|
8064
8124
|
const bob = Math.sin(timeMs / 920 + animationPhase) * size * 0.014;
|
|
@@ -8155,12 +8215,12 @@
|
|
|
8155
8215
|
x: -faceEyeSpacing,
|
|
8156
8216
|
y: faceEyeYOffset,
|
|
8157
8217
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -faceEyeSpacing, faceEyeYOffset),
|
|
8158
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
8218
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
8159
8219
|
drawProjectedOrganicEye(context, {
|
|
8160
8220
|
x: faceEyeSpacing,
|
|
8161
8221
|
y: faceEyeYOffset,
|
|
8162
8222
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
8163
|
-
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 +
|
|
8223
|
+
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
8164
8224
|
drawProjectedOrganicMouth(context, [
|
|
8165
8225
|
{
|
|
8166
8226
|
x: -mouthHalfWidth,
|
|
@@ -8204,14 +8264,28 @@
|
|
|
8204
8264
|
/**
|
|
8205
8265
|
* Draws the soft ground shadow below the octopus.
|
|
8206
8266
|
*
|
|
8267
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
8268
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
8269
|
+
*
|
|
8207
8270
|
* @private helper of `octopus3dAvatarVisual`
|
|
8208
8271
|
*/
|
|
8209
8272
|
function drawOctopus3dShadow(context, size, palette, interaction, timeMs) {
|
|
8273
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.04;
|
|
8274
|
+
const cy = size * 0.87 + Math.sin(timeMs / 920) * size * 0.008;
|
|
8275
|
+
const rx = size * (0.18 + interaction.intensity * 0.02);
|
|
8276
|
+
const ry = size * 0.06;
|
|
8210
8277
|
context.save();
|
|
8211
|
-
context.
|
|
8212
|
-
context.
|
|
8278
|
+
context.translate(cx, cy);
|
|
8279
|
+
context.scale(1, ry / rx);
|
|
8280
|
+
const blurRadius = rx * 1.4;
|
|
8281
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
8282
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
8283
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
8284
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
8285
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
8286
|
+
context.fillStyle = shadowGradient;
|
|
8213
8287
|
context.beginPath();
|
|
8214
|
-
context.
|
|
8288
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
8215
8289
|
context.fill();
|
|
8216
8290
|
context.restore();
|
|
8217
8291
|
}
|
|
@@ -8248,7 +8322,7 @@
|
|
|
8248
8322
|
corners: projectedCorners,
|
|
8249
8323
|
averageDepth: transformedCorners.reduce((depthSum, transformedCorner) => depthSum + transformedCorner.z, 0) /
|
|
8250
8324
|
transformedCorners.length,
|
|
8251
|
-
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$
|
|
8325
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$3), -1, 1),
|
|
8252
8326
|
fillStyle: resolveSurfacePatchFillStyle(palette, verticalProgress + verticalColorBias),
|
|
8253
8327
|
outlineColor,
|
|
8254
8328
|
});
|
|
@@ -8437,11 +8511,40 @@
|
|
|
8437
8511
|
*
|
|
8438
8512
|
* @private helper of `octopus3d2AvatarVisual`
|
|
8439
8513
|
*/
|
|
8440
|
-
const LIGHT_DIRECTION = normalizeVector3({
|
|
8514
|
+
const LIGHT_DIRECTION$2 = normalizeVector3({
|
|
8441
8515
|
x: 0.38,
|
|
8442
8516
|
y: -0.6,
|
|
8443
8517
|
z: 0.98,
|
|
8444
8518
|
});
|
|
8519
|
+
/**
|
|
8520
|
+
* Cache keyed by the `createRandom` factory reference (stable per mounted `<Avatar/>`).
|
|
8521
|
+
*
|
|
8522
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
8523
|
+
*/
|
|
8524
|
+
const octopus3d2StableStateCache = new WeakMap();
|
|
8525
|
+
/**
|
|
8526
|
+
* Returns the stable per-avatar state, computing it on first access and caching for subsequent frames.
|
|
8527
|
+
*
|
|
8528
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
8529
|
+
*/
|
|
8530
|
+
function getOctopus3d2StableState(createRandom) {
|
|
8531
|
+
const cached = octopus3d2StableStateCache.get(createRandom);
|
|
8532
|
+
if (cached !== undefined) {
|
|
8533
|
+
return cached;
|
|
8534
|
+
}
|
|
8535
|
+
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
8536
|
+
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
8537
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
8538
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
8539
|
+
const state = {
|
|
8540
|
+
morphologyProfile: createOctopus3MorphologyProfile(createRandom),
|
|
8541
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
8542
|
+
leftEyePhaseOffset,
|
|
8543
|
+
rightEyePhaseOffset,
|
|
8544
|
+
};
|
|
8545
|
+
octopus3d2StableStateCache.set(createRandom, state);
|
|
8546
|
+
return state;
|
|
8547
|
+
}
|
|
8445
8548
|
/**
|
|
8446
8549
|
* Octopus 3D 2 avatar visual.
|
|
8447
8550
|
*
|
|
@@ -8454,10 +8557,7 @@
|
|
|
8454
8557
|
isAnimated: true,
|
|
8455
8558
|
supportsPointerTracking: true,
|
|
8456
8559
|
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
8457
|
-
const morphologyProfile =
|
|
8458
|
-
const animationRandom = createRandom('octopus3d2-animation-profile');
|
|
8459
|
-
const eyeRandom = createRandom('octopus3d2-eye-profile');
|
|
8460
|
-
const animationPhase = animationRandom() * Math.PI * 2;
|
|
8560
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset } = getOctopus3d2StableState(createRandom);
|
|
8461
8561
|
const sceneCenterX = size * 0.5;
|
|
8462
8562
|
const sceneCenterY = size * 0.575;
|
|
8463
8563
|
const bob = Math.sin(timeMs / 940 + animationPhase) * size * 0.013;
|
|
@@ -8510,8 +8610,8 @@
|
|
|
8510
8610
|
const rightEyeLocalCenter = sampleBlobbyOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude);
|
|
8511
8611
|
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
8512
8612
|
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
8513
|
-
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase +
|
|
8514
|
-
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 +
|
|
8613
|
+
drawProjectedOrganicEye(context, leftEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
8614
|
+
drawProjectedOrganicEye(context, rightEyeLocalCenter, eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.9 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
8515
8615
|
drawProjectedOrganicMouth(context, [
|
|
8516
8616
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
8517
8617
|
sampleBlobbyOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
@@ -8540,54 +8640,118 @@
|
|
|
8540
8640
|
/**
|
|
8541
8641
|
* Draws the soft floor shadow that anchors the single mesh in the frame.
|
|
8542
8642
|
*
|
|
8643
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
8644
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
8645
|
+
*
|
|
8543
8646
|
* @private helper of `octopus3d2AvatarVisual`
|
|
8544
8647
|
*/
|
|
8545
8648
|
function drawBlobbyOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
8649
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
8650
|
+
const cy = size * 0.88 + Math.sin(timeMs / 940) * size * 0.008;
|
|
8651
|
+
const rx = size * (0.18 + (morphologyProfile.body.horizontalStretch - 1) * 0.04 + interaction.intensity * 0.018);
|
|
8652
|
+
const ry = size * 0.062;
|
|
8546
8653
|
context.save();
|
|
8547
|
-
context.
|
|
8548
|
-
context.
|
|
8654
|
+
context.translate(cx, cy);
|
|
8655
|
+
context.scale(1, ry / rx);
|
|
8656
|
+
const blurRadius = rx * 1.4;
|
|
8657
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
8658
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
8659
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
8660
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
8661
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
8662
|
+
context.fillStyle = shadowGradient;
|
|
8549
8663
|
context.beginPath();
|
|
8550
|
-
context.
|
|
8664
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
8551
8665
|
context.fill();
|
|
8552
8666
|
context.restore();
|
|
8553
8667
|
}
|
|
8668
|
+
/**
|
|
8669
|
+
* Number of latitude segments used by the single blobby octopus mesh.
|
|
8670
|
+
*
|
|
8671
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
8672
|
+
*/
|
|
8673
|
+
const LATITUDE_PATCH_COUNT$2 = 12;
|
|
8674
|
+
/**
|
|
8675
|
+
* Number of longitude segments used by the single blobby octopus mesh.
|
|
8676
|
+
*
|
|
8677
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
8678
|
+
*/
|
|
8679
|
+
const LONGITUDE_PATCH_COUNT$2 = 24;
|
|
8554
8680
|
/**
|
|
8555
8681
|
* Resolves all visible projected patches for the single blobby octopus mesh.
|
|
8556
8682
|
*
|
|
8683
|
+
* Within a single frame, mesh corner samples and longitude-only lobe-wave values are
|
|
8684
|
+
* quantized to the patch grid and computed once each rather than re-evaluated for every
|
|
8685
|
+
* patch corner — the patch loop alone would call `sampleBlobbyOctopusSurfacePoint`
|
|
8686
|
+
* `latitudePatchCount * longitudePatchCount * 4` times without caching, even though most
|
|
8687
|
+
* corners are shared between neighboring patches.
|
|
8688
|
+
*
|
|
8557
8689
|
* @private helper of `octopus3d2AvatarVisual`
|
|
8558
8690
|
*/
|
|
8559
8691
|
function resolveVisibleBlobbyOctopusPatches(options) {
|
|
8560
8692
|
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, animationPhase, timeMs, } = options;
|
|
8561
|
-
const latitudePatchCount =
|
|
8562
|
-
const longitudePatchCount =
|
|
8693
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$2;
|
|
8694
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$2;
|
|
8563
8695
|
const surfacePatches = [];
|
|
8696
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
8697
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
8698
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
8699
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
8700
|
+
}
|
|
8701
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
8702
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
8703
|
+
}
|
|
8704
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
8705
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
8706
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveLowerLobeWave(longitudeBoundaries[boundaryIndex], morphologyProfile, animationPhase, timeMs);
|
|
8707
|
+
}
|
|
8708
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
8709
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
8710
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
8711
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
8712
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
8713
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
8714
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
8715
|
+
const cornerSample = sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
8716
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8564
8719
|
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
8565
|
-
const startLatitude =
|
|
8566
|
-
const endLatitude =
|
|
8720
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
8721
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
8567
8722
|
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
8568
8723
|
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
8724
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
8725
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
8569
8726
|
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
8570
|
-
const startLongitude =
|
|
8571
|
-
const endLongitude =
|
|
8727
|
+
const startLongitude = longitudeBoundaries[longitudeIndex];
|
|
8728
|
+
const endLongitude = longitudeBoundaries[longitudeIndex + 1];
|
|
8572
8729
|
const centerLongitude = (startLongitude + endLongitude) / 2;
|
|
8573
|
-
const
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8730
|
+
const transformedCorners = [
|
|
8731
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
8732
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
8733
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
8734
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
8578
8735
|
];
|
|
8579
|
-
const transformedCorners = localCorners.map((localCorner) => transformScenePoint(localCorner, center, rotationX, rotationY));
|
|
8580
8736
|
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
8581
8737
|
if (surfaceNormal.z <= 0.01) {
|
|
8582
8738
|
continue;
|
|
8583
8739
|
}
|
|
8584
|
-
const projectedCorners =
|
|
8740
|
+
const projectedCorners = [
|
|
8741
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
8742
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
8743
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
8744
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
8745
|
+
];
|
|
8585
8746
|
surfacePatches.push({
|
|
8586
8747
|
corners: projectedCorners,
|
|
8587
|
-
averageDepth: transformedCorners.
|
|
8588
|
-
transformedCorners.
|
|
8589
|
-
|
|
8590
|
-
|
|
8748
|
+
averageDepth: (transformedCorners[0].z +
|
|
8749
|
+
transformedCorners[1].z +
|
|
8750
|
+
transformedCorners[2].z +
|
|
8751
|
+
transformedCorners[3].z) /
|
|
8752
|
+
4,
|
|
8753
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$2), -1, 1),
|
|
8754
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, Math.max(0, Math.cos(centerLongitude)), resolveLowerLobeWave(centerLongitude, morphologyProfile, animationPhase, timeMs)),
|
|
8591
8755
|
outlineColor: verticalProgress < 0.58 ? `${palette.highlight}73` : `${palette.shadow}8a`,
|
|
8592
8756
|
});
|
|
8593
8757
|
}
|
|
@@ -8603,12 +8767,21 @@
|
|
|
8603
8767
|
* @private helper of `octopus3d2AvatarVisual`
|
|
8604
8768
|
*/
|
|
8605
8769
|
function sampleBlobbyOctopusSurfacePoint(options, latitude, longitude) {
|
|
8770
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
8771
|
+
return sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs));
|
|
8772
|
+
}
|
|
8773
|
+
/**
|
|
8774
|
+
* Samples one point on the continuous Octopus 3D 2 surface using a precomputed lower-lobe wave
|
|
8775
|
+
* to skip the per-call trig evaluation for `latitudePatchCount + 1` longitude-shared corners.
|
|
8776
|
+
*
|
|
8777
|
+
* @private helper of `octopus3d2AvatarVisual`
|
|
8778
|
+
*/
|
|
8779
|
+
function sampleBlobbyOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, lowerLobeWave) {
|
|
8606
8780
|
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
8607
8781
|
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
8608
8782
|
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
8609
8783
|
const upperBlend = Math.pow(1 - verticalProgress, 1.2);
|
|
8610
8784
|
const lowerBlend = Math.pow(verticalProgress, 1.42);
|
|
8611
|
-
const lowerLobeWave = resolveLowerLobeWave(longitude, morphologyProfile, animationPhase, timeMs);
|
|
8612
8785
|
const skirtEnvelope = Math.pow(cosineLatitude, 0.5) * lowerBlend;
|
|
8613
8786
|
const horizontalScale = 1.02 +
|
|
8614
8787
|
skirtEnvelope * (0.34 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.22 + lowerLobeWave * 0.22) -
|
|
@@ -8646,7 +8819,7 @@
|
|
|
8646
8819
|
*
|
|
8647
8820
|
* @private helper of `octopus3d2AvatarVisual`
|
|
8648
8821
|
*/
|
|
8649
|
-
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
8822
|
+
function resolveBlobbySurfacePatchFillStyle$1(palette, verticalProgress, forwardness, lowerLobeWave) {
|
|
8650
8823
|
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.12 - forwardness * 0.07, 0, 1);
|
|
8651
8824
|
if (tonalProgress < 0.16) {
|
|
8652
8825
|
return palette.highlight;
|
|
@@ -8686,6 +8859,1384 @@
|
|
|
8686
8859
|
context.restore();
|
|
8687
8860
|
}
|
|
8688
8861
|
|
|
8862
|
+
/* eslint-disable no-magic-numbers */
|
|
8863
|
+
/**
|
|
8864
|
+
* Light direction used by the continuous octopus mesh shading.
|
|
8865
|
+
*
|
|
8866
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
8867
|
+
*/
|
|
8868
|
+
const LIGHT_DIRECTION$1 = normalizeVector3({
|
|
8869
|
+
x: 0.34,
|
|
8870
|
+
y: -0.62,
|
|
8871
|
+
z: 1,
|
|
8872
|
+
});
|
|
8873
|
+
/**
|
|
8874
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
8875
|
+
*
|
|
8876
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
8877
|
+
*/
|
|
8878
|
+
const OCTOPUS_TENTACLE_COUNT$1 = 8;
|
|
8879
|
+
/**
|
|
8880
|
+
* Cache keyed by the `createRandom` factory reference, which is stable for the lifetime of one
|
|
8881
|
+
* mounted `<Avatar/>` component (created inside `resolveAvatarRenderDefinition` and held in a
|
|
8882
|
+
* React `useMemo`). Using a `WeakMap` ensures the entry is collected when the component unmounts.
|
|
8883
|
+
*
|
|
8884
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
8885
|
+
*/
|
|
8886
|
+
const stableStateCache = new WeakMap();
|
|
8887
|
+
/**
|
|
8888
|
+
* Returns the stable per-avatar state, computing it on first access and returning the cached
|
|
8889
|
+
* result on every subsequent call within the same `<Avatar/>` mount.
|
|
8890
|
+
*
|
|
8891
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
8892
|
+
*/
|
|
8893
|
+
function getOctopus3d3StableState(createRandom) {
|
|
8894
|
+
const cached = stableStateCache.get(createRandom);
|
|
8895
|
+
if (cached !== undefined) {
|
|
8896
|
+
return cached;
|
|
8897
|
+
}
|
|
8898
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
8899
|
+
const animationRandom = createRandom('octopus3d3-animation-profile');
|
|
8900
|
+
const eyeRandom = createRandom('octopus3d3-eye-profile');
|
|
8901
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
8902
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
8903
|
+
const state = {
|
|
8904
|
+
morphologyProfile,
|
|
8905
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
8906
|
+
leftEyePhaseOffset,
|
|
8907
|
+
rightEyePhaseOffset,
|
|
8908
|
+
tentacleProfiles: createContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
8909
|
+
};
|
|
8910
|
+
stableStateCache.set(createRandom, state);
|
|
8911
|
+
return state;
|
|
8912
|
+
}
|
|
8913
|
+
/**
|
|
8914
|
+
* Octopus 3D 3 avatar visual.
|
|
8915
|
+
*
|
|
8916
|
+
* @private built-in avatar visual
|
|
8917
|
+
*/
|
|
8918
|
+
const octopus3d3AvatarVisual = {
|
|
8919
|
+
id: 'octopus3d3',
|
|
8920
|
+
title: 'Octopus 3D 3',
|
|
8921
|
+
description: 'Cute continuous 3D octopus with a blobby single mesh, waving tentacle lobes, rich shading, and cursor-aware eyes.',
|
|
8922
|
+
isAnimated: true,
|
|
8923
|
+
supportsPointerTracking: true,
|
|
8924
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
8925
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles } = getOctopus3d3StableState(createRandom);
|
|
8926
|
+
const sceneCenterX = size * 0.5;
|
|
8927
|
+
const sceneCenterY = size * 0.535;
|
|
8928
|
+
const bob = Math.sin(timeMs / 960 + animationPhase) * size * 0.012;
|
|
8929
|
+
const meshCenter = {
|
|
8930
|
+
x: interaction.bodyOffsetX * size * 0.048 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
8931
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.026 + bob,
|
|
8932
|
+
z: interaction.intensity * size * 0.018,
|
|
8933
|
+
};
|
|
8934
|
+
const rotationY = -0.1 +
|
|
8935
|
+
Math.sin(timeMs / 2700 + animationPhase) * 0.035 +
|
|
8936
|
+
interaction.bodyOffsetX * 0.22 +
|
|
8937
|
+
interaction.gazeX * 0.88;
|
|
8938
|
+
const rotationX = -0.07 +
|
|
8939
|
+
Math.cos(timeMs / 3100 + animationPhase * 0.7) * 0.018 -
|
|
8940
|
+
interaction.bodyOffsetY * 0.08 -
|
|
8941
|
+
interaction.gazeY * 0.38;
|
|
8942
|
+
const surfaceOptions = {
|
|
8943
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.1,
|
|
8944
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.08,
|
|
8945
|
+
radiusZ: size *
|
|
8946
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
8947
|
+
(1.02 + (morphologyProfile.body.horizontalStretch - 1) * 0.18),
|
|
8948
|
+
morphologyProfile,
|
|
8949
|
+
timeMs,
|
|
8950
|
+
animationPhase,
|
|
8951
|
+
tentacleProfiles,
|
|
8952
|
+
};
|
|
8953
|
+
const surfacePatches = resolveVisibleContinuousOctopusPatches({
|
|
8954
|
+
...surfaceOptions,
|
|
8955
|
+
center: meshCenter,
|
|
8956
|
+
rotationX,
|
|
8957
|
+
rotationY,
|
|
8958
|
+
sceneCenterX,
|
|
8959
|
+
sceneCenterY,
|
|
8960
|
+
size,
|
|
8961
|
+
palette,
|
|
8962
|
+
});
|
|
8963
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.2 - 0.03, -0.22, 0.08);
|
|
8964
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.1, 0.18, 0.32);
|
|
8965
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.2 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
8966
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
8967
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
8968
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
8969
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.76;
|
|
8970
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.9;
|
|
8971
|
+
drawAvatarFrame(context, size, palette);
|
|
8972
|
+
drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
8973
|
+
drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
8974
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
8975
|
+
drawContinuousSurfacePatch(context, surfacePatch);
|
|
8976
|
+
}
|
|
8977
|
+
drawProjectedSurfaceCurrents({
|
|
8978
|
+
context,
|
|
8979
|
+
surfaceOptions,
|
|
8980
|
+
center: meshCenter,
|
|
8981
|
+
rotationX,
|
|
8982
|
+
rotationY,
|
|
8983
|
+
sceneCenterX,
|
|
8984
|
+
sceneCenterY,
|
|
8985
|
+
size,
|
|
8986
|
+
palette,
|
|
8987
|
+
morphologyProfile,
|
|
8988
|
+
timeMs,
|
|
8989
|
+
animationPhase,
|
|
8990
|
+
});
|
|
8991
|
+
drawProjectedTentacleSuckers({
|
|
8992
|
+
context,
|
|
8993
|
+
surfaceOptions,
|
|
8994
|
+
center: meshCenter,
|
|
8995
|
+
rotationX,
|
|
8996
|
+
rotationY,
|
|
8997
|
+
sceneCenterX,
|
|
8998
|
+
sceneCenterY,
|
|
8999
|
+
size,
|
|
9000
|
+
palette,
|
|
9001
|
+
});
|
|
9002
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
9003
|
+
drawProjectedOrganicEye(context, sampleContinuousOctopusSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
9004
|
+
drawProjectedOrganicMouth(context, [
|
|
9005
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
9006
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
9007
|
+
sampleContinuousOctopusSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
9008
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
9009
|
+
},
|
|
9010
|
+
};
|
|
9011
|
+
/**
|
|
9012
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
9013
|
+
*
|
|
9014
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9015
|
+
*/
|
|
9016
|
+
function createContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
9017
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT$1 }, (_, tentacleIndex) => {
|
|
9018
|
+
const tentacleRandom = createRandom(`octopus3d3-tentacle-${tentacleIndex}`);
|
|
9019
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT$1 - 1);
|
|
9020
|
+
return {
|
|
9021
|
+
centerLongitude: -Math.PI * 0.86 +
|
|
9022
|
+
progress * Math.PI * 1.72 +
|
|
9023
|
+
(tentacleRandom() - 0.5) * (0.08 + morphologyProfile.tentacles.rootSpreadScale * 0.03),
|
|
9024
|
+
widthScale: 0.86 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.16,
|
|
9025
|
+
lengthScale: 0.86 + tentacleRandom() * 0.36 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.22,
|
|
9026
|
+
swayScale: 0.82 + tentacleRandom() * 0.38 + (morphologyProfile.tentacles.swayScale - 1) * 0.2,
|
|
9027
|
+
depthScale: 0.86 + tentacleRandom() * 0.32 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.2,
|
|
9028
|
+
phase: tentacleRandom() * Math.PI * 2,
|
|
9029
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
9030
|
+
};
|
|
9031
|
+
});
|
|
9032
|
+
}
|
|
9033
|
+
/**
|
|
9034
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
9035
|
+
*
|
|
9036
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9037
|
+
*/
|
|
9038
|
+
function drawContinuousOctopusAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
9039
|
+
const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.11, sceneCenterY - size * 0.17 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.66 + interaction.intensity * 0.02));
|
|
9040
|
+
glowGradient.addColorStop(0, `${palette.highlight}66`);
|
|
9041
|
+
glowGradient.addColorStop(0.34, `${palette.accent}2e`);
|
|
9042
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
9043
|
+
context.fillStyle = glowGradient;
|
|
9044
|
+
context.fillRect(0, 0, size, size);
|
|
9045
|
+
const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1550) * size * 0.05, sceneCenterY + size * 0.29, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.54);
|
|
9046
|
+
lowerGradient.addColorStop(0, `${palette.secondary}25`);
|
|
9047
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
9048
|
+
context.fillStyle = lowerGradient;
|
|
9049
|
+
context.fillRect(0, 0, size, size);
|
|
9050
|
+
}
|
|
9051
|
+
/**
|
|
9052
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
9053
|
+
*
|
|
9054
|
+
* Uses a scaled radial gradient instead of `context.filter = 'blur()'` to approximate the
|
|
9055
|
+
* blurry ellipse without triggering a costly software rasterization pass on every frame.
|
|
9056
|
+
*
|
|
9057
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9058
|
+
*/
|
|
9059
|
+
function drawContinuousOctopusShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
9060
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.045;
|
|
9061
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
9062
|
+
const rx = size * (0.19 + morphologyProfile.tentacles.rootSpreadScale * 0.022 + interaction.intensity * 0.02);
|
|
9063
|
+
const ry = size * 0.06;
|
|
9064
|
+
// Scale the context so that drawing a circle produces the correct ellipse aspect ratio,
|
|
9065
|
+
// then fill with a radial gradient that approximates the blurry edge without context.filter.
|
|
9066
|
+
context.save();
|
|
9067
|
+
context.translate(cx, cy);
|
|
9068
|
+
context.scale(1, ry / rx);
|
|
9069
|
+
const blurRadius = rx * 1.4;
|
|
9070
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
9071
|
+
shadowGradient.addColorStop(0, `${palette.shadow}7a`);
|
|
9072
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}44`);
|
|
9073
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1a`);
|
|
9074
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
9075
|
+
context.fillStyle = shadowGradient;
|
|
9076
|
+
context.beginPath();
|
|
9077
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
9078
|
+
context.fill();
|
|
9079
|
+
context.restore();
|
|
9080
|
+
}
|
|
9081
|
+
/**
|
|
9082
|
+
* Number of latitude segments used by the continuous Octopus 3D 3 mesh.
|
|
9083
|
+
*
|
|
9084
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9085
|
+
*/
|
|
9086
|
+
const LATITUDE_PATCH_COUNT$1 = 16;
|
|
9087
|
+
/**
|
|
9088
|
+
* Number of longitude segments used by the continuous Octopus 3D 3 mesh.
|
|
9089
|
+
*
|
|
9090
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9091
|
+
*/
|
|
9092
|
+
const LONGITUDE_PATCH_COUNT$1 = 40;
|
|
9093
|
+
/**
|
|
9094
|
+
* Resolves visible projected patches for the continuous octopus mesh.
|
|
9095
|
+
*
|
|
9096
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
9097
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
9098
|
+
* than re-evaluated for every patch corner — the patch loop alone calls `sampleContinuousOctopusSurfacePoint`
|
|
9099
|
+
* for `latitudePatchCount * longitudePatchCount * 4` corners without caching, and each call
|
|
9100
|
+
* triggers an inner 8-iteration `Math.exp` loop, which dominates the per-frame cost.
|
|
9101
|
+
*
|
|
9102
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9103
|
+
*/
|
|
9104
|
+
function resolveVisibleContinuousOctopusPatches(options) {
|
|
9105
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
9106
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT$1;
|
|
9107
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT$1;
|
|
9108
|
+
const surfacePatches = [];
|
|
9109
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
9110
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
9111
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
9112
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
9113
|
+
}
|
|
9114
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
9115
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
9116
|
+
}
|
|
9117
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
9118
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
9119
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
9120
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
9121
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
9122
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
9123
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
9124
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveContinuousTentacleInfluence(options, cornerLongitude);
|
|
9125
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveContinuousLobeWave(options, cornerLongitude);
|
|
9126
|
+
}
|
|
9127
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
9128
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
9129
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveContinuousTentacleInfluence(options, patchCenterLongitude);
|
|
9130
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveContinuousLobeWave(options, patchCenterLongitude);
|
|
9131
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
9132
|
+
}
|
|
9133
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
9134
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
9135
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
9136
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
9137
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
9138
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
9139
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
9140
|
+
const cornerSample = sampleContinuousOctopusSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
9141
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
9145
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
9146
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
9147
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
9148
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
9149
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
9150
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
9151
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
9152
|
+
const transformedCorners = [
|
|
9153
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
9154
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
9155
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
9156
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
9157
|
+
];
|
|
9158
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
9159
|
+
if (surfaceNormal.z <= 0.008) {
|
|
9160
|
+
continue;
|
|
9161
|
+
}
|
|
9162
|
+
const projectedCorners = [
|
|
9163
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
9164
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
9165
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
9166
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
9167
|
+
];
|
|
9168
|
+
surfacePatches.push({
|
|
9169
|
+
corners: projectedCorners,
|
|
9170
|
+
averageDepth: (transformedCorners[0].z +
|
|
9171
|
+
transformedCorners[1].z +
|
|
9172
|
+
transformedCorners[2].z +
|
|
9173
|
+
transformedCorners[3].z) /
|
|
9174
|
+
4,
|
|
9175
|
+
lightIntensity: clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION$1), -1, 1),
|
|
9176
|
+
fillStyle: resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
9177
|
+
outlineColor: verticalProgress < 0.54 ? `${palette.highlight}69` : `${palette.shadow}78`,
|
|
9178
|
+
});
|
|
9179
|
+
}
|
|
9180
|
+
}
|
|
9181
|
+
return surfacePatches;
|
|
9182
|
+
}
|
|
9183
|
+
/**
|
|
9184
|
+
* Samples one point on the continuous Octopus 3D 3 surface.
|
|
9185
|
+
*
|
|
9186
|
+
* The lower hemisphere is pulled into eight seeded waving lobes, so the portrait reads as
|
|
9187
|
+
* tentacled while still being rendered as one connected blobby mesh.
|
|
9188
|
+
*
|
|
9189
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9190
|
+
*/
|
|
9191
|
+
function sampleContinuousOctopusSurfacePoint(options, latitude, longitude) {
|
|
9192
|
+
return sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, resolveContinuousTentacleInfluence(options, longitude), resolveContinuousLobeWave(options, longitude));
|
|
9193
|
+
}
|
|
9194
|
+
/**
|
|
9195
|
+
* Samples one point on the continuous Octopus 3D 3 surface using precomputed longitude-only
|
|
9196
|
+
* values to skip the per-call `Math.exp` tentacle-influence loop and the lobe-wave trig call.
|
|
9197
|
+
*
|
|
9198
|
+
* The patch loop quantizes the mesh into a fixed `(latitudePatchCount + 1) * (longitudePatchCount + 1)`
|
|
9199
|
+
* corner grid, so the same longitude is reused across every latitude row and each
|
|
9200
|
+
* tentacle/lobe value can be computed once per frame instead of `latitudePatchCount * 4` times.
|
|
9201
|
+
*
|
|
9202
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9203
|
+
*/
|
|
9204
|
+
function sampleContinuousOctopusSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
9205
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
9206
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
9207
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
9208
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.28);
|
|
9209
|
+
const lowerBlend = smoothStep$1(0.38, 1, verticalProgress);
|
|
9210
|
+
const tipBlend = smoothStep$1(0.68, 1, verticalProgress);
|
|
9211
|
+
const centerPull = resolveSignedAngularDistance$1(longitude, tentacleInfluence.centerLongitude);
|
|
9212
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.24 + tipBlend * 0.2);
|
|
9213
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
9214
|
+
animationPhase * 0.6 +
|
|
9215
|
+
timeMs / (1750 + morphologyProfile.body.lobeCount * 30)) *
|
|
9216
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.8) *
|
|
9217
|
+
(0.3 + lowerBlend * 0.7);
|
|
9218
|
+
const tentacleWave = Math.sin(timeMs / 760 + tentacleInfluence.phase + verticalProgress * 2.4) *
|
|
9219
|
+
lowerBlend *
|
|
9220
|
+
tentacleInfluence.core *
|
|
9221
|
+
tentacleInfluence.swayScale;
|
|
9222
|
+
const horizontalScale = 1.04 +
|
|
9223
|
+
mantleRipple +
|
|
9224
|
+
lowerBlend * (0.16 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.1) +
|
|
9225
|
+
lowerBlend * tentacleInfluence.core * (0.2 + lowerLobeWave * 0.12) -
|
|
9226
|
+
upperBlend * 0.08;
|
|
9227
|
+
const depthScale = 1.06 +
|
|
9228
|
+
upperBlend * 0.16 +
|
|
9229
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.1 +
|
|
9230
|
+
lowerBlend * tentacleInfluence.core * (0.1 + tentacleInfluence.depthScale * 0.06) -
|
|
9231
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
9232
|
+
const tentacleTubeRadius = lowerBlend * tentacleInfluence.core * (0.11 + tipBlend * 0.06 + tentacleInfluence.widthScale * 0.025) * radiusX;
|
|
9233
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
9234
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.72;
|
|
9235
|
+
const lowerDrop = lowerBlend *
|
|
9236
|
+
radiusY *
|
|
9237
|
+
(0.18 +
|
|
9238
|
+
tentacleInfluence.core *
|
|
9239
|
+
(0.38 +
|
|
9240
|
+
tentacleInfluence.lengthScale * 0.22 +
|
|
9241
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.08));
|
|
9242
|
+
return {
|
|
9243
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX + tentacleWave * radiusX * (0.052 + tipBlend * 0.05),
|
|
9244
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.12) -
|
|
9245
|
+
upperBlend * radiusY * 0.1 +
|
|
9246
|
+
lowerDrop +
|
|
9247
|
+
Math.sin(timeMs / 1420 + animationPhase + latitude * 1.6) * lowerBlend * radiusY * 0.018 +
|
|
9248
|
+
Math.cos(timeMs / 880 + tentacleInfluence.phase) *
|
|
9249
|
+
lowerBlend *
|
|
9250
|
+
tipBlend *
|
|
9251
|
+
tentacleInfluence.core *
|
|
9252
|
+
radiusY *
|
|
9253
|
+
0.034,
|
|
9254
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
9255
|
+
Math.cos(timeMs / 980 + tentacleInfluence.phase + verticalProgress) *
|
|
9256
|
+
lowerBlend *
|
|
9257
|
+
tentacleInfluence.core *
|
|
9258
|
+
radiusZ *
|
|
9259
|
+
0.04,
|
|
9260
|
+
};
|
|
9261
|
+
}
|
|
9262
|
+
/**
|
|
9263
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
9264
|
+
*
|
|
9265
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9266
|
+
*/
|
|
9267
|
+
function resolveContinuousTentacleInfluence(options, longitude) {
|
|
9268
|
+
let totalWeight = 0;
|
|
9269
|
+
let weightedSin = 0;
|
|
9270
|
+
let weightedCos = 0;
|
|
9271
|
+
let weightedWidthScale = 0;
|
|
9272
|
+
let weightedLengthScale = 0;
|
|
9273
|
+
let weightedSwayScale = 0;
|
|
9274
|
+
let weightedDepthScale = 0;
|
|
9275
|
+
let weightedPhase = 0;
|
|
9276
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
9277
|
+
const distance = Math.abs(resolveSignedAngularDistance$1(longitude, tentacleProfile.centerLongitude));
|
|
9278
|
+
const width = 0.2 * tentacleProfile.widthScale;
|
|
9279
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
9280
|
+
totalWeight += weight;
|
|
9281
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
9282
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
9283
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
9284
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
9285
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
9286
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
9287
|
+
weightedPhase += tentacleProfile.phase * weight;
|
|
9288
|
+
}
|
|
9289
|
+
if (totalWeight < 0.0001) {
|
|
9290
|
+
return {
|
|
9291
|
+
core: 0,
|
|
9292
|
+
centerLongitude: longitude,
|
|
9293
|
+
widthScale: 1,
|
|
9294
|
+
lengthScale: 1,
|
|
9295
|
+
swayScale: 1,
|
|
9296
|
+
depthScale: 1,
|
|
9297
|
+
phase: 0,
|
|
9298
|
+
};
|
|
9299
|
+
}
|
|
9300
|
+
return {
|
|
9301
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
9302
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
9303
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
9304
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
9305
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
9306
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
9307
|
+
phase: weightedPhase / totalWeight,
|
|
9308
|
+
};
|
|
9309
|
+
}
|
|
9310
|
+
/**
|
|
9311
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
9312
|
+
*
|
|
9313
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9314
|
+
*/
|
|
9315
|
+
function resolveContinuousLobeWave(options, longitude) {
|
|
9316
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
9317
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT$1 +
|
|
9318
|
+
animationPhase +
|
|
9319
|
+
timeMs / (980 + morphologyProfile.body.lobeCount * 18)) +
|
|
9320
|
+
1) /
|
|
9321
|
+
2);
|
|
9322
|
+
}
|
|
9323
|
+
/**
|
|
9324
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
9325
|
+
*
|
|
9326
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9327
|
+
*/
|
|
9328
|
+
function resolveContinuousSurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
9329
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
9330
|
+
if (tonalProgress < 0.14) {
|
|
9331
|
+
return palette.highlight;
|
|
9332
|
+
}
|
|
9333
|
+
if (tonalProgress < 0.32) {
|
|
9334
|
+
return palette.secondary;
|
|
9335
|
+
}
|
|
9336
|
+
if (tonalProgress < 0.72) {
|
|
9337
|
+
return forwardness > 0.55 ? palette.secondary : palette.primary;
|
|
9338
|
+
}
|
|
9339
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
9340
|
+
}
|
|
9341
|
+
/**
|
|
9342
|
+
* Draws one projected mesh patch with soft shading and a subtle edge.
|
|
9343
|
+
*
|
|
9344
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9345
|
+
*/
|
|
9346
|
+
function drawContinuousSurfacePatch(context, surfacePatch) {
|
|
9347
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
9348
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
9349
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.18 * surfacePatch.lightIntensity})`);
|
|
9350
|
+
}
|
|
9351
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
9352
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.25 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
9353
|
+
}
|
|
9354
|
+
context.save();
|
|
9355
|
+
context.beginPath();
|
|
9356
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
9357
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
9358
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
9359
|
+
}
|
|
9360
|
+
context.closePath();
|
|
9361
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
9362
|
+
context.lineWidth = Math.max(0.7, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0032);
|
|
9363
|
+
context.lineJoin = 'round';
|
|
9364
|
+
context.stroke();
|
|
9365
|
+
context.restore();
|
|
9366
|
+
}
|
|
9367
|
+
/**
|
|
9368
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
9369
|
+
*
|
|
9370
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9371
|
+
*/
|
|
9372
|
+
function drawProjectedSurfaceCurrents(options) {
|
|
9373
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
9374
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
9375
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
9376
|
+
context.save();
|
|
9377
|
+
context.lineCap = 'round';
|
|
9378
|
+
context.lineJoin = 'round';
|
|
9379
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
9380
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
9381
|
+
const projectedPoints = [];
|
|
9382
|
+
for (let sampleIndex = 0; sampleIndex < 8; sampleIndex++) {
|
|
9383
|
+
const progress = sampleIndex / 7;
|
|
9384
|
+
const latitude = -0.46 + progress * 0.74;
|
|
9385
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1160 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.035;
|
|
9386
|
+
const scenePoint = transformScenePoint(sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
9387
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
9388
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
if (projectedPoints.length < 3) {
|
|
9392
|
+
continue;
|
|
9393
|
+
}
|
|
9394
|
+
context.beginPath();
|
|
9395
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
9396
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
9397
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
9398
|
+
}
|
|
9399
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
9400
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
9401
|
+
context.stroke();
|
|
9402
|
+
}
|
|
9403
|
+
context.restore();
|
|
9404
|
+
}
|
|
9405
|
+
/**
|
|
9406
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
9407
|
+
*
|
|
9408
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9409
|
+
*/
|
|
9410
|
+
function drawProjectedTentacleSuckers(options) {
|
|
9411
|
+
const { surfaceOptions, size } = options;
|
|
9412
|
+
const { timeMs } = surfaceOptions;
|
|
9413
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
9414
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.12) {
|
|
9415
|
+
continue;
|
|
9416
|
+
}
|
|
9417
|
+
for (let suckerIndex = 0; suckerIndex < 3; suckerIndex++) {
|
|
9418
|
+
const latitude = 0.52 + suckerIndex * 0.14;
|
|
9419
|
+
const sideOffset = tentacleProfile.suckerSide * (0.035 + suckerIndex * 0.012) * tentacleProfile.widthScale;
|
|
9420
|
+
const waveOffset = Math.sin(timeMs / 900 + tentacleProfile.phase + suckerIndex * 0.8) * 0.018;
|
|
9421
|
+
drawProjectedSurfaceSpot({
|
|
9422
|
+
...options,
|
|
9423
|
+
latitude,
|
|
9424
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
9425
|
+
radiusScale: size * (0.0065 - suckerIndex * 0.0007),
|
|
9426
|
+
});
|
|
9427
|
+
}
|
|
9428
|
+
}
|
|
9429
|
+
}
|
|
9430
|
+
/**
|
|
9431
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
9432
|
+
*
|
|
9433
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9434
|
+
*/
|
|
9435
|
+
function drawProjectedSurfaceSpot(options) {
|
|
9436
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
9437
|
+
const localCenter = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude);
|
|
9438
|
+
const localHorizontal = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
9439
|
+
const localVertical = sampleContinuousOctopusSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
9440
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
9441
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
9442
|
+
return;
|
|
9443
|
+
}
|
|
9444
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
9445
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
9446
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
9447
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
9448
|
+
radiusScale *
|
|
9449
|
+
0.74, size * 0.003, size * 0.018);
|
|
9450
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
9451
|
+
radiusScale *
|
|
9452
|
+
0.52, size * 0.0024, size * 0.014);
|
|
9453
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
9454
|
+
context.save();
|
|
9455
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
9456
|
+
context.rotate(rotation);
|
|
9457
|
+
context.beginPath();
|
|
9458
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
9459
|
+
context.fillStyle = `${palette.highlight}73`;
|
|
9460
|
+
context.fill();
|
|
9461
|
+
context.strokeStyle = `${palette.highlight}99`;
|
|
9462
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
9463
|
+
context.stroke();
|
|
9464
|
+
context.restore();
|
|
9465
|
+
}
|
|
9466
|
+
/**
|
|
9467
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
9468
|
+
*
|
|
9469
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9470
|
+
*/
|
|
9471
|
+
function resolveSignedAngularDistance$1(sourceLongitude, targetLongitude) {
|
|
9472
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
9473
|
+
}
|
|
9474
|
+
/**
|
|
9475
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
9476
|
+
*
|
|
9477
|
+
* @private helper of `octopus3d3AvatarVisual`
|
|
9478
|
+
*/
|
|
9479
|
+
function smoothStep$1(edgeStart, edgeEnd, value) {
|
|
9480
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
9481
|
+
return progress * progress * (3 - 2 * progress);
|
|
9482
|
+
}
|
|
9483
|
+
|
|
9484
|
+
/* eslint-disable no-magic-numbers */
|
|
9485
|
+
/**
|
|
9486
|
+
* Light direction used by the continuous Octopus 3D 4 mesh shading.
|
|
9487
|
+
*
|
|
9488
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9489
|
+
*/
|
|
9490
|
+
const LIGHT_DIRECTION = normalizeVector3({
|
|
9491
|
+
x: 0.32,
|
|
9492
|
+
y: -0.66,
|
|
9493
|
+
z: 1,
|
|
9494
|
+
});
|
|
9495
|
+
/**
|
|
9496
|
+
* Rim-light direction used to brighten the side of the silhouette.
|
|
9497
|
+
*
|
|
9498
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9499
|
+
*/
|
|
9500
|
+
const RIM_LIGHT_DIRECTION = normalizeVector3({
|
|
9501
|
+
x: -0.7,
|
|
9502
|
+
y: -0.18,
|
|
9503
|
+
z: 0.55,
|
|
9504
|
+
});
|
|
9505
|
+
/**
|
|
9506
|
+
* Real-octopus tentacle count used by the continuous lower mesh.
|
|
9507
|
+
*
|
|
9508
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9509
|
+
*/
|
|
9510
|
+
const OCTOPUS_TENTACLE_COUNT = 8;
|
|
9511
|
+
/**
|
|
9512
|
+
* Number of seeded skin spots painted across the octopus body.
|
|
9513
|
+
*
|
|
9514
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9515
|
+
*/
|
|
9516
|
+
const SKIN_SPOT_COUNT = 14;
|
|
9517
|
+
/**
|
|
9518
|
+
* Cache keyed by the `createRandom` factory reference, stable per mounted `<Avatar/>` component.
|
|
9519
|
+
*
|
|
9520
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9521
|
+
*/
|
|
9522
|
+
const octopus3d4StableStateCache = new WeakMap();
|
|
9523
|
+
/**
|
|
9524
|
+
* Returns the stable per-avatar state, computing it on first access and caching afterwards.
|
|
9525
|
+
*
|
|
9526
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9527
|
+
*/
|
|
9528
|
+
function getOctopus3d4StableState(createRandom) {
|
|
9529
|
+
const cached = octopus3d4StableStateCache.get(createRandom);
|
|
9530
|
+
if (cached !== undefined) {
|
|
9531
|
+
return cached;
|
|
9532
|
+
}
|
|
9533
|
+
const morphologyProfile = createOctopus3MorphologyProfile(createRandom);
|
|
9534
|
+
const animationRandom = createRandom('octopus3d4-animation-profile');
|
|
9535
|
+
const eyeRandom = createRandom('octopus3d4-eye-profile');
|
|
9536
|
+
const leftEyePhaseOffset = eyeRandom() * 0.7;
|
|
9537
|
+
const rightEyePhaseOffset = eyeRandom() * 0.7;
|
|
9538
|
+
const state = {
|
|
9539
|
+
morphologyProfile,
|
|
9540
|
+
animationPhase: animationRandom() * Math.PI * 2,
|
|
9541
|
+
leftEyePhaseOffset,
|
|
9542
|
+
rightEyePhaseOffset,
|
|
9543
|
+
tentacleProfiles: createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile),
|
|
9544
|
+
skinSpots: createBlobbySkinSpots(createRandom),
|
|
9545
|
+
};
|
|
9546
|
+
octopus3d4StableStateCache.set(createRandom, state);
|
|
9547
|
+
return state;
|
|
9548
|
+
}
|
|
9549
|
+
/**
|
|
9550
|
+
* Octopus 3D 4 avatar visual.
|
|
9551
|
+
*
|
|
9552
|
+
* @private built-in avatar visual
|
|
9553
|
+
*/
|
|
9554
|
+
const octopus3d4AvatarVisual = {
|
|
9555
|
+
id: 'octopus3d4',
|
|
9556
|
+
title: 'Octopus 3D 4',
|
|
9557
|
+
description: 'Cute blobby 3D octopus rendered as one smooth continuous mesh with naturally undulating tentacles, rich multi-pass shading, pigment spots, and cursor-aware head and eyes.',
|
|
9558
|
+
isAnimated: true,
|
|
9559
|
+
supportsPointerTracking: true,
|
|
9560
|
+
render({ context, size, palette, createRandom, timeMs, interaction }) {
|
|
9561
|
+
const { morphologyProfile, animationPhase, leftEyePhaseOffset, rightEyePhaseOffset, tentacleProfiles, skinSpots } = getOctopus3d4StableState(createRandom);
|
|
9562
|
+
const sceneCenterX = size * 0.5;
|
|
9563
|
+
const sceneCenterY = size * 0.535;
|
|
9564
|
+
const bob = Math.sin(timeMs / 980 + animationPhase) * size * 0.013;
|
|
9565
|
+
const meshCenter = {
|
|
9566
|
+
x: interaction.bodyOffsetX * size * 0.052 + size * morphologyProfile.body.centerXJitterRatio * 0.44,
|
|
9567
|
+
y: -size * 0.07 + interaction.bodyOffsetY * size * 0.028 + bob,
|
|
9568
|
+
z: interaction.intensity * size * 0.02,
|
|
9569
|
+
};
|
|
9570
|
+
const rotationY = -0.08 +
|
|
9571
|
+
Math.sin(timeMs / 2800 + animationPhase) * 0.04 +
|
|
9572
|
+
interaction.bodyOffsetX * 0.24 +
|
|
9573
|
+
interaction.gazeX * 0.98;
|
|
9574
|
+
const rotationX = -0.07 +
|
|
9575
|
+
Math.cos(timeMs / 3200 + animationPhase * 0.7) * 0.02 -
|
|
9576
|
+
interaction.bodyOffsetY * 0.08 -
|
|
9577
|
+
interaction.gazeY * 0.42;
|
|
9578
|
+
const surfaceOptions = {
|
|
9579
|
+
radiusX: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch * 1.12,
|
|
9580
|
+
radiusY: size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1,
|
|
9581
|
+
radiusZ: size *
|
|
9582
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
9583
|
+
(1.04 + (morphologyProfile.body.horizontalStretch - 1) * 0.2),
|
|
9584
|
+
morphologyProfile,
|
|
9585
|
+
timeMs,
|
|
9586
|
+
animationPhase,
|
|
9587
|
+
tentacleProfiles,
|
|
9588
|
+
};
|
|
9589
|
+
const surfacePatches = resolveVisibleBlobbyContinuousPatches({
|
|
9590
|
+
...surfaceOptions,
|
|
9591
|
+
center: meshCenter,
|
|
9592
|
+
rotationX,
|
|
9593
|
+
rotationY,
|
|
9594
|
+
sceneCenterX,
|
|
9595
|
+
sceneCenterY,
|
|
9596
|
+
size,
|
|
9597
|
+
palette,
|
|
9598
|
+
});
|
|
9599
|
+
const eyeLatitude = clampNumber$1(morphologyProfile.face.eyeCenterYOffsetRatio * 4.4 - 0.04, -0.24, 0.08);
|
|
9600
|
+
const eyeLongitude = clampNumber$1(morphologyProfile.face.eyeSpacingRatio * 3.0, 0.18, 0.32);
|
|
9601
|
+
const mouthLatitude = clampNumber$1(eyeLatitude + 0.21 + morphologyProfile.face.mouthYOffsetRatio, 0.08, 0.34);
|
|
9602
|
+
const mouthCenterLongitude = clampNumber$1(morphologyProfile.face.mouthCenterOffsetRatio * 5.6, -0.08, 0.08);
|
|
9603
|
+
const mouthHalfLongitude = clampNumber$1(eyeLongitude * 0.78, 0.15, 0.28);
|
|
9604
|
+
const mouthCurveLatitude = clampNumber$1(mouthLatitude + morphologyProfile.face.mouthCurveDepthRatio * 0.78, mouthLatitude + 0.03, 0.42);
|
|
9605
|
+
const eyeRadiusX = size * morphologyProfile.face.eyeRadiusXRatio * 0.78;
|
|
9606
|
+
const eyeRadiusY = eyeRadiusX * morphologyProfile.face.eyeHeightRatio * 0.92;
|
|
9607
|
+
drawAvatarFrame(context, size, palette);
|
|
9608
|
+
drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs);
|
|
9609
|
+
drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile);
|
|
9610
|
+
for (const surfacePatch of surfacePatches.sort((firstSurfacePatch, secondSurfacePatch) => firstSurfacePatch.averageDepth - secondSurfacePatch.averageDepth)) {
|
|
9611
|
+
drawBlobbyContinuousSurfacePatch(context, surfacePatch);
|
|
9612
|
+
}
|
|
9613
|
+
drawBlobbySkinSpots({
|
|
9614
|
+
context,
|
|
9615
|
+
surfaceOptions,
|
|
9616
|
+
center: meshCenter,
|
|
9617
|
+
rotationX,
|
|
9618
|
+
rotationY,
|
|
9619
|
+
sceneCenterX,
|
|
9620
|
+
sceneCenterY,
|
|
9621
|
+
size,
|
|
9622
|
+
palette,
|
|
9623
|
+
skinSpots,
|
|
9624
|
+
});
|
|
9625
|
+
drawBlobbyContinuousCurrents({
|
|
9626
|
+
context,
|
|
9627
|
+
surfaceOptions,
|
|
9628
|
+
center: meshCenter,
|
|
9629
|
+
rotationX,
|
|
9630
|
+
rotationY,
|
|
9631
|
+
sceneCenterX,
|
|
9632
|
+
sceneCenterY,
|
|
9633
|
+
size,
|
|
9634
|
+
palette,
|
|
9635
|
+
morphologyProfile,
|
|
9636
|
+
timeMs,
|
|
9637
|
+
animationPhase,
|
|
9638
|
+
});
|
|
9639
|
+
drawBlobbyContinuousSuckers({
|
|
9640
|
+
context,
|
|
9641
|
+
surfaceOptions,
|
|
9642
|
+
center: meshCenter,
|
|
9643
|
+
rotationX,
|
|
9644
|
+
rotationY,
|
|
9645
|
+
sceneCenterX,
|
|
9646
|
+
sceneCenterY,
|
|
9647
|
+
size,
|
|
9648
|
+
palette,
|
|
9649
|
+
});
|
|
9650
|
+
drawBlobbyContinuousGloss({
|
|
9651
|
+
context,
|
|
9652
|
+
surfaceOptions,
|
|
9653
|
+
center: meshCenter,
|
|
9654
|
+
rotationX,
|
|
9655
|
+
rotationY,
|
|
9656
|
+
sceneCenterX,
|
|
9657
|
+
sceneCenterY,
|
|
9658
|
+
size,
|
|
9659
|
+
palette,
|
|
9660
|
+
});
|
|
9661
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, -eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + leftEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
9662
|
+
drawProjectedOrganicEye(context, sampleBlobbyContinuousSurfacePoint(surfaceOptions, eyeLatitude, eyeLongitude), eyeRadiusX, eyeRadiusY, meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.85 + rightEyePhaseOffset, interaction, morphologyProfile.face.eyeStyle);
|
|
9663
|
+
drawProjectedOrganicMouth(context, [
|
|
9664
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude - mouthHalfLongitude),
|
|
9665
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthCurveLatitude, mouthCenterLongitude),
|
|
9666
|
+
sampleBlobbyContinuousSurfacePoint(surfaceOptions, mouthLatitude, mouthCenterLongitude + mouthHalfLongitude),
|
|
9667
|
+
], meshCenter, rotationX, rotationY, sceneCenterX, sceneCenterY, palette, size);
|
|
9668
|
+
},
|
|
9669
|
+
};
|
|
9670
|
+
/**
|
|
9671
|
+
* Creates seeded tentacle-lobe profiles around the visible lower octopus body.
|
|
9672
|
+
*
|
|
9673
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9674
|
+
*/
|
|
9675
|
+
function createBlobbyContinuousTentacleProfiles(createRandom, morphologyProfile) {
|
|
9676
|
+
return Array.from({ length: OCTOPUS_TENTACLE_COUNT }, (_, tentacleIndex) => {
|
|
9677
|
+
const tentacleRandom = createRandom(`octopus3d4-tentacle-${tentacleIndex}`);
|
|
9678
|
+
const progress = tentacleIndex / (OCTOPUS_TENTACLE_COUNT - 1);
|
|
9679
|
+
return {
|
|
9680
|
+
centerLongitude: -Math.PI * 0.9 +
|
|
9681
|
+
progress * Math.PI * 1.8 +
|
|
9682
|
+
(tentacleRandom() - 0.5) * (0.06 + morphologyProfile.tentacles.rootSpreadScale * 0.025),
|
|
9683
|
+
widthScale: 0.92 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.baseWidthScale - 1) * 0.18,
|
|
9684
|
+
lengthScale: 0.9 + tentacleRandom() * 0.34 + (morphologyProfile.tentacles.flowLengthScale - 1) * 0.24,
|
|
9685
|
+
swayScale: 0.86 + tentacleRandom() * 0.4 + (morphologyProfile.tentacles.swayScale - 1) * 0.22,
|
|
9686
|
+
depthScale: 0.9 + tentacleRandom() * 0.3 + (morphologyProfile.tentacles.tipReachScale - 1) * 0.22,
|
|
9687
|
+
curlScale: 0.62 + tentacleRandom() * 0.46,
|
|
9688
|
+
primaryPhase: tentacleRandom() * Math.PI * 2,
|
|
9689
|
+
secondaryPhase: tentacleRandom() * Math.PI * 2,
|
|
9690
|
+
suckerSide: tentacleRandom() > 0.5 ? 1 : -1,
|
|
9691
|
+
};
|
|
9692
|
+
});
|
|
9693
|
+
}
|
|
9694
|
+
/**
|
|
9695
|
+
* Creates seeded skin pigment spots distributed across the upper octopus mesh.
|
|
9696
|
+
*
|
|
9697
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9698
|
+
*/
|
|
9699
|
+
function createBlobbySkinSpots(createRandom) {
|
|
9700
|
+
const spotRandom = createRandom('octopus3d4-skin-spots');
|
|
9701
|
+
return Array.from({ length: SKIN_SPOT_COUNT }, () => ({
|
|
9702
|
+
latitude: -0.45 + spotRandom() * 0.7,
|
|
9703
|
+
longitude: -0.5 + spotRandom() * 1.0,
|
|
9704
|
+
radiusScale: 0.0028 + spotRandom() * 0.0052,
|
|
9705
|
+
opacity: 0.16 + spotRandom() * 0.22,
|
|
9706
|
+
}));
|
|
9707
|
+
}
|
|
9708
|
+
/**
|
|
9709
|
+
* Draws the soft underwater atmosphere behind the continuous octopus mesh.
|
|
9710
|
+
*
|
|
9711
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9712
|
+
*/
|
|
9713
|
+
function drawBlobbyContinuousAtmosphere(context, size, palette, sceneCenterX, sceneCenterY, interaction, timeMs) {
|
|
9714
|
+
const glowGradient = context.createRadialGradient(sceneCenterX + interaction.gazeX * size * 0.12, sceneCenterY - size * 0.18 + interaction.gazeY * size * 0.05, size * 0.04, sceneCenterX, sceneCenterY, size * (0.68 + interaction.intensity * 0.025));
|
|
9715
|
+
glowGradient.addColorStop(0, `${palette.highlight}74`);
|
|
9716
|
+
glowGradient.addColorStop(0.32, `${palette.accent}32`);
|
|
9717
|
+
glowGradient.addColorStop(1, `${palette.highlight}00`);
|
|
9718
|
+
context.fillStyle = glowGradient;
|
|
9719
|
+
context.fillRect(0, 0, size, size);
|
|
9720
|
+
const lowerGradient = context.createRadialGradient(sceneCenterX + Math.sin(timeMs / 1500) * size * 0.05, sceneCenterY + size * 0.3, size * 0.06, sceneCenterX, sceneCenterY + size * 0.3, size * 0.56);
|
|
9721
|
+
lowerGradient.addColorStop(0, `${palette.secondary}28`);
|
|
9722
|
+
lowerGradient.addColorStop(1, `${palette.secondary}00`);
|
|
9723
|
+
context.fillStyle = lowerGradient;
|
|
9724
|
+
context.fillRect(0, 0, size, size);
|
|
9725
|
+
}
|
|
9726
|
+
/**
|
|
9727
|
+
* Draws the soft lower shadow that anchors the octopus in the avatar frame.
|
|
9728
|
+
*
|
|
9729
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9730
|
+
*/
|
|
9731
|
+
function drawBlobbyContinuousShadow(context, size, palette, interaction, timeMs, morphologyProfile) {
|
|
9732
|
+
const cx = size * 0.5 + interaction.gazeX * size * 0.046;
|
|
9733
|
+
const cy = size * 0.9 + Math.sin(timeMs / 980) * size * 0.007;
|
|
9734
|
+
const rx = size * (0.2 + morphologyProfile.tentacles.rootSpreadScale * 0.024 + interaction.intensity * 0.022);
|
|
9735
|
+
const ry = size * 0.062;
|
|
9736
|
+
context.save();
|
|
9737
|
+
context.translate(cx, cy);
|
|
9738
|
+
context.scale(1, ry / rx);
|
|
9739
|
+
const blurRadius = rx * 1.42;
|
|
9740
|
+
const shadowGradient = context.createRadialGradient(0, 0, 0, 0, 0, blurRadius);
|
|
9741
|
+
shadowGradient.addColorStop(0, `${palette.shadow}82`);
|
|
9742
|
+
shadowGradient.addColorStop(0.45, `${palette.shadow}4a`);
|
|
9743
|
+
shadowGradient.addColorStop(0.8, `${palette.shadow}1c`);
|
|
9744
|
+
shadowGradient.addColorStop(1, `${palette.shadow}00`);
|
|
9745
|
+
context.fillStyle = shadowGradient;
|
|
9746
|
+
context.beginPath();
|
|
9747
|
+
context.arc(0, 0, blurRadius, 0, Math.PI * 2);
|
|
9748
|
+
context.fill();
|
|
9749
|
+
context.restore();
|
|
9750
|
+
}
|
|
9751
|
+
/**
|
|
9752
|
+
* Number of latitude segments used by the continuous Octopus 3D 4 mesh.
|
|
9753
|
+
*
|
|
9754
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9755
|
+
*/
|
|
9756
|
+
const LATITUDE_PATCH_COUNT = 20;
|
|
9757
|
+
/**
|
|
9758
|
+
* Number of longitude segments used by the continuous Octopus 3D 4 mesh.
|
|
9759
|
+
*
|
|
9760
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9761
|
+
*/
|
|
9762
|
+
const LONGITUDE_PATCH_COUNT = 48;
|
|
9763
|
+
/**
|
|
9764
|
+
* Resolves visible projected patches for the continuous Octopus 3D 4 mesh.
|
|
9765
|
+
*
|
|
9766
|
+
* Within a single frame, mesh corner samples and longitude-only computations (tentacle
|
|
9767
|
+
* influence and lobe wave) are quantized to the patch grid and computed once each rather
|
|
9768
|
+
* than re-evaluated for every patch corner.
|
|
9769
|
+
*
|
|
9770
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9771
|
+
*/
|
|
9772
|
+
function resolveVisibleBlobbyContinuousPatches(options) {
|
|
9773
|
+
const { center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette } = options;
|
|
9774
|
+
const latitudePatchCount = LATITUDE_PATCH_COUNT;
|
|
9775
|
+
const longitudePatchCount = LONGITUDE_PATCH_COUNT;
|
|
9776
|
+
const surfacePatches = [];
|
|
9777
|
+
const latitudeBoundaries = new Float64Array(latitudePatchCount + 1);
|
|
9778
|
+
const longitudeBoundaries = new Float64Array(longitudePatchCount + 1);
|
|
9779
|
+
for (let boundaryIndex = 0; boundaryIndex <= latitudePatchCount; boundaryIndex++) {
|
|
9780
|
+
latitudeBoundaries[boundaryIndex] = -Math.PI / 2 + (boundaryIndex / latitudePatchCount) * Math.PI;
|
|
9781
|
+
}
|
|
9782
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
9783
|
+
longitudeBoundaries[boundaryIndex] = -Math.PI + (boundaryIndex / longitudePatchCount) * Math.PI * 2;
|
|
9784
|
+
}
|
|
9785
|
+
const cachedTentacleInfluencesByCornerLongitude = new Array(longitudePatchCount + 1);
|
|
9786
|
+
const cachedLobeWavesByCornerLongitude = new Float64Array(longitudePatchCount + 1);
|
|
9787
|
+
const cachedTentacleInfluencesByPatchCenterLongitude = new Array(longitudePatchCount);
|
|
9788
|
+
const cachedLobeWavesByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
9789
|
+
const cachedCosByPatchCenterLongitude = new Float64Array(longitudePatchCount);
|
|
9790
|
+
for (let boundaryIndex = 0; boundaryIndex <= longitudePatchCount; boundaryIndex++) {
|
|
9791
|
+
const cornerLongitude = longitudeBoundaries[boundaryIndex];
|
|
9792
|
+
cachedTentacleInfluencesByCornerLongitude[boundaryIndex] = resolveBlobbyTentacleInfluence(options, cornerLongitude);
|
|
9793
|
+
cachedLobeWavesByCornerLongitude[boundaryIndex] = resolveBlobbyLobeWave(options, cornerLongitude);
|
|
9794
|
+
}
|
|
9795
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
9796
|
+
const patchCenterLongitude = (longitudeBoundaries[longitudeIndex] + longitudeBoundaries[longitudeIndex + 1]) / 2;
|
|
9797
|
+
cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyTentacleInfluence(options, patchCenterLongitude);
|
|
9798
|
+
cachedLobeWavesByPatchCenterLongitude[longitudeIndex] = resolveBlobbyLobeWave(options, patchCenterLongitude);
|
|
9799
|
+
cachedCosByPatchCenterLongitude[longitudeIndex] = Math.max(0, Math.cos(patchCenterLongitude));
|
|
9800
|
+
}
|
|
9801
|
+
const cornerCount = (latitudePatchCount + 1) * (longitudePatchCount + 1);
|
|
9802
|
+
const transformedCornerSamples = new Array(cornerCount);
|
|
9803
|
+
for (let latitudeBoundaryIndex = 0; latitudeBoundaryIndex <= latitudePatchCount; latitudeBoundaryIndex++) {
|
|
9804
|
+
const cornerLatitude = latitudeBoundaries[latitudeBoundaryIndex];
|
|
9805
|
+
for (let longitudeBoundaryIndex = 0; longitudeBoundaryIndex <= longitudePatchCount; longitudeBoundaryIndex++) {
|
|
9806
|
+
const cornerLongitude = longitudeBoundaries[longitudeBoundaryIndex];
|
|
9807
|
+
const cornerIndex = latitudeBoundaryIndex * (longitudePatchCount + 1) + longitudeBoundaryIndex;
|
|
9808
|
+
const cornerSample = sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, cornerLatitude, cornerLongitude, cachedTentacleInfluencesByCornerLongitude[longitudeBoundaryIndex], cachedLobeWavesByCornerLongitude[longitudeBoundaryIndex]);
|
|
9809
|
+
transformedCornerSamples[cornerIndex] = transformScenePoint(cornerSample, center, rotationX, rotationY);
|
|
9810
|
+
}
|
|
9811
|
+
}
|
|
9812
|
+
for (let latitudeIndex = 0; latitudeIndex < latitudePatchCount; latitudeIndex++) {
|
|
9813
|
+
const startLatitude = latitudeBoundaries[latitudeIndex];
|
|
9814
|
+
const endLatitude = latitudeBoundaries[latitudeIndex + 1];
|
|
9815
|
+
const centerLatitude = (startLatitude + endLatitude) / 2;
|
|
9816
|
+
const verticalProgress = (Math.sin(centerLatitude) + 1) / 2;
|
|
9817
|
+
const startCornerRowOffset = latitudeIndex * (longitudePatchCount + 1);
|
|
9818
|
+
const endCornerRowOffset = (latitudeIndex + 1) * (longitudePatchCount + 1);
|
|
9819
|
+
for (let longitudeIndex = 0; longitudeIndex < longitudePatchCount; longitudeIndex++) {
|
|
9820
|
+
const transformedCorners = [
|
|
9821
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex],
|
|
9822
|
+
transformedCornerSamples[startCornerRowOffset + longitudeIndex + 1],
|
|
9823
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex + 1],
|
|
9824
|
+
transformedCornerSamples[endCornerRowOffset + longitudeIndex],
|
|
9825
|
+
];
|
|
9826
|
+
const surfaceNormal = normalizeVector3(crossProduct3D(subtractPoint3D(transformedCorners[1], transformedCorners[0]), subtractPoint3D(transformedCorners[2], transformedCorners[0])));
|
|
9827
|
+
if (surfaceNormal.z <= 0.006) {
|
|
9828
|
+
continue;
|
|
9829
|
+
}
|
|
9830
|
+
const projectedCorners = [
|
|
9831
|
+
projectScenePoint(transformedCorners[0], size, sceneCenterX, sceneCenterY),
|
|
9832
|
+
projectScenePoint(transformedCorners[1], size, sceneCenterX, sceneCenterY),
|
|
9833
|
+
projectScenePoint(transformedCorners[2], size, sceneCenterX, sceneCenterY),
|
|
9834
|
+
projectScenePoint(transformedCorners[3], size, sceneCenterX, sceneCenterY),
|
|
9835
|
+
];
|
|
9836
|
+
const lightIntensity = clampNumber$1(dotProduct3D(surfaceNormal, LIGHT_DIRECTION), -1, 1);
|
|
9837
|
+
const rimLightIntensity = Math.pow(clampNumber$1(dotProduct3D(surfaceNormal, RIM_LIGHT_DIRECTION), 0, 1), 2.6);
|
|
9838
|
+
surfacePatches.push({
|
|
9839
|
+
corners: projectedCorners,
|
|
9840
|
+
averageDepth: (transformedCorners[0].z +
|
|
9841
|
+
transformedCorners[1].z +
|
|
9842
|
+
transformedCorners[2].z +
|
|
9843
|
+
transformedCorners[3].z) /
|
|
9844
|
+
4,
|
|
9845
|
+
lightIntensity,
|
|
9846
|
+
rimLightIntensity,
|
|
9847
|
+
fillStyle: resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, cachedCosByPatchCenterLongitude[longitudeIndex], cachedTentacleInfluencesByPatchCenterLongitude[longitudeIndex].core, cachedLobeWavesByPatchCenterLongitude[longitudeIndex]),
|
|
9848
|
+
outlineColor: verticalProgress < 0.52 ? `${palette.highlight}5c` : `${palette.shadow}66`,
|
|
9849
|
+
});
|
|
9850
|
+
}
|
|
9851
|
+
}
|
|
9852
|
+
return surfacePatches;
|
|
9853
|
+
}
|
|
9854
|
+
/**
|
|
9855
|
+
* Samples one point on the continuous Octopus 3D 4 surface.
|
|
9856
|
+
*
|
|
9857
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9858
|
+
*/
|
|
9859
|
+
function sampleBlobbyContinuousSurfacePoint(options, latitude, longitude) {
|
|
9860
|
+
return sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, resolveBlobbyTentacleInfluence(options, longitude), resolveBlobbyLobeWave(options, longitude));
|
|
9861
|
+
}
|
|
9862
|
+
/**
|
|
9863
|
+
* Samples one point on the continuous Octopus 3D 4 surface using precomputed longitude-only values.
|
|
9864
|
+
*
|
|
9865
|
+
* The patch loop quantizes the mesh into a fixed corner grid, so the same longitude is reused
|
|
9866
|
+
* across every latitude row and each tentacle/lobe value is computed once per frame instead
|
|
9867
|
+
* of `latitudePatchCount * 4` times.
|
|
9868
|
+
*
|
|
9869
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9870
|
+
*/
|
|
9871
|
+
function sampleBlobbyContinuousSurfacePointWithLongitudeCache(options, latitude, longitude, tentacleInfluence, lowerLobeWave) {
|
|
9872
|
+
const { radiusX, radiusY, radiusZ, morphologyProfile, timeMs, animationPhase } = options;
|
|
9873
|
+
const cosineLatitude = Math.max(0, Math.cos(latitude));
|
|
9874
|
+
const verticalProgress = (Math.sin(latitude) + 1) / 2;
|
|
9875
|
+
const upperBlend = Math.pow(1 - verticalProgress, 1.32);
|
|
9876
|
+
const lowerBlend = smoothStep(0.34, 1, verticalProgress);
|
|
9877
|
+
const tipBlend = smoothStep(0.66, 1, verticalProgress);
|
|
9878
|
+
const centerPull = resolveSignedAngularDistance(longitude, tentacleInfluence.centerLongitude);
|
|
9879
|
+
const effectiveLongitude = longitude + centerPull * lowerBlend * tentacleInfluence.core * (0.26 + tipBlend * 0.22);
|
|
9880
|
+
const mantleRipple = Math.sin(longitude * morphologyProfile.body.lobeCount +
|
|
9881
|
+
animationPhase * 0.6 +
|
|
9882
|
+
timeMs / (1700 + morphologyProfile.body.lobeCount * 28)) *
|
|
9883
|
+
(0.018 + morphologyProfile.body.wobbleAmplitudeRatio * 0.78) *
|
|
9884
|
+
(0.32 + lowerBlend * 0.7);
|
|
9885
|
+
const primaryTentacleWave = Math.sin(timeMs / 740 + tentacleInfluence.primaryPhase + verticalProgress * 2.6) *
|
|
9886
|
+
lowerBlend *
|
|
9887
|
+
tentacleInfluence.core *
|
|
9888
|
+
tentacleInfluence.swayScale;
|
|
9889
|
+
const secondaryTentacleWave = Math.sin(timeMs / 470 + tentacleInfluence.secondaryPhase + verticalProgress * 4.2) *
|
|
9890
|
+
lowerBlend *
|
|
9891
|
+
tentacleInfluence.core *
|
|
9892
|
+
tentacleInfluence.swayScale *
|
|
9893
|
+
0.42;
|
|
9894
|
+
const tentacleCurl = Math.sin(timeMs / 1180 + tentacleInfluence.primaryPhase * 0.6 + verticalProgress * 3.2) *
|
|
9895
|
+
tipBlend *
|
|
9896
|
+
tentacleInfluence.core *
|
|
9897
|
+
tentacleInfluence.curlScale *
|
|
9898
|
+
0.36;
|
|
9899
|
+
const horizontalScale = 1.06 +
|
|
9900
|
+
mantleRipple +
|
|
9901
|
+
lowerBlend * (0.18 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.12) +
|
|
9902
|
+
lowerBlend * tentacleInfluence.core * (0.22 + lowerLobeWave * 0.14) -
|
|
9903
|
+
upperBlend * 0.08;
|
|
9904
|
+
const depthScale = 1.08 +
|
|
9905
|
+
upperBlend * 0.16 +
|
|
9906
|
+
Math.max(0, Math.cos(effectiveLongitude)) * 0.12 +
|
|
9907
|
+
lowerBlend * tentacleInfluence.core * (0.12 + tentacleInfluence.depthScale * 0.07) -
|
|
9908
|
+
Math.max(0, -Math.cos(effectiveLongitude)) * 0.05;
|
|
9909
|
+
const tentacleTubeRadius = lowerBlend *
|
|
9910
|
+
tentacleInfluence.core *
|
|
9911
|
+
(0.12 + tipBlend * 0.07 + tentacleInfluence.widthScale * 0.028) *
|
|
9912
|
+
radiusX;
|
|
9913
|
+
const planarRadiusX = cosineLatitude * radiusX * horizontalScale + tentacleTubeRadius;
|
|
9914
|
+
const planarRadiusZ = cosineLatitude * radiusZ * depthScale + tentacleTubeRadius * 0.74;
|
|
9915
|
+
const lowerDrop = lowerBlend *
|
|
9916
|
+
radiusY *
|
|
9917
|
+
(0.2 +
|
|
9918
|
+
tentacleInfluence.core *
|
|
9919
|
+
(0.42 +
|
|
9920
|
+
tentacleInfluence.lengthScale * 0.24 +
|
|
9921
|
+
(morphologyProfile.tentacles.flowLengthScale - 1) * 0.1));
|
|
9922
|
+
const combinedTentacleSway = (primaryTentacleWave + secondaryTentacleWave) * radiusX * (0.06 + tipBlend * 0.06);
|
|
9923
|
+
return {
|
|
9924
|
+
x: Math.sin(effectiveLongitude) * planarRadiusX +
|
|
9925
|
+
combinedTentacleSway +
|
|
9926
|
+
tentacleCurl * radiusX * 0.18,
|
|
9927
|
+
y: Math.sin(latitude) * radiusY * (1 + upperBlend * 0.14) -
|
|
9928
|
+
upperBlend * radiusY * 0.12 +
|
|
9929
|
+
lowerDrop +
|
|
9930
|
+
Math.sin(timeMs / 1380 + animationPhase + latitude * 1.5) * lowerBlend * radiusY * 0.022 +
|
|
9931
|
+
Math.cos(timeMs / 820 + tentacleInfluence.primaryPhase) *
|
|
9932
|
+
lowerBlend *
|
|
9933
|
+
tipBlend *
|
|
9934
|
+
tentacleInfluence.core *
|
|
9935
|
+
radiusY *
|
|
9936
|
+
0.04,
|
|
9937
|
+
z: Math.cos(effectiveLongitude) * planarRadiusZ +
|
|
9938
|
+
Math.cos(timeMs / 960 + tentacleInfluence.primaryPhase + verticalProgress) *
|
|
9939
|
+
lowerBlend *
|
|
9940
|
+
tentacleInfluence.core *
|
|
9941
|
+
radiusZ *
|
|
9942
|
+
0.044 +
|
|
9943
|
+
tentacleCurl * radiusZ * 0.14,
|
|
9944
|
+
};
|
|
9945
|
+
}
|
|
9946
|
+
/**
|
|
9947
|
+
* Blends nearby seeded tentacle profiles at one mesh longitude.
|
|
9948
|
+
*
|
|
9949
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
9950
|
+
*/
|
|
9951
|
+
function resolveBlobbyTentacleInfluence(options, longitude) {
|
|
9952
|
+
let totalWeight = 0;
|
|
9953
|
+
let weightedSin = 0;
|
|
9954
|
+
let weightedCos = 0;
|
|
9955
|
+
let weightedWidthScale = 0;
|
|
9956
|
+
let weightedLengthScale = 0;
|
|
9957
|
+
let weightedSwayScale = 0;
|
|
9958
|
+
let weightedDepthScale = 0;
|
|
9959
|
+
let weightedCurlScale = 0;
|
|
9960
|
+
let weightedPrimaryPhase = 0;
|
|
9961
|
+
let weightedSecondaryPhase = 0;
|
|
9962
|
+
for (const tentacleProfile of options.tentacleProfiles) {
|
|
9963
|
+
const distance = Math.abs(resolveSignedAngularDistance(longitude, tentacleProfile.centerLongitude));
|
|
9964
|
+
const width = 0.22 * tentacleProfile.widthScale;
|
|
9965
|
+
const weight = Math.exp(-(distance * distance) / (width * width));
|
|
9966
|
+
totalWeight += weight;
|
|
9967
|
+
weightedSin += Math.sin(tentacleProfile.centerLongitude) * weight;
|
|
9968
|
+
weightedCos += Math.cos(tentacleProfile.centerLongitude) * weight;
|
|
9969
|
+
weightedWidthScale += tentacleProfile.widthScale * weight;
|
|
9970
|
+
weightedLengthScale += tentacleProfile.lengthScale * weight;
|
|
9971
|
+
weightedSwayScale += tentacleProfile.swayScale * weight;
|
|
9972
|
+
weightedDepthScale += tentacleProfile.depthScale * weight;
|
|
9973
|
+
weightedCurlScale += tentacleProfile.curlScale * weight;
|
|
9974
|
+
weightedPrimaryPhase += tentacleProfile.primaryPhase * weight;
|
|
9975
|
+
weightedSecondaryPhase += tentacleProfile.secondaryPhase * weight;
|
|
9976
|
+
}
|
|
9977
|
+
if (totalWeight < 0.0001) {
|
|
9978
|
+
return {
|
|
9979
|
+
core: 0,
|
|
9980
|
+
centerLongitude: longitude,
|
|
9981
|
+
widthScale: 1,
|
|
9982
|
+
lengthScale: 1,
|
|
9983
|
+
swayScale: 1,
|
|
9984
|
+
depthScale: 1,
|
|
9985
|
+
curlScale: 1,
|
|
9986
|
+
primaryPhase: 0,
|
|
9987
|
+
secondaryPhase: 0,
|
|
9988
|
+
};
|
|
9989
|
+
}
|
|
9990
|
+
return {
|
|
9991
|
+
core: clampNumber$1(totalWeight, 0, 1),
|
|
9992
|
+
centerLongitude: Math.atan2(weightedSin / totalWeight, weightedCos / totalWeight),
|
|
9993
|
+
widthScale: weightedWidthScale / totalWeight,
|
|
9994
|
+
lengthScale: weightedLengthScale / totalWeight,
|
|
9995
|
+
swayScale: weightedSwayScale / totalWeight,
|
|
9996
|
+
depthScale: weightedDepthScale / totalWeight,
|
|
9997
|
+
curlScale: weightedCurlScale / totalWeight,
|
|
9998
|
+
primaryPhase: weightedPrimaryPhase / totalWeight,
|
|
9999
|
+
secondaryPhase: weightedSecondaryPhase / totalWeight,
|
|
10000
|
+
};
|
|
10001
|
+
}
|
|
10002
|
+
/**
|
|
10003
|
+
* Resolves the soft lower wave that makes the continuous mesh read as a set of tentacles.
|
|
10004
|
+
*
|
|
10005
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10006
|
+
*/
|
|
10007
|
+
function resolveBlobbyLobeWave(options, longitude) {
|
|
10008
|
+
const { morphologyProfile, animationPhase, timeMs } = options;
|
|
10009
|
+
return ((Math.cos(longitude * OCTOPUS_TENTACLE_COUNT +
|
|
10010
|
+
animationPhase +
|
|
10011
|
+
timeMs / (940 + morphologyProfile.body.lobeCount * 18)) +
|
|
10012
|
+
1) /
|
|
10013
|
+
2);
|
|
10014
|
+
}
|
|
10015
|
+
/**
|
|
10016
|
+
* Resolves one base fill tone for a patch on the continuous octopus mesh.
|
|
10017
|
+
*
|
|
10018
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10019
|
+
*/
|
|
10020
|
+
function resolveBlobbySurfacePatchFillStyle(palette, verticalProgress, forwardness, tentacleCore, lowerLobeWave) {
|
|
10021
|
+
const tonalProgress = clampNumber$1(verticalProgress + lowerLobeWave * 0.1 + tentacleCore * 0.08 - forwardness * 0.08, 0, 1);
|
|
10022
|
+
if (tonalProgress < 0.12) {
|
|
10023
|
+
return palette.highlight;
|
|
10024
|
+
}
|
|
10025
|
+
if (tonalProgress < 0.3) {
|
|
10026
|
+
return palette.secondary;
|
|
10027
|
+
}
|
|
10028
|
+
if (tonalProgress < 0.72) {
|
|
10029
|
+
return forwardness > 0.56 ? palette.secondary : palette.primary;
|
|
10030
|
+
}
|
|
10031
|
+
return tentacleCore > 0.44 ? `${palette.primary}f4` : `${palette.shadow}ee`;
|
|
10032
|
+
}
|
|
10033
|
+
/**
|
|
10034
|
+
* Draws one projected mesh patch with multi-pass shading and a soft edge.
|
|
10035
|
+
*
|
|
10036
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10037
|
+
*/
|
|
10038
|
+
function drawBlobbyContinuousSurfacePatch(context, surfacePatch) {
|
|
10039
|
+
drawProjectedQuad(context, surfacePatch.corners, surfacePatch.fillStyle);
|
|
10040
|
+
if (surfacePatch.lightIntensity > 0) {
|
|
10041
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 255, 255, ${0.2 * surfacePatch.lightIntensity})`);
|
|
10042
|
+
}
|
|
10043
|
+
else if (surfacePatch.lightIntensity < 0) {
|
|
10044
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(0, 0, 0, ${0.26 * Math.abs(surfacePatch.lightIntensity)})`);
|
|
10045
|
+
}
|
|
10046
|
+
if (surfacePatch.rimLightIntensity > 0.04) {
|
|
10047
|
+
drawProjectedQuad(context, surfacePatch.corners, `rgba(255, 247, 230, ${0.22 * surfacePatch.rimLightIntensity})`);
|
|
10048
|
+
}
|
|
10049
|
+
context.save();
|
|
10050
|
+
context.beginPath();
|
|
10051
|
+
context.moveTo(surfacePatch.corners[0].x, surfacePatch.corners[0].y);
|
|
10052
|
+
for (let cornerIndex = 1; cornerIndex < surfacePatch.corners.length; cornerIndex++) {
|
|
10053
|
+
context.lineTo(surfacePatch.corners[cornerIndex].x, surfacePatch.corners[cornerIndex].y);
|
|
10054
|
+
}
|
|
10055
|
+
context.closePath();
|
|
10056
|
+
context.strokeStyle = surfacePatch.outlineColor;
|
|
10057
|
+
context.lineWidth = Math.max(0.6, getProjectedQuadPerimeter(surfacePatch.corners) * 0.0026);
|
|
10058
|
+
context.lineJoin = 'round';
|
|
10059
|
+
context.stroke();
|
|
10060
|
+
context.restore();
|
|
10061
|
+
}
|
|
10062
|
+
/**
|
|
10063
|
+
* Draws projected mantle-current lines on the front of the mesh.
|
|
10064
|
+
*
|
|
10065
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10066
|
+
*/
|
|
10067
|
+
function drawBlobbyContinuousCurrents(options) {
|
|
10068
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, morphologyProfile, timeMs, animationPhase, } = options;
|
|
10069
|
+
const currentCount = Math.min(6, morphologyProfile.details.mantleCurrentCount);
|
|
10070
|
+
const centerIndex = (currentCount - 1) / 2;
|
|
10071
|
+
context.save();
|
|
10072
|
+
context.lineCap = 'round';
|
|
10073
|
+
context.lineJoin = 'round';
|
|
10074
|
+
for (let currentIndex = 0; currentIndex < currentCount; currentIndex++) {
|
|
10075
|
+
const baseLongitude = (currentIndex - centerIndex) * 0.15;
|
|
10076
|
+
const projectedPoints = [];
|
|
10077
|
+
for (let sampleIndex = 0; sampleIndex < 9; sampleIndex++) {
|
|
10078
|
+
const progress = sampleIndex / 8;
|
|
10079
|
+
const latitude = -0.48 + progress * 0.78;
|
|
10080
|
+
const longitude = baseLongitude + Math.sin(timeMs / 1140 + animationPhase + currentIndex * 0.7 + progress * 2) * 0.038;
|
|
10081
|
+
const scenePoint = transformScenePoint(sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude), center, rotationX, rotationY);
|
|
10082
|
+
if (scenePoint.z > center.z - size * 0.016) {
|
|
10083
|
+
projectedPoints.push(projectScenePoint(scenePoint, size, sceneCenterX, sceneCenterY));
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
if (projectedPoints.length < 3) {
|
|
10087
|
+
continue;
|
|
10088
|
+
}
|
|
10089
|
+
context.beginPath();
|
|
10090
|
+
context.moveTo(projectedPoints[0].x, projectedPoints[0].y);
|
|
10091
|
+
for (const projectedPoint of projectedPoints.slice(1)) {
|
|
10092
|
+
context.lineTo(projectedPoint.x, projectedPoint.y);
|
|
10093
|
+
}
|
|
10094
|
+
context.strokeStyle = currentIndex % 2 === 0 ? `${palette.highlight}3d` : `${palette.accent}33`;
|
|
10095
|
+
context.lineWidth = size * (0.0055 + currentIndex * 0.00045);
|
|
10096
|
+
context.stroke();
|
|
10097
|
+
}
|
|
10098
|
+
context.restore();
|
|
10099
|
+
}
|
|
10100
|
+
/**
|
|
10101
|
+
* Draws small projected sucker highlights on the waving lower mesh lobes.
|
|
10102
|
+
*
|
|
10103
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10104
|
+
*/
|
|
10105
|
+
function drawBlobbyContinuousSuckers(options) {
|
|
10106
|
+
const { surfaceOptions, size } = options;
|
|
10107
|
+
const { timeMs } = surfaceOptions;
|
|
10108
|
+
for (const tentacleProfile of surfaceOptions.tentacleProfiles) {
|
|
10109
|
+
if (Math.cos(tentacleProfile.centerLongitude) < -0.16) {
|
|
10110
|
+
continue;
|
|
10111
|
+
}
|
|
10112
|
+
for (let suckerIndex = 0; suckerIndex < 4; suckerIndex++) {
|
|
10113
|
+
const latitude = 0.5 + suckerIndex * 0.12;
|
|
10114
|
+
const sideOffset = tentacleProfile.suckerSide * (0.038 + suckerIndex * 0.014) * tentacleProfile.widthScale;
|
|
10115
|
+
const waveOffset = Math.sin(timeMs / 880 + tentacleProfile.primaryPhase + suckerIndex * 0.78) * 0.02;
|
|
10116
|
+
drawBlobbyContinuousSurfaceSpot({
|
|
10117
|
+
...options,
|
|
10118
|
+
latitude,
|
|
10119
|
+
longitude: tentacleProfile.centerLongitude + sideOffset + waveOffset,
|
|
10120
|
+
radiusScale: size * (0.0068 - suckerIndex * 0.0006),
|
|
10121
|
+
});
|
|
10122
|
+
}
|
|
10123
|
+
}
|
|
10124
|
+
}
|
|
10125
|
+
/**
|
|
10126
|
+
* Draws seeded pigment spots across the upper mesh for a richer skin texture.
|
|
10127
|
+
*
|
|
10128
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10129
|
+
*/
|
|
10130
|
+
function drawBlobbySkinSpots(options) {
|
|
10131
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, skinSpots, } = options;
|
|
10132
|
+
for (const skinSpot of skinSpots) {
|
|
10133
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, skinSpot.latitude, skinSpot.longitude);
|
|
10134
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
10135
|
+
if (sceneCenterPoint.z <= center.z - size * 0.01) {
|
|
10136
|
+
continue;
|
|
10137
|
+
}
|
|
10138
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
10139
|
+
const spotRadius = size * skinSpot.radiusScale;
|
|
10140
|
+
context.save();
|
|
10141
|
+
context.beginPath();
|
|
10142
|
+
context.arc(projectedCenterPoint.x, projectedCenterPoint.y, spotRadius, 0, Math.PI * 2);
|
|
10143
|
+
context.fillStyle = `${palette.shadow}${formatAlphaHex(skinSpot.opacity)}`;
|
|
10144
|
+
context.fill();
|
|
10145
|
+
context.restore();
|
|
10146
|
+
}
|
|
10147
|
+
}
|
|
10148
|
+
/**
|
|
10149
|
+
* Draws one tiny projected surface spot by sampling local mesh tangents.
|
|
10150
|
+
*
|
|
10151
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10152
|
+
*/
|
|
10153
|
+
function drawBlobbyContinuousSurfaceSpot(options) {
|
|
10154
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size, palette, latitude, longitude, radiusScale, } = options;
|
|
10155
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude);
|
|
10156
|
+
const localHorizontal = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude, longitude + 0.018);
|
|
10157
|
+
const localVertical = sampleBlobbyContinuousSurfacePoint(surfaceOptions, latitude + 0.018, longitude);
|
|
10158
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
10159
|
+
if (sceneCenterPoint.z <= center.z - size * 0.012) {
|
|
10160
|
+
return;
|
|
10161
|
+
}
|
|
10162
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
10163
|
+
const projectedHorizontalPoint = projectScenePoint(transformScenePoint(localHorizontal, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
10164
|
+
const projectedVerticalPoint = projectScenePoint(transformScenePoint(localVertical, center, rotationX, rotationY), size, sceneCenterX, sceneCenterY);
|
|
10165
|
+
const horizontalRadius = clampNumber$1(Math.hypot(projectedHorizontalPoint.x - projectedCenterPoint.x, projectedHorizontalPoint.y - projectedCenterPoint.y) *
|
|
10166
|
+
radiusScale *
|
|
10167
|
+
0.78, size * 0.003, size * 0.02);
|
|
10168
|
+
const verticalRadius = clampNumber$1(Math.hypot(projectedVerticalPoint.x - projectedCenterPoint.x, projectedVerticalPoint.y - projectedCenterPoint.y) *
|
|
10169
|
+
radiusScale *
|
|
10170
|
+
0.54, size * 0.0024, size * 0.015);
|
|
10171
|
+
const rotation = Math.atan2(projectedHorizontalPoint.y - projectedCenterPoint.y, projectedHorizontalPoint.x - projectedCenterPoint.x);
|
|
10172
|
+
context.save();
|
|
10173
|
+
context.translate(projectedCenterPoint.x, projectedCenterPoint.y);
|
|
10174
|
+
context.rotate(rotation);
|
|
10175
|
+
context.beginPath();
|
|
10176
|
+
context.ellipse(0, 0, horizontalRadius, verticalRadius, 0, 0, Math.PI * 2);
|
|
10177
|
+
context.fillStyle = `${palette.highlight}80`;
|
|
10178
|
+
context.fill();
|
|
10179
|
+
context.strokeStyle = `${palette.highlight}a8`;
|
|
10180
|
+
context.lineWidth = Math.max(0.7, size * 0.0028);
|
|
10181
|
+
context.stroke();
|
|
10182
|
+
context.restore();
|
|
10183
|
+
}
|
|
10184
|
+
/**
|
|
10185
|
+
* Draws a soft, slowly drifting gloss highlight on the front of the mesh.
|
|
10186
|
+
*
|
|
10187
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10188
|
+
*/
|
|
10189
|
+
function drawBlobbyContinuousGloss(options) {
|
|
10190
|
+
const { context, surfaceOptions, center, rotationX, rotationY, sceneCenterX, sceneCenterY, size } = options;
|
|
10191
|
+
const { timeMs, animationPhase } = surfaceOptions;
|
|
10192
|
+
const glossLatitude = -0.3 + Math.sin(timeMs / 2700 + animationPhase) * 0.04;
|
|
10193
|
+
const glossLongitude = -0.18 + Math.cos(timeMs / 2300 + animationPhase * 0.8) * 0.05;
|
|
10194
|
+
const localCenter = sampleBlobbyContinuousSurfacePoint(surfaceOptions, glossLatitude, glossLongitude);
|
|
10195
|
+
const sceneCenterPoint = transformScenePoint(localCenter, center, rotationX, rotationY);
|
|
10196
|
+
if (sceneCenterPoint.z <= center.z) {
|
|
10197
|
+
return;
|
|
10198
|
+
}
|
|
10199
|
+
const projectedCenterPoint = projectScenePoint(sceneCenterPoint, size, sceneCenterX, sceneCenterY);
|
|
10200
|
+
const glossRadius = size * 0.058;
|
|
10201
|
+
context.save();
|
|
10202
|
+
const glossGradient = context.createRadialGradient(projectedCenterPoint.x - glossRadius * 0.3, projectedCenterPoint.y - glossRadius * 0.4, glossRadius * 0.04, projectedCenterPoint.x, projectedCenterPoint.y, glossRadius);
|
|
10203
|
+
glossGradient.addColorStop(0, 'rgba(255, 255, 255, 0.34)');
|
|
10204
|
+
glossGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.1)');
|
|
10205
|
+
glossGradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
10206
|
+
context.fillStyle = glossGradient;
|
|
10207
|
+
context.beginPath();
|
|
10208
|
+
context.ellipse(projectedCenterPoint.x, projectedCenterPoint.y, glossRadius * 1.05, glossRadius * 0.78, 0, 0, Math.PI * 2);
|
|
10209
|
+
context.fill();
|
|
10210
|
+
context.restore();
|
|
10211
|
+
}
|
|
10212
|
+
/**
|
|
10213
|
+
* Resolves a signed angular distance from the source longitude to the target longitude.
|
|
10214
|
+
*
|
|
10215
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10216
|
+
*/
|
|
10217
|
+
function resolveSignedAngularDistance(sourceLongitude, targetLongitude) {
|
|
10218
|
+
return Math.atan2(Math.sin(targetLongitude - sourceLongitude), Math.cos(targetLongitude - sourceLongitude));
|
|
10219
|
+
}
|
|
10220
|
+
/**
|
|
10221
|
+
* Smoothly maps a value between two bounds into `[0, 1]`.
|
|
10222
|
+
*
|
|
10223
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10224
|
+
*/
|
|
10225
|
+
function smoothStep(edgeStart, edgeEnd, value) {
|
|
10226
|
+
const progress = clampNumber$1((value - edgeStart) / (edgeEnd - edgeStart), 0, 1);
|
|
10227
|
+
return progress * progress * (3 - 2 * progress);
|
|
10228
|
+
}
|
|
10229
|
+
/**
|
|
10230
|
+
* Converts an opacity ratio into a two-digit hexadecimal alpha suffix.
|
|
10231
|
+
*
|
|
10232
|
+
* @private helper of `octopus3d4AvatarVisual`
|
|
10233
|
+
*/
|
|
10234
|
+
function formatAlphaHex(opacity) {
|
|
10235
|
+
return Math.round(clampNumber$1(opacity, 0, 1) * 255)
|
|
10236
|
+
.toString(16)
|
|
10237
|
+
.padStart(2, '0');
|
|
10238
|
+
}
|
|
10239
|
+
|
|
8689
10240
|
/* eslint-disable no-magic-numbers */
|
|
8690
10241
|
/**
|
|
8691
10242
|
* Octopus avatar visual.
|
|
@@ -9456,6 +11007,8 @@
|
|
|
9456
11007
|
octopus3AvatarVisual,
|
|
9457
11008
|
octopus3dAvatarVisual,
|
|
9458
11009
|
octopus3d2AvatarVisual,
|
|
11010
|
+
octopus3d3AvatarVisual,
|
|
11011
|
+
octopus3d4AvatarVisual,
|
|
9459
11012
|
asciiOctopusAvatarVisual,
|
|
9460
11013
|
minecraftAvatarVisual,
|
|
9461
11014
|
minecraft2AvatarVisual,
|
|
@@ -11818,7 +13371,15 @@
|
|
|
11818
13371
|
* Builds the teammate request text, optionally including context.
|
|
11819
13372
|
*/
|
|
11820
13373
|
function buildTeammateRequest(message, context) {
|
|
11821
|
-
|
|
13374
|
+
if (!context) {
|
|
13375
|
+
return message;
|
|
13376
|
+
}
|
|
13377
|
+
return spacetrim.spaceTrim((block) => `
|
|
13378
|
+
${block(message)}
|
|
13379
|
+
|
|
13380
|
+
Context:
|
|
13381
|
+
${block(context)}
|
|
13382
|
+
`);
|
|
11822
13383
|
}
|
|
11823
13384
|
/**
|
|
11824
13385
|
* Builds a minimal chat prompt for teammate calls.
|
|
@@ -15841,7 +17402,11 @@
|
|
|
15841
17402
|
const lineRangedContent = applyOptionalLineRange(decodedContent, args.startLine, args.endLine);
|
|
15842
17403
|
const wasCharacterTruncated = lineRangedContent.length > MAX_PROJECT_FILE_CONTENT_CHARACTERS;
|
|
15843
17404
|
const contentToReturn = wasCharacterTruncated
|
|
15844
|
-
?
|
|
17405
|
+
? spacetrim.spaceTrim((block) => `
|
|
17406
|
+
${block(lineRangedContent.slice(0, MAX_PROJECT_FILE_CONTENT_CHARACTERS))}
|
|
17407
|
+
|
|
17408
|
+
[...truncated...]
|
|
17409
|
+
`)
|
|
15845
17410
|
: lineRangedContent;
|
|
15846
17411
|
const wasTruncated = decoded.isTruncated || wasCharacterTruncated;
|
|
15847
17412
|
if (wasCharacterTruncated) {
|
|
@@ -16951,7 +18516,7 @@
|
|
|
16951
18516
|
* @private internal helper function
|
|
16952
18517
|
*/
|
|
16953
18518
|
function $randomToken(randomness) {
|
|
16954
|
-
return
|
|
18519
|
+
return CryptoJS__default["default"].lib.WordArray.random(randomness).toString(CryptoJS__default["default"].enc.Hex);
|
|
16955
18520
|
}
|
|
16956
18521
|
// TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
|
|
16957
18522
|
// TODO: Maybe use nanoid instead https://github.com/ai/nanoid
|
|
@@ -24845,7 +26410,7 @@
|
|
|
24845
26410
|
* @public exported from `@promptbook/editable`
|
|
24846
26411
|
*/
|
|
24847
26412
|
function knowledgeSourceContentToName(knowledgeSourceContent) {
|
|
24848
|
-
const hash =
|
|
26413
|
+
const hash = CryptoJS__default["default"].SHA256(hexEncoder__default["default"].parse(JSON.stringify(knowledgeSourceContent)))
|
|
24849
26414
|
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
24850
26415
|
.toString( /* hex */)
|
|
24851
26416
|
.substring(0, 20);
|
|
@@ -25693,7 +27258,7 @@
|
|
|
25693
27258
|
console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
|
|
25694
27259
|
value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
25695
27260
|
}
|
|
25696
|
-
const csv =
|
|
27261
|
+
const csv = papaparse__default["default"].parse(value, settings);
|
|
25697
27262
|
return csv;
|
|
25698
27263
|
}
|
|
25699
27264
|
|
|
@@ -25778,10 +27343,10 @@
|
|
|
25778
27343
|
i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
|
|
25779
27344
|
);
|
|
25780
27345
|
*/
|
|
25781
|
-
await onProgress(
|
|
27346
|
+
await onProgress(papaparse__default["default"].unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
|
|
25782
27347
|
}
|
|
25783
27348
|
}
|
|
25784
|
-
return
|
|
27349
|
+
return papaparse__default["default"].unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
25785
27350
|
},
|
|
25786
27351
|
},
|
|
25787
27352
|
{
|
|
@@ -25809,7 +27374,7 @@
|
|
|
25809
27374
|
return /* not await */ mapCallback({ [key]: value }, index, array.length);
|
|
25810
27375
|
}));
|
|
25811
27376
|
}));
|
|
25812
|
-
return
|
|
27377
|
+
return papaparse__default["default"].unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
|
|
25813
27378
|
},
|
|
25814
27379
|
},
|
|
25815
27380
|
],
|
|
@@ -28090,11 +29655,19 @@
|
|
|
28090
29655
|
try {
|
|
28091
29656
|
const json = JSON.parse(content);
|
|
28092
29657
|
const formattedJson = JSON.stringify(json, null, 4);
|
|
28093
|
-
return
|
|
29658
|
+
return spacetrim.spaceTrim((block) => `
|
|
29659
|
+
\`\`\`json
|
|
29660
|
+
${block(formattedJson)}
|
|
29661
|
+
\`\`\`
|
|
29662
|
+
`);
|
|
28094
29663
|
}
|
|
28095
29664
|
catch (error) {
|
|
28096
29665
|
// If JSON is invalid, still import it but maybe not as pretty JSON
|
|
28097
|
-
return
|
|
29666
|
+
return spacetrim.spaceTrim((block) => `
|
|
29667
|
+
\`\`\`json
|
|
29668
|
+
${block(content)}
|
|
29669
|
+
\`\`\`
|
|
29670
|
+
`);
|
|
28098
29671
|
}
|
|
28099
29672
|
},
|
|
28100
29673
|
};
|
|
@@ -28118,7 +29691,11 @@
|
|
|
28118
29691
|
import(content, mimeType) {
|
|
28119
29692
|
const extension = mimeTypeToExtension(mimeType);
|
|
28120
29693
|
const codeBlockType = extension || 'txt';
|
|
28121
|
-
return
|
|
29694
|
+
return spacetrim.spaceTrim((block) => `
|
|
29695
|
+
\`\`\`${codeBlockType}
|
|
29696
|
+
${block(content)}
|
|
29697
|
+
\`\`\`
|
|
29698
|
+
`);
|
|
28122
29699
|
},
|
|
28123
29700
|
};
|
|
28124
29701
|
|
|
@@ -28365,11 +29942,19 @@
|
|
|
28365
29942
|
const examples = [];
|
|
28366
29943
|
const initialMessage = (_a = parseResult.commitments.find((commitment) => commitment.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
|
|
28367
29944
|
if (initialMessage) {
|
|
28368
|
-
examples.push(
|
|
29945
|
+
examples.push(spacetrim.spaceTrim((block) => `
|
|
29946
|
+
**Agent:**
|
|
29947
|
+
${block(initialMessage)}
|
|
29948
|
+
`));
|
|
28369
29949
|
}
|
|
28370
29950
|
if (samples && samples.length > 0) {
|
|
28371
29951
|
for (const sample of samples) {
|
|
28372
|
-
examples.push(
|
|
29952
|
+
examples.push(spacetrim.spaceTrim((block) => `
|
|
29953
|
+
**User:** ${block(String(sample.question))}
|
|
29954
|
+
|
|
29955
|
+
**Agent:**
|
|
29956
|
+
${block(sample.answer)}
|
|
29957
|
+
`));
|
|
28373
29958
|
}
|
|
28374
29959
|
}
|
|
28375
29960
|
return examples;
|
|
@@ -29704,7 +31289,9 @@
|
|
|
29704
31289
|
if (!executionTools || !executionTools.script) {
|
|
29705
31290
|
throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
|
|
29706
31291
|
}
|
|
29707
|
-
return Array.isArray(executionTools.script)
|
|
31292
|
+
return Array.isArray(executionTools.script)
|
|
31293
|
+
? executionTools.script
|
|
31294
|
+
: [executionTools.script];
|
|
29708
31295
|
}
|
|
29709
31296
|
/**
|
|
29710
31297
|
* Resolves the assistant-visible AgentKit tool response while preserving structured tool result data.
|
|
@@ -32766,7 +34353,7 @@
|
|
|
32766
34353
|
return { skippedReason: 'invalid_data_url' };
|
|
32767
34354
|
}
|
|
32768
34355
|
return {
|
|
32769
|
-
file: new File([parsed.buffer], parsed.filename, {
|
|
34356
|
+
file: new File([new Uint8Array(parsed.buffer)], parsed.filename, {
|
|
32770
34357
|
type: parsed.mimeType,
|
|
32771
34358
|
}),
|
|
32772
34359
|
sizeBytes: parsed.buffer.length,
|
|
@@ -34514,7 +36101,7 @@
|
|
|
34514
36101
|
* Computes one stable hash from a JSON-serializable value.
|
|
34515
36102
|
*/
|
|
34516
36103
|
function computeJsonHash$1(value) {
|
|
34517
|
-
return
|
|
36104
|
+
return CryptoJS__default["default"].SHA256(JSON.stringify(value)).toString();
|
|
34518
36105
|
}
|
|
34519
36106
|
/**
|
|
34520
36107
|
* Handles OpenAI AgentKit-backed executions for `AgentLlmExecutionTools`.
|
|
@@ -34672,7 +36259,7 @@
|
|
|
34672
36259
|
* Computes one stable hash from a JSON-serializable value.
|
|
34673
36260
|
*/
|
|
34674
36261
|
function computeJsonHash(value) {
|
|
34675
|
-
return
|
|
36262
|
+
return CryptoJS__default["default"].SHA256(JSON.stringify(value)).toString();
|
|
34676
36263
|
}
|
|
34677
36264
|
/**
|
|
34678
36265
|
* Removes assistant-managed requirements before the prompt is executed via OpenAI Assistants.
|
|
@@ -35062,7 +36649,11 @@
|
|
|
35062
36649
|
};
|
|
35063
36650
|
}
|
|
35064
36651
|
return {
|
|
35065
|
-
content:
|
|
36652
|
+
content: spacetrim.spaceTrim((block) => `
|
|
36653
|
+
${block(content.slice(0, Math.max(0, maxCharacters)))}
|
|
36654
|
+
|
|
36655
|
+
[...truncated...]
|
|
36656
|
+
`),
|
|
35066
36657
|
isTruncated: true,
|
|
35067
36658
|
};
|
|
35068
36659
|
}
|
|
@@ -35528,7 +37119,7 @@
|
|
|
35528
37119
|
* Returns a virtual model name representing the agent behavior.
|
|
35529
37120
|
*/
|
|
35530
37121
|
get modelName() {
|
|
35531
|
-
const hash =
|
|
37122
|
+
const hash = CryptoJS__default["default"].SHA256(hexEncoder__default["default"].parse(this.options.agentSource)).toString( /* hex */);
|
|
35532
37123
|
const agentId = hash.substring(0, 10);
|
|
35533
37124
|
return (normalizeToKebabCase(this.title) + '-' + agentId);
|
|
35534
37125
|
}
|